diff --git a/backend/crates/application/src/lib.rs b/backend/crates/application/src/lib.rs index cbb39fb..dfccaef 100644 --- a/backend/crates/application/src/lib.rs +++ b/backend/crates/application/src/lib.rs @@ -239,10 +239,7 @@ mod tests { let repo = MockRepository::::new(); let service = Service::new(repo); - let create_user = CreateUser { - username: "test_user".to_string(), - email: "test@example.com".to_string(), - }; + let create_user = CreateUser::new("test_user".to_string(), "test@example.com".to_string()).unwrap(); let user = service.create(create_user).await.unwrap(); assert_eq!(user.username, "test_user"); @@ -254,10 +251,7 @@ mod tests { let repo = MockRepository::::new(); let service = Service::new(repo); - let create_user = CreateUser { - username: "test_user".to_string(), - email: "test@example.com".to_string(), - }; + let create_user = CreateUser::new("test_user".to_string(), "test@example.com".to_string()).unwrap(); let created = service.create(create_user).await.unwrap(); let found = service.get(created.id).await.unwrap(); @@ -269,14 +263,8 @@ mod tests { let repo = MockRepository::::new(); let service = Service::new(repo); - let user1 = CreateUser { - username: "user1".to_string(), - email: "user1@example.com".to_string(), - }; - let user2 = CreateUser { - username: "user2".to_string(), - email: "user2@example.com".to_string(), - }; + let user1 = CreateUser::new("user1".to_string(), "user1@example.com".to_string()).unwrap(); + let user2 = CreateUser::new("user2".to_string(), "user2@example.com".to_string()).unwrap(); service.create(user1).await.unwrap(); service.create(user2).await.unwrap(); @@ -290,16 +278,10 @@ mod tests { let repo = MockRepository::::new(); let service = Service::new(repo); - let create_user = CreateUser { - username: "test_user".to_string(), - email: "test@example.com".to_string(), - }; + let create_user = CreateUser::new("test_user".to_string(), "test@example.com".to_string()).unwrap(); let created = service.create(create_user).await.unwrap(); - let update = UpdateUser { - username: Some("updated_user".to_string()), - email: None, - }; + let update = UpdateUser::new(Some("updated_user".to_string()), None).unwrap(); let updated = service.update(created.id, update).await.unwrap(); assert_eq!(updated.username, "updated_user"); @@ -311,10 +293,7 @@ mod tests { let repo = MockRepository::::new(); let service = Service::new(repo); - let create_user = CreateUser { - username: "test_user".to_string(), - email: "test@example.com".to_string(), - }; + let create_user = CreateUser::new("test_user".to_string(), "test@example.com".to_string()).unwrap(); let created = service.create(create_user).await.unwrap(); service.delete(created.id).await.unwrap(); @@ -326,10 +305,7 @@ mod tests { let repo = MockRepository::::new(); let service = Service::new(repo); - let create_product = CreateProduct { - name: "Test Product".to_string(), - description: "Test Description".to_string(), - }; + let create_product = CreateProduct::new("Test Product".to_string(), "Test Description".to_string()).unwrap(); let product = service.create(create_product).await.unwrap(); assert_eq!(product.name, "Test Product"); @@ -341,10 +317,7 @@ mod tests { let repo = MockRepository::::new(); let service = Service::new(repo); - let create_product = CreateProduct { - name: "Test Product".to_string(), - description: "Test Description".to_string(), - }; + let create_product = CreateProduct::new("Test Product".to_string(), "Test Description".to_string()).unwrap(); let created = service.create(create_product).await.unwrap(); let found = service.get(created.id).await.unwrap(); @@ -356,14 +329,8 @@ mod tests { let repo = MockRepository::::new(); let service = Service::new(repo); - let product1 = CreateProduct { - name: "Product 1".to_string(), - description: "Description 1".to_string(), - }; - let product2 = CreateProduct { - name: "Product 2".to_string(), - description: "Description 2".to_string(), - }; + let product1 = CreateProduct::new("Product 1".to_string(), "Description 1".to_string()).unwrap(); + let product2 = CreateProduct::new("Product 2".to_string(), "Description 2".to_string()).unwrap(); service.create(product1).await.unwrap(); service.create(product2).await.unwrap(); @@ -377,16 +344,10 @@ mod tests { let repo = MockRepository::::new(); let service = Service::new(repo); - let create_product = CreateProduct { - name: "Test Product".to_string(), - description: "Test Description".to_string(), - }; + let create_product = CreateProduct::new("Test Product".to_string(), "Test Description".to_string()).unwrap(); let created = service.create(create_product).await.unwrap(); - let update = UpdateProduct { - name: Some("Updated Product".to_string()), - description: None, - }; + let update = UpdateProduct::new(Some("Updated Product".to_string()), None).unwrap(); let updated = service.update(created.id, update).await.unwrap(); assert_eq!(updated.name, "Updated Product"); @@ -398,10 +359,7 @@ mod tests { let repo = MockRepository::::new(); let service = Service::new(repo); - let create_product = CreateProduct { - name: "Test Product".to_string(), - description: "Test Description".to_string(), - }; + let create_product = CreateProduct::new("Test Product".to_string(), "Test Description".to_string()).unwrap(); let created = service.create(create_product).await.unwrap(); service.delete(created.id).await.unwrap(); @@ -426,10 +384,7 @@ mod tests { let repo = MockRepository::::new(); let service = Service::new(repo); - let update = UpdateUser { - username: Some("new_username".to_string()), - email: None, - }; + let update = UpdateUser::new(Some("new_username".to_string()), None).unwrap(); let result = service.update(Uuid::new_v4(), update).await; assert!(matches!(result, Err(ApplicationError::Domain(domain::DomainError::NotFound(_))))); diff --git a/backend/crates/cli/src/lib.rs b/backend/crates/cli/src/lib.rs index a42d6e8..e9a8b8b 100644 --- a/backend/crates/cli/src/lib.rs +++ b/backend/crates/cli/src/lib.rs @@ -123,7 +123,7 @@ impl Cli { match self.command { Some(Commands::User { command }) => match command { UserCommands::Create { username, email } => { - let user = user_service.create(CreateUser { username, email }).await?; + let user = user_service.create(CreateUser::new(username, email)?).await?; println!("Created user: {:?}", user); } UserCommands::List => { @@ -135,7 +135,7 @@ impl Cli { println!("User: {:?}", user); } UserCommands::Update { id, username, email } => { - let update = UpdateUser { username, email }; + let update = UpdateUser::new(username, email)?; let user = user_service.update(id, update).await?; println!("Updated user: {:?}", user); } @@ -146,7 +146,7 @@ impl Cli { }, Some(Commands::Product { command }) => match command { ProductCommands::Create { name, description } => { - let product = product_service.create(CreateProduct { name, description }).await?; + let product = product_service.create(CreateProduct::new(name, description)?).await?; println!("Created product: {:?}", product); } ProductCommands::List => { @@ -158,7 +158,7 @@ impl Cli { println!("Product: {:?}", product); } ProductCommands::Update { id, name, description } => { - let update = UpdateProduct { name, description }; + let update = UpdateProduct::new(name, description)?; let product = product_service.update(id, update).await?; println!("Updated product: {:?}", product); } @@ -1074,7 +1074,7 @@ mod tests { let product_service = MockProductService::new(); let result = cli.run(user_service, product_service).await; - assert!(result.is_ok()); // Should succeed even with empty strings + assert!(result.is_err()); // Should fail due to empty username validation } /// Tests CLI with empty strings in product creation. @@ -1093,7 +1093,7 @@ mod tests { let product_service = MockProductService::new(); let result = cli.run(user_service, product_service).await; - assert!(result.is_ok()); // Should succeed even with empty strings + assert!(result.is_err()); // Should fail due to empty product name validation } /// Tests CLI with very long strings. diff --git a/backend/crates/domain/src/lib.rs b/backend/crates/domain/src/lib.rs index 4126f23..d37b2fa 100644 --- a/backend/crates/domain/src/lib.rs +++ b/backend/crates/domain/src/lib.rs @@ -69,24 +69,64 @@ pub struct CreateUser { pub email: String, } +impl CreateUser { + pub fn new(username: String, email: String) -> Result { + if username.trim().is_empty() { + return Err(DomainError::InvalidInput("Username cannot be empty".to_string())); + } + Ok(Self { username, email }) + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UpdateUser { pub username: Option, pub email: Option, } +impl UpdateUser { + pub fn new(username: Option, email: Option) -> Result { + if let Some(ref username) = username { + if username.trim().is_empty() { + return Err(DomainError::InvalidInput("Username cannot be empty".to_string())); + } + } + Ok(Self { username, email }) + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CreateProduct { pub name: String, pub description: String, } +impl CreateProduct { + pub fn new(name: String, description: String) -> Result { + if name.trim().is_empty() { + return Err(DomainError::InvalidInput("Product name cannot be empty".to_string())); + } + Ok(Self { name, description }) + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UpdateProduct { pub name: Option, pub description: Option, } +impl UpdateProduct { + pub fn new(name: Option, description: Option) -> Result { + if let Some(ref name) = name { + if name.trim().is_empty() { + return Err(DomainError::InvalidInput("Product name cannot be empty".to_string())); + } + } + Ok(Self { name, description }) + } +} + pub trait Repository: Send + Sync where T: Send, @@ -124,25 +164,61 @@ mod tests { #[test] fn test_create_user_validation() { - let create_user = CreateUser { - username: "test_user".to_string(), - email: "test@example.com".to_string(), - }; + let create_user = CreateUser::new("test_user".to_string(), "test@example.com".to_string()).unwrap(); assert_eq!(create_user.username, "test_user"); assert_eq!(create_user.email, "test@example.com"); } + #[test] + fn test_create_user_empty_username() { + let result = CreateUser::new("".to_string(), "test@example.com".to_string()); + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), DomainError::InvalidInput(_))); + } + + #[test] + fn test_create_user_whitespace_username() { + let result = CreateUser::new(" ".to_string(), "test@example.com".to_string()); + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), DomainError::InvalidInput(_))); + } + + #[test] + fn test_create_user_empty_email() { + let create_user = CreateUser::new("test_user".to_string(), "".to_string()).unwrap(); + assert_eq!(create_user.username, "test_user"); + assert_eq!(create_user.email, ""); + } + #[test] fn test_update_user_partial() { - let update_user = UpdateUser { - username: Some("new_username".to_string()), - email: None, - }; + let update_user = UpdateUser::new(Some("new_username".to_string()), None).unwrap(); assert_eq!(update_user.username, Some("new_username".to_string())); assert_eq!(update_user.email, None); } + + #[test] + fn test_update_user_empty_username() { + let result = UpdateUser::new(Some("".to_string()), None); + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), DomainError::InvalidInput(_))); + } + + #[test] + fn test_update_user_whitespace_username() { + let result = UpdateUser::new(Some(" ".to_string()), None); + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), DomainError::InvalidInput(_))); + } + + #[test] + fn test_update_user_empty_email() { + let update_user = UpdateUser::new(None, Some("".to_string())).unwrap(); + assert_eq!(update_user.username, None); + assert_eq!(update_user.email, Some("".to_string())); + } } mod product_tests { @@ -164,25 +240,59 @@ mod tests { #[test] fn test_create_product_validation() { - let create_product = CreateProduct { - name: "Test Product".to_string(), - description: "Test Description".to_string(), - }; - + let create_product = CreateProduct::new("Test Product".to_string(), "Test Description".to_string()).unwrap(); assert_eq!(create_product.name, "Test Product"); assert_eq!(create_product.description, "Test Description"); } #[test] - fn test_update_product_partial() { - let update_product = UpdateProduct { - name: Some("New Product Name".to_string()), - description: None, - }; + fn test_create_product_empty_name() { + let result = CreateProduct::new("".to_string(), "desc".to_string()); + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), DomainError::InvalidInput(_))); + } + #[test] + fn test_create_product_whitespace_name() { + let result = CreateProduct::new(" ".to_string(), "desc".to_string()); + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), DomainError::InvalidInput(_))); + } + + #[test] + fn test_create_product_empty_description() { + let create_product = CreateProduct::new("Test Product".to_string(), "".to_string()).unwrap(); + assert_eq!(create_product.name, "Test Product"); + assert_eq!(create_product.description, ""); + } + + #[test] + fn test_update_product_partial() { + let update_product = UpdateProduct::new(Some("New Product Name".to_string()), None).unwrap(); assert_eq!(update_product.name, Some("New Product Name".to_string())); assert_eq!(update_product.description, None); } + + #[test] + fn test_update_product_empty_name() { + let result = UpdateProduct::new(Some("".to_string()), None); + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), DomainError::InvalidInput(_))); + } + + #[test] + fn test_update_product_whitespace_name() { + let result = UpdateProduct::new(Some(" ".to_string()), None); + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), DomainError::InvalidInput(_))); + } + + #[test] + fn test_update_product_empty_description() { + let update_product = UpdateProduct::new(None, Some("".to_string())).unwrap(); + assert_eq!(update_product.name, None); + assert_eq!(update_product.description, Some("".to_string())); + } } mod domain_error_tests { @@ -229,10 +339,7 @@ mod tests { #[tokio::test] async fn test_repository_create() { let repo = MockRepository::::new(); - let create_user = CreateUser { - username: "test_user".to_string(), - email: "test@example.com".to_string(), - }; + let create_user = CreateUser::new("test_user".to_string(), "test@example.com".to_string()).unwrap(); let user = User { id: Uuid::new_v4(), diff --git a/backend/crates/integration-tests/src/cli_tests.rs b/backend/crates/integration-tests/src/cli_tests.rs index 6d07159..127d33b 100644 --- a/backend/crates/integration-tests/src/cli_tests.rs +++ b/backend/crates/integration-tests/src/cli_tests.rs @@ -346,20 +346,47 @@ async fn test_cli_edge_cases() -> Result<()> { let user_service = Service::new(user_repo); let product_service = Service::new(product_repo); - // Test empty strings (currently allowed by domain layer) + // Test empty username (should fail validation) let cli = Cli::parse_from(&[ - "cli", "user", "create", "--username", "", "--email", "" + "cli", "user", "create", "--username", "", "--email", "test@example.com" ]); - let _result = cli.run(user_service.clone(), product_service.clone()).await; - // Note: Empty strings are currently allowed by the domain layer - // This test documents the current behavior + let result = cli.run(user_service.clone(), product_service.clone()).await; + assert!(result.is_err()); + // Test whitespace username (should fail validation) + let cli = Cli::parse_from(&[ + "cli", "user", "create", "--username", " ", "--email", "test@example.com" + ]); + let result = cli.run(user_service.clone(), product_service.clone()).await; + assert!(result.is_err()); + + // Test empty email (should be allowed) + let cli = Cli::parse_from(&[ + "cli", "user", "create", "--username", "testuser", "--email", "" + ]); + let result = cli.run(user_service.clone(), product_service.clone()).await; + assert!(result.is_ok()); + + // Test empty product name (should fail validation) let cli = Cli::parse_from(&[ "cli", "product", "create", "--name", "", "--description", "" ]); - let _result = cli.run(user_service.clone(), product_service.clone()).await; - // Note: Empty strings are currently allowed by the domain layer - // This test documents the current behavior + let result = cli.run(user_service.clone(), product_service.clone()).await; + assert!(result.is_err()); + + // Test whitespace product name (should fail validation) + let cli = Cli::parse_from(&[ + "cli", "product", "create", "--name", " ", "--description", "desc" + ]); + let result = cli.run(user_service.clone(), product_service.clone()).await; + assert!(result.is_err()); + + // Test empty product description (should be allowed) + let cli = Cli::parse_from(&[ + "cli", "product", "create", "--name", "Test Product", "--description", "" + ]); + let result = cli.run(user_service.clone(), product_service.clone()).await; + assert!(result.is_ok()); // Test very long strings let long_string = "a".repeat(1000); diff --git a/backend/crates/memory/src/lib.rs b/backend/crates/memory/src/lib.rs index 9152559..ad02470 100644 --- a/backend/crates/memory/src/lib.rs +++ b/backend/crates/memory/src/lib.rs @@ -196,10 +196,7 @@ mod tests { #[tokio::test] async fn test_create_user() { let repo = InMemoryUserRepository::new(); - let create_data = CreateUser { - username: "testuser".to_string(), - email: "test@example.com".to_string(), - }; + let create_data = CreateUser::new("testuser".to_string(), "test@example.com".to_string()).unwrap(); let user = repo.create(create_data).await.unwrap(); @@ -213,10 +210,7 @@ mod tests { #[tokio::test] async fn test_find_by_id_existing() { let repo = InMemoryUserRepository::new(); - let create_data = CreateUser { - username: "testuser".to_string(), - email: "test@example.com".to_string(), - }; + let create_data = CreateUser::new("testuser".to_string(), "test@example.com".to_string()).unwrap(); let created_user = repo.create(create_data).await.unwrap(); let found_user = repo.find_by_id(created_user.id).await.unwrap(); @@ -255,15 +249,9 @@ mod tests { async fn test_find_all_with_users() { let repo = InMemoryUserRepository::new(); - let user1 = repo.create(CreateUser { - username: "user1".to_string(), - email: "user1@example.com".to_string(), - }).await.unwrap(); + let user1 = repo.create(CreateUser::new("user1".to_string(), "user1@example.com".to_string()).unwrap()).await.unwrap(); - let user2 = repo.create(CreateUser { - username: "user2".to_string(), - email: "user2@example.com".to_string(), - }).await.unwrap(); + let user2 = repo.create(CreateUser::new("user2".to_string(), "user2@example.com".to_string()).unwrap()).await.unwrap(); let users = repo.find_all().await.unwrap(); @@ -275,18 +263,12 @@ mod tests { #[tokio::test] async fn test_update_user_existing() { let repo = InMemoryUserRepository::new(); - let user = repo.create(CreateUser { - username: "olduser".to_string(), - email: "old@example.com".to_string(), - }).await.unwrap(); + let user = repo.create(CreateUser::new("olduser".to_string(), "old@example.com".to_string()).unwrap()).await.unwrap(); let original_updated_at = user.updated_at; sleep(Duration::from_millis(1)).await; // Ensure timestamp difference - let update_data = UpdateUser { - username: Some("newuser".to_string()), - email: Some("new@example.com".to_string()), - }; + let update_data = UpdateUser::new(Some("newuser".to_string()), Some("new@example.com".to_string())).unwrap(); let updated_user = repo.update(user.id, update_data).await.unwrap(); @@ -298,15 +280,9 @@ mod tests { #[tokio::test] async fn test_update_user_partial() { let repo = InMemoryUserRepository::new(); - let user = repo.create(CreateUser { - username: "testuser".to_string(), - email: "test@example.com".to_string(), - }).await.unwrap(); + let user = repo.create(CreateUser::new("testuser".to_string(), "test@example.com".to_string()).unwrap()).await.unwrap(); - let update_data = UpdateUser { - username: Some("newuser".to_string()), - email: None, - }; + let update_data = UpdateUser::new(Some("newuser".to_string()), None).unwrap(); let updated_user = repo.update(user.id, update_data).await.unwrap(); @@ -318,10 +294,7 @@ mod tests { async fn test_update_user_not_found() { let repo = InMemoryUserRepository::new(); let non_existent_id = Uuid::new_v4(); - let update_data = UpdateUser { - username: Some("newuser".to_string()), - email: None, - }; + let update_data = UpdateUser::new(Some("newuser".to_string()), None).unwrap(); let result = repo.update(non_existent_id, update_data).await; assert!(result.is_err()); @@ -338,10 +311,7 @@ mod tests { #[tokio::test] async fn test_delete_user_existing() { let repo = InMemoryUserRepository::new(); - let user = repo.create(CreateUser { - username: "testuser".to_string(), - email: "test@example.com".to_string(), - }).await.unwrap(); + let user = repo.create(CreateUser::new("testuser".to_string(), "test@example.com".to_string()).unwrap()).await.unwrap(); let result = repo.delete(user.id).await; assert!(result.is_ok()); @@ -377,10 +347,7 @@ mod tests { let handles: Vec<_> = (0..10).map(|i| { let repo = repo_clone.clone(); tokio::spawn(async move { - repo.create(CreateUser { - username: format!("user{}", i), - email: format!("user{}@example.com", i), - }).await + repo.create(CreateUser::new(format!("user{}", i), format!("user{}@example.com", i)).unwrap()).await }) }).collect(); @@ -402,10 +369,7 @@ mod tests { #[tokio::test] async fn test_create_product() { let repo = InMemoryProductRepository::new(); - let create_data = CreateProduct { - name: "Test Product".to_string(), - description: "Test Description".to_string(), - }; + let create_data = CreateProduct::new("Test Product".to_string(), "Test Description".to_string()).unwrap(); let product = repo.create(create_data).await.unwrap(); @@ -419,10 +383,7 @@ mod tests { #[tokio::test] async fn test_find_by_id_existing() { let repo = InMemoryProductRepository::new(); - let create_data = CreateProduct { - name: "Test Product".to_string(), - description: "Test Description".to_string(), - }; + let create_data = CreateProduct::new("Test Product".to_string(), "Test Description".to_string()).unwrap(); let created_product = repo.create(create_data).await.unwrap(); let found_product = repo.find_by_id(created_product.id).await.unwrap(); @@ -461,15 +422,9 @@ mod tests { async fn test_find_all_with_products() { let repo = InMemoryProductRepository::new(); - let product1 = repo.create(CreateProduct { - name: "Product 1".to_string(), - description: "Description 1".to_string(), - }).await.unwrap(); + let product1 = repo.create(CreateProduct::new("Product 1".to_string(), "Description 1".to_string()).unwrap()).await.unwrap(); - let product2 = repo.create(CreateProduct { - name: "Product 2".to_string(), - description: "Description 2".to_string(), - }).await.unwrap(); + let product2 = repo.create(CreateProduct::new("Product 2".to_string(), "Description 2".to_string()).unwrap()).await.unwrap(); let products = repo.find_all().await.unwrap(); @@ -481,18 +436,12 @@ mod tests { #[tokio::test] async fn test_update_product_existing() { let repo = InMemoryProductRepository::new(); - let product = repo.create(CreateProduct { - name: "Old Product".to_string(), - description: "Old Description".to_string(), - }).await.unwrap(); + let product = repo.create(CreateProduct::new("Old Product".to_string(), "Old Description".to_string()).unwrap()).await.unwrap(); let original_updated_at = product.updated_at; sleep(Duration::from_millis(1)).await; // Ensure timestamp difference - let update_data = UpdateProduct { - name: Some("New Product".to_string()), - description: Some("New Description".to_string()), - }; + let update_data = UpdateProduct::new(Some("New Product".to_string()), Some("New Description".to_string())).unwrap(); let updated_product = repo.update(product.id, update_data).await.unwrap(); @@ -504,15 +453,9 @@ mod tests { #[tokio::test] async fn test_update_product_partial() { let repo = InMemoryProductRepository::new(); - let product = repo.create(CreateProduct { - name: "Test Product".to_string(), - description: "Test Description".to_string(), - }).await.unwrap(); + let product = repo.create(CreateProduct::new("Test Product".to_string(), "Test Description".to_string()).unwrap()).await.unwrap(); - let update_data = UpdateProduct { - name: Some("New Product".to_string()), - description: None, - }; + let update_data = UpdateProduct::new(Some("New Product".to_string()), None).unwrap(); let updated_product = repo.update(product.id, update_data).await.unwrap(); @@ -524,10 +467,7 @@ mod tests { async fn test_update_product_not_found() { let repo = InMemoryProductRepository::new(); let non_existent_id = Uuid::new_v4(); - let update_data = UpdateProduct { - name: Some("New Product".to_string()), - description: None, - }; + let update_data = UpdateProduct::new(Some("New Product".to_string()), None).unwrap(); let result = repo.update(non_existent_id, update_data).await; assert!(result.is_err()); @@ -544,10 +484,7 @@ mod tests { #[tokio::test] async fn test_delete_product_existing() { let repo = InMemoryProductRepository::new(); - let product = repo.create(CreateProduct { - name: "Test Product".to_string(), - description: "Test Description".to_string(), - }).await.unwrap(); + let product = repo.create(CreateProduct::new("Test Product".to_string(), "Test Description".to_string()).unwrap()).await.unwrap(); let result = repo.delete(product.id).await; assert!(result.is_ok()); @@ -583,10 +520,7 @@ mod tests { let handles: Vec<_> = (0..10).map(|i| { let repo = repo_clone.clone(); tokio::spawn(async move { - repo.create(CreateProduct { - name: format!("Product {}", i), - description: format!("Description {}", i), - }).await + repo.create(CreateProduct::new(format!("Product {}", i), format!("Description {}", i)).unwrap()).await }) }).collect(); @@ -612,10 +546,7 @@ mod tests { let service = MemoryUserService::new(repo); // Test create - let user = service.create(CreateUser { - username: "testuser".to_string(), - email: "test@example.com".to_string(), - }).await.unwrap(); + let user = service.create(CreateUser::new("testuser".to_string(), "test@example.com".to_string()).unwrap()).await.unwrap(); assert_eq!(user.username, "testuser"); assert_eq!(user.email, "test@example.com"); @@ -625,10 +556,7 @@ mod tests { assert_eq!(found_user.id, user.id); // Test update - let updated_user = service.update(user.id, UpdateUser { - username: Some("newuser".to_string()), - email: None, - }).await.unwrap(); + let updated_user = service.update(user.id, UpdateUser::new(Some("newuser".to_string()), None).unwrap()).await.unwrap(); assert_eq!(updated_user.username, "newuser"); assert_eq!(updated_user.email, "test@example.com"); @@ -652,10 +580,7 @@ mod tests { let service = MemoryProductService::new(repo); // Test create - let product = service.create(CreateProduct { - name: "Test Product".to_string(), - description: "Test Description".to_string(), - }).await.unwrap(); + let product = service.create(CreateProduct::new("Test Product".to_string(), "Test Description".to_string()).unwrap()).await.unwrap(); assert_eq!(product.name, "Test Product"); assert_eq!(product.description, "Test Description"); @@ -665,10 +590,7 @@ mod tests { assert_eq!(found_product.id, product.id); // Test update - let updated_product = service.update(product.id, UpdateProduct { - name: Some("New Product".to_string()), - description: None, - }).await.unwrap(); + let updated_product = service.update(product.id, UpdateProduct::new(Some("New Product".to_string()), None).unwrap()).await.unwrap(); assert_eq!(updated_product.name, "New Product"); assert_eq!(updated_product.description, "Test Description"); @@ -695,25 +617,16 @@ mod tests { let repo = InMemoryUserRepository::new(); // Create multiple users - let user1 = repo.create(CreateUser { - username: "user1".to_string(), - email: "user1@example.com".to_string(), - }).await.unwrap(); + let user1 = repo.create(CreateUser::new("user1".to_string(), "user1@example.com".to_string()).unwrap()).await.unwrap(); - let user2 = repo.create(CreateUser { - username: "user2".to_string(), - email: "user2@example.com".to_string(), - }).await.unwrap(); + let user2 = repo.create(CreateUser::new("user2".to_string(), "user2@example.com".to_string()).unwrap()).await.unwrap(); // Verify both users exist let users = repo.find_all().await.unwrap(); assert_eq!(users.len(), 2); // Update one user - let updated_user1 = repo.update(user1.id, UpdateUser { - username: Some("updated_user1".to_string()), - email: None, - }).await.unwrap(); + let updated_user1 = repo.update(user1.id, UpdateUser::new(Some("updated_user1".to_string()), None).unwrap()).await.unwrap(); assert_eq!(updated_user1.username, "updated_user1"); assert_eq!(updated_user1.email, "user1@example.com"); @@ -732,25 +645,16 @@ mod tests { let repo = InMemoryProductRepository::new(); // Create multiple products - let product1 = repo.create(CreateProduct { - name: "Product 1".to_string(), - description: "Description 1".to_string(), - }).await.unwrap(); + let product1 = repo.create(CreateProduct::new("Product 1".to_string(), "Description 1".to_string()).unwrap()).await.unwrap(); - let product2 = repo.create(CreateProduct { - name: "Product 2".to_string(), - description: "Description 2".to_string(), - }).await.unwrap(); + let product2 = repo.create(CreateProduct::new("Product 2".to_string(), "Description 2".to_string()).unwrap()).await.unwrap(); // Verify both products exist let products = repo.find_all().await.unwrap(); assert_eq!(products.len(), 2); // Update one product - let updated_product1 = repo.update(product1.id, UpdateProduct { - name: Some("Updated Product 1".to_string()), - description: None, - }).await.unwrap(); + let updated_product1 = repo.update(product1.id, UpdateProduct::new(Some("Updated Product 1".to_string()), None).unwrap()).await.unwrap(); assert_eq!(updated_product1.name, "Updated Product 1"); assert_eq!(updated_product1.description, "Description 1"); diff --git a/backend/crates/postgres/src/lib.rs b/backend/crates/postgres/src/lib.rs index e9ae201..4d7d8b5 100644 --- a/backend/crates/postgres/src/lib.rs +++ b/backend/crates/postgres/src/lib.rs @@ -295,10 +295,7 @@ mod tests { // Clean up at the beginning to ensure isolation cleanup_test_data(&pool).await; - let create_data = CreateUser { - username: "testuser".to_string(), - email: "test@example.com".to_string(), - }; + let create_data = CreateUser::new("testuser".to_string(), "test@example.com".to_string()).unwrap(); let result = repo.create(create_data).await; assert!(result.is_ok()); @@ -322,20 +319,14 @@ mod tests { // Clean up at the beginning to ensure isolation cleanup_test_data(&pool).await; - let create_data = CreateUser { - username: "duplicate_user".to_string(), - email: "test1@example.com".to_string(), - }; + let create_data = CreateUser::new("duplicate_user".to_string(), "test1@example.com".to_string()).unwrap(); // Create first user let result1 = repo.create(create_data.clone()).await; assert!(result1.is_ok()); // Try to create second user with same username - let create_data2 = CreateUser { - username: "duplicate_user".to_string(), - email: "test2@example.com".to_string(), - }; + let create_data2 = CreateUser::new("duplicate_user".to_string(), "test2@example.com".to_string()).unwrap(); let result2 = repo.create(create_data2).await; assert!(result2.is_err()); @@ -351,10 +342,7 @@ mod tests { // Clean up at the beginning to ensure isolation cleanup_test_data(&pool).await; - let create_data = CreateUser { - username: "finduser".to_string(), - email: "find@example.com".to_string(), - }; + let create_data = CreateUser::new("finduser".to_string(), "find@example.com".to_string()).unwrap(); let created_user = repo.create(create_data).await.unwrap(); let found_user = repo.find_by_id(created_user.id).await; @@ -404,15 +392,9 @@ mod tests { let (username1, email1) = unique_test_data("user1"); let (username2, email2) = unique_test_data("user2"); - let _user1 = repo.create(CreateUser { - username: username1.clone(), - email: email1, - }).await.unwrap(); + let _user1 = repo.create(CreateUser::new(username1.clone(), email1).unwrap()).await.unwrap(); - let _user2 = repo.create(CreateUser { - username: username2.clone(), - email: email2, - }).await.unwrap(); + let _user2 = repo.create(CreateUser::new(username2.clone(), email2).unwrap()).await.unwrap(); let users = repo.find_all().await.unwrap(); assert_eq!(users.len(), 2); @@ -448,19 +430,13 @@ mod tests { // Clean up at the beginning to ensure isolation cleanup_test_data(&pool).await; - let create_data = CreateUser { - username: "updateuser".to_string(), - email: "update@example.com".to_string(), - }; + let create_data = CreateUser::new("updateuser".to_string(), "update@example.com".to_string()).unwrap(); let user = repo.create(create_data).await.unwrap(); let original_updated_at = user.updated_at; // Update username only - let update_data = UpdateUser { - username: Some("updateduser".to_string()), - email: None, - }; + let update_data = UpdateUser::new(Some("updateduser".to_string()), None).unwrap(); let updated_user = repo.update(user.id, update_data).await.unwrap(); assert_eq!(updated_user.username, "updateduser"); @@ -479,17 +455,11 @@ mod tests { // Clean up at the beginning to ensure isolation cleanup_test_data(&pool).await; - let create_data = CreateUser { - username: "emailuser".to_string(), - email: "old@example.com".to_string(), - }; + let create_data = CreateUser::new("emailuser".to_string(), "old@example.com".to_string()).unwrap(); let user = repo.create(create_data).await.unwrap(); - let update_data = UpdateUser { - username: None, - email: Some("new@example.com".to_string()), - }; + let update_data = UpdateUser::new(None, Some("new@example.com".to_string())).unwrap(); let updated_user = repo.update(user.id, update_data).await.unwrap(); assert_eq!(updated_user.username, "emailuser"); // Should remain unchanged @@ -507,17 +477,11 @@ mod tests { // Clean up at the beginning to ensure isolation cleanup_test_data(&pool).await; - let create_data = CreateUser { - username: "bothuser".to_string(), - email: "both@example.com".to_string(), - }; + let create_data = CreateUser::new("bothuser".to_string(), "both@example.com".to_string()).unwrap(); let user = repo.create(create_data).await.unwrap(); - let update_data = UpdateUser { - username: Some("newbothuser".to_string()), - email: Some("newboth@example.com".to_string()), - }; + let update_data = UpdateUser::new(Some("newbothuser".to_string()), Some("newboth@example.com".to_string())).unwrap(); let updated_user = repo.update(user.id, update_data).await.unwrap(); assert_eq!(updated_user.username, "newbothuser"); @@ -536,10 +500,7 @@ mod tests { cleanup_test_data(&pool).await; let nonexistent_id = Uuid::new_v4(); - let update_data = UpdateUser { - username: Some("nonexistent".to_string()), - email: None, - }; + let update_data = UpdateUser::new(Some("nonexistent".to_string()), None).unwrap(); let result = repo.update(nonexistent_id, update_data).await; assert!(result.is_err()); @@ -563,10 +524,7 @@ mod tests { cleanup_test_data(&pool).await; let (username, email) = unique_test_data("delete_user"); - let create_data = CreateUser { - username: username.clone(), - email: email.clone(), - }; + let create_data = CreateUser::new(username.clone(), email.clone()).unwrap(); let user = repo.create(create_data).await.unwrap(); let user_id = user.id; @@ -619,10 +577,7 @@ mod tests { cleanup_test_data(&pool).await; let (username, email) = unique_test_data("concurrent_user"); - let create_data = CreateUser { - username: username.clone(), - email: email.clone(), - }; + let create_data = CreateUser::new(username.clone(), email.clone()).unwrap(); // Create a user let user = repo.create(create_data).await.unwrap(); @@ -666,17 +621,13 @@ mod tests { // Clean up at the beginning to ensure isolation cleanup_test_data(&pool).await; - let (name, _) = unique_test_data("test_product"); - let create_data = CreateProduct { - name: name.clone(), - description: "Test Description".to_string(), - }; + let create_data = CreateProduct::new("Test Product".to_string(), "Test Description".to_string()).unwrap(); let result = repo.create(create_data).await; assert!(result.is_ok()); let product = result.unwrap(); - assert_eq!(product.name, name); + assert_eq!(product.name, "Test Product"); assert_eq!(product.description, "Test Description"); assert!(product.id != Uuid::nil()); assert!(product.created_at <= Utc::now()); @@ -694,10 +645,7 @@ mod tests { // Clean up at the beginning to ensure isolation cleanup_test_data(&pool).await; - let create_data = CreateProduct { - name: "Find Product".to_string(), - description: "Find Description".to_string(), - }; + let create_data = CreateProduct::new("findproduct".to_string(), "finddesc".to_string()).unwrap(); let created_product = repo.create(create_data).await.unwrap(); let found_product = repo.find_by_id(created_product.id).await; @@ -705,8 +653,8 @@ mod tests { assert!(found_product.is_ok()); let product = found_product.unwrap(); assert_eq!(product.id, created_product.id); - assert_eq!(product.name, "Find Product"); - assert_eq!(product.description, "Find Description"); + assert_eq!(product.name, "findproduct"); + assert_eq!(product.description, "finddesc"); cleanup_test_data(&pool).await; } @@ -744,18 +692,11 @@ mod tests { cleanup_test_data(&pool).await; // Create multiple products with unique names - let (name1, _) = unique_test_data("product1"); - let (name2, _) = unique_test_data("product2"); + let (name1, desc1) = unique_test_data("product1"); + let (name2, desc2) = unique_test_data("product2"); - let _product1 = repo.create(CreateProduct { - name: name1.clone(), - description: "Description 1".to_string(), - }).await.unwrap(); - - let _product2 = repo.create(CreateProduct { - name: name2.clone(), - description: "Description 2".to_string(), - }).await.unwrap(); + let _product1 = repo.create(CreateProduct::new(name1.clone(), desc1).unwrap()).await.unwrap(); + let _product2 = repo.create(CreateProduct::new(name2.clone(), desc2).unwrap()).await.unwrap(); let products = repo.find_all().await.unwrap(); assert_eq!(products.len(), 2); @@ -791,24 +732,17 @@ mod tests { // Clean up at the beginning to ensure isolation cleanup_test_data(&pool).await; - let (name, _) = unique_test_data("update_product"); - let create_data = CreateProduct { - name: name.clone(), - description: "Update Description".to_string(), - }; + let create_data = CreateProduct::new("updateproduct".to_string(), "updatedesc".to_string()).unwrap(); let product = repo.create(create_data).await.unwrap(); let original_updated_at = product.updated_at; // Update name only - let update_data = UpdateProduct { - name: Some("Updated Product".to_string()), - description: None, - }; + let update_data = UpdateProduct::new(Some("updatedproduct".to_string()), None).unwrap(); let updated_product = repo.update(product.id, update_data).await.unwrap(); - assert_eq!(updated_product.name, "Updated Product"); - assert_eq!(updated_product.description, "Update Description"); // Should remain unchanged + assert_eq!(updated_product.name, "updatedproduct"); + assert_eq!(updated_product.description, "updatedesc"); // Should remain unchanged assert!(updated_product.updated_at > original_updated_at); cleanup_test_data(&pool).await; @@ -971,10 +905,7 @@ mod tests { cleanup_test_data(&pool).await; let (username, email) = unique_test_data("service_user"); - let create_data = CreateUser { - username: username.clone(), - email: email.clone(), - }; + let create_data = CreateUser::new(username.clone(), email.clone()).unwrap(); // Test create let user = service.create(create_data).await.unwrap(); @@ -991,10 +922,7 @@ mod tests { // Test update let (new_username, _) = unique_test_data("updated_service_user"); - let update_data = UpdateUser { - username: Some(new_username.clone()), - email: None, - }; + let update_data = UpdateUser::new(Some(new_username.clone()), None).unwrap(); let updated_user = service.update(user.id, update_data).await.unwrap(); assert_eq!(updated_user.username, new_username); diff --git a/backend/crates/tui/src/lib.rs b/backend/crates/tui/src/lib.rs index 70db528..adc9965 100644 --- a/backend/crates/tui/src/lib.rs +++ b/backend/crates/tui/src/lib.rs @@ -212,11 +212,13 @@ where cmd if cmd.starts_with("user create") => { match parse_user_create(cmd) { Ok((username, email)) => { - match user_service_clone - .create(CreateUser { username, email }) - .await - { - Ok(user) => app.add_message(format!("Created user: {:?}", user)), + match CreateUser::new(username, email) { + Ok(create_user) => { + match user_service_clone.create(create_user).await { + Ok(user) => app.add_message(format!("Created user: {:?}", user)), + Err(e) => app.add_message(format!("Error: {}", e)), + } + } Err(e) => app.add_message(format!("Error: {}", e)), } } @@ -233,7 +235,13 @@ where match parse_product_create(cmd) { Ok((name, description)) => { match product_service_clone - .create(CreateProduct { name, description }) + .create(match CreateProduct::new(name, description) { + Ok(create_product) => create_product, + Err(e) => { + app.add_message(format!("Error: {}", e)); + continue; + } + }) .await { Ok(product) => app.add_message(format!("Created product: {:?}", product)),