This commit is contained in:
2026-05-17 12:04:51 +02:00
parent 54910c6459
commit d813e59b5c
46 changed files with 1003 additions and 810 deletions

View File

@@ -139,7 +139,10 @@ impl UserReader for PgUserRepository {
.into_domain()
}
async fn list_paginated(&self, page: PageParams) -> Result<Paginated<UserSummary>, DomainError> {
async fn list_paginated(
&self,
page: PageParams,
) -> Result<Paginated<UserSummary>, DomainError> {
#[derive(sqlx::FromRow)]
struct Row {
id: uuid::Uuid,
@@ -187,7 +190,12 @@ impl UserReader for PgUserRepository {
following_count: r.following_count,
})
.collect();
Ok(Paginated { items, total, page: page.page, per_page: page.per_page })
Ok(Paginated {
items,
total,
page: page.page,
per_page: page.per_page,
})
}
async fn find_by_ids(&self, ids: &[UserId]) -> Result<HashMap<UserId, User>, DomainError> {
@@ -195,18 +203,19 @@ impl UserReader for PgUserRepository {
return Ok(HashMap::new());
}
let uuids: Vec<uuid::Uuid> = ids.iter().map(|id| id.as_uuid()).collect();
let rows = sqlx::query_as::<_, UserRow>(
&format!("{USER_SELECT} WHERE id = ANY($1)")
)
.bind(&uuids[..])
.fetch_all(&self.pool)
.await
.into_domain()?;
let rows = sqlx::query_as::<_, UserRow>(&format!("{USER_SELECT} WHERE id = ANY($1)"))
.bind(&uuids[..])
.fetch_all(&self.pool)
.await
.into_domain()?;
Ok(rows.into_iter().map(|r| {
let user = User::from(r);
(user.id.clone(), user)
}).collect())
Ok(rows
.into_iter()
.map(|r| {
let user = User::from(r);
(user.id.clone(), user)
})
.collect())
}
}

View File

@@ -1,69 +1,70 @@
use super::*;
use domain::{models::user::User, value_objects::*};
#[sqlx::test(migrations = "./migrations")]
async fn save_and_find_by_id(pool: sqlx::PgPool) {
let repo = PgUserRepository::new(pool);
let user = User::new_local(
UserId::new(),
Username::new("alice").unwrap(),
Email::new("alice@ex.com").unwrap(),
PasswordHash("hash".into()),
);
repo.save(&user).await.unwrap();
let found = repo.find_by_id(&user.id).await.unwrap().unwrap();
assert_eq!(found.username.as_str(), "alice");
assert_eq!(found.email.as_str(), "alice@ex.com");
}
use super::*;
use domain::{models::user::User, value_objects::*};
#[sqlx::test(migrations = "./migrations")]
async fn find_by_username_returns_none_when_missing(pool: sqlx::PgPool) {
let repo = PgUserRepository::new(pool);
let result = repo
.find_by_username(&Username::new("ghost").unwrap())
.await
.unwrap();
assert!(result.is_none());
}
#[sqlx::test(migrations = "./migrations")]
async fn save_and_find_by_id(pool: sqlx::PgPool) {
let repo = PgUserRepository::new(pool);
let user = User::new_local(
UserId::new(),
Username::new("alice").unwrap(),
Email::new("alice@ex.com").unwrap(),
PasswordHash("hash".into()),
);
repo.save(&user).await.unwrap();
let found = repo.find_by_id(&user.id).await.unwrap().unwrap();
assert_eq!(found.username.as_str(), "alice");
assert_eq!(found.email.as_str(), "alice@ex.com");
}
#[sqlx::test(migrations = "./migrations")]
async fn find_by_email(pool: sqlx::PgPool) {
let repo = PgUserRepository::new(pool);
let user = User::new_local(
UserId::new(),
Username::new("bob").unwrap(),
Email::new("bob@ex.com").unwrap(),
PasswordHash("hash".into()),
);
repo.save(&user).await.unwrap();
let found = repo
.find_by_email(&Email::new("bob@ex.com").unwrap())
.await
.unwrap();
assert!(found.is_some());
}
#[sqlx::test(migrations = "./migrations")]
async fn update_profile_changes_fields(pool: sqlx::PgPool) {
let repo = PgUserRepository::new(pool);
let user = User::new_local(
UserId::new(),
Username::new("charlie").unwrap(),
Email::new("charlie@ex.com").unwrap(),
PasswordHash("hash".into()),
);
repo.save(&user).await.unwrap();
repo.update_profile(
&user.id,
Some("Charlie".into()),
Some("bio".into()),
None,
None,
None,
)
#[sqlx::test(migrations = "./migrations")]
async fn find_by_username_returns_none_when_missing(pool: sqlx::PgPool) {
let repo = PgUserRepository::new(pool);
let result = repo
.find_by_username(&Username::new("ghost").unwrap())
.await
.unwrap();
let found = repo.find_by_id(&user.id).await.unwrap().unwrap();
assert_eq!(found.display_name.as_deref(), Some("Charlie"));
assert_eq!(found.bio.as_deref(), Some("bio"));
}
assert!(result.is_none());
}
#[sqlx::test(migrations = "./migrations")]
async fn find_by_email(pool: sqlx::PgPool) {
let repo = PgUserRepository::new(pool);
let user = User::new_local(
UserId::new(),
Username::new("bob").unwrap(),
Email::new("bob@ex.com").unwrap(),
PasswordHash("hash".into()),
);
repo.save(&user).await.unwrap();
let found = repo
.find_by_email(&Email::new("bob@ex.com").unwrap())
.await
.unwrap();
assert!(found.is_some());
}
#[sqlx::test(migrations = "./migrations")]
async fn update_profile_changes_fields(pool: sqlx::PgPool) {
let repo = PgUserRepository::new(pool);
let user = User::new_local(
UserId::new(),
Username::new("charlie").unwrap(),
Email::new("charlie@ex.com").unwrap(),
PasswordHash("hash".into()),
);
repo.save(&user).await.unwrap();
repo.update_profile(
&user.id,
Some("Charlie".into()),
Some("bio".into()),
None,
None,
None,
)
.await
.unwrap();
let found = repo.find_by_id(&user.id).await.unwrap().unwrap();
assert_eq!(found.display_name.as_deref(), Some("Charlie"));
assert_eq!(found.bio.as_deref(), Some("bio"));
}