|
|
|
|
@@ -3,7 +3,7 @@ use domain::{
|
|
|
|
|
entities::User,
|
|
|
|
|
errors::DomainError,
|
|
|
|
|
ports::{PasswordHasher, UserRepository},
|
|
|
|
|
value_objects::{Email, SystemId},
|
|
|
|
|
value_objects::Email,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub struct RegisterUser {
|
|
|
|
|
@@ -16,7 +16,7 @@ impl RegisterUser {
|
|
|
|
|
Self { repo, hasher }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn execute(&self, email: &str, password: &str) -> Result<User, DomainError> {
|
|
|
|
|
pub async fn execute(&self, username: &str, email: &str, password: &str) -> Result<User, DomainError> {
|
|
|
|
|
if password.len() < 8 {
|
|
|
|
|
return Err(DomainError::Validation("Password must be at least 8 characters".to_string()));
|
|
|
|
|
}
|
|
|
|
|
@@ -24,8 +24,11 @@ impl RegisterUser {
|
|
|
|
|
if self.repo.find_by_email(&email).await?.is_some() {
|
|
|
|
|
return Err(DomainError::Conflict(format!("Email {} is already registered", email.as_str())));
|
|
|
|
|
}
|
|
|
|
|
if self.repo.find_by_username(username).await?.is_some() {
|
|
|
|
|
return Err(DomainError::Conflict(format!("Username {username} is already taken")));
|
|
|
|
|
}
|
|
|
|
|
let hash = self.hasher.hash(password).await?;
|
|
|
|
|
let user = User::new(SystemId::new(), email, hash);
|
|
|
|
|
let user = User::new(username, email, hash);
|
|
|
|
|
self.repo.save(&user).await?;
|
|
|
|
|
Ok(user)
|
|
|
|
|
}
|
|
|
|
|
@@ -40,8 +43,9 @@ mod tests {
|
|
|
|
|
async fn register_creates_user() {
|
|
|
|
|
let repo = Arc::new(InMemoryUserRepository::new());
|
|
|
|
|
let uc = RegisterUser::new(repo.clone(), Arc::new(StubPasswordHasher));
|
|
|
|
|
let user = uc.execute("test@example.com", "password123").await.unwrap();
|
|
|
|
|
let user = uc.execute("testuser", "test@example.com", "password123").await.unwrap();
|
|
|
|
|
assert_eq!(user.email.as_str(), "test@example.com");
|
|
|
|
|
assert_eq!(user.username, "testuser");
|
|
|
|
|
assert_eq!(repo.all().await.len(), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -49,8 +53,17 @@ mod tests {
|
|
|
|
|
async fn register_rejects_duplicate_email() {
|
|
|
|
|
let repo = Arc::new(InMemoryUserRepository::new());
|
|
|
|
|
let uc = RegisterUser::new(repo.clone(), Arc::new(StubPasswordHasher));
|
|
|
|
|
uc.execute("test@example.com", "password123").await.unwrap();
|
|
|
|
|
let result = uc.execute("test@example.com", "different1").await;
|
|
|
|
|
uc.execute("user1", "test@example.com", "password123").await.unwrap();
|
|
|
|
|
let result = uc.execute("user2", "test@example.com", "different1").await;
|
|
|
|
|
assert!(matches!(result, Err(DomainError::Conflict(_))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
async fn register_rejects_duplicate_username() {
|
|
|
|
|
let repo = Arc::new(InMemoryUserRepository::new());
|
|
|
|
|
let uc = RegisterUser::new(repo.clone(), Arc::new(StubPasswordHasher));
|
|
|
|
|
uc.execute("sameuser", "a@example.com", "password123").await.unwrap();
|
|
|
|
|
let result = uc.execute("sameuser", "b@example.com", "password123").await;
|
|
|
|
|
assert!(matches!(result, Err(DomainError::Conflict(_))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -58,7 +71,7 @@ mod tests {
|
|
|
|
|
async fn register_rejects_short_password() {
|
|
|
|
|
let repo = Arc::new(InMemoryUserRepository::new());
|
|
|
|
|
let uc = RegisterUser::new(repo, Arc::new(StubPasswordHasher));
|
|
|
|
|
let result = uc.execute("test@example.com", "short").await;
|
|
|
|
|
let result = uc.execute("user", "test@example.com", "short").await;
|
|
|
|
|
assert!(matches!(result, Err(DomainError::Validation(_))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -66,7 +79,7 @@ mod tests {
|
|
|
|
|
async fn register_rejects_invalid_email() {
|
|
|
|
|
let repo = Arc::new(InMemoryUserRepository::new());
|
|
|
|
|
let uc = RegisterUser::new(repo, Arc::new(StubPasswordHasher));
|
|
|
|
|
let result = uc.execute("notanemail", "password123").await;
|
|
|
|
|
let result = uc.execute("user", "notanemail", "password123").await;
|
|
|
|
|
assert!(matches!(result, Err(DomainError::Validation(_))));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|