/* * This file is part of Sharenet. * * Sharenet is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. * * You may obtain a copy of the license at: * https://creativecommons.org/licenses/by-nc-sa/4.0/ * * Copyright (c) 2024 Continuist */ use anyhow::Result; use clap::Parser; use cli::Cli; use application::Service; use memory::{InMemoryUserRepository, InMemoryProductRepository}; use postgres::{PostgresUserRepository, PostgresProductRepository}; use uuid::Uuid; use serial_test::serial; use application::UseCase; // Import the centralized test setup use crate::test_setup::{setup_test_db, unique_test_data}; // Helper functions are now imported from test_setup module above // Test CLI with memory repository #[tokio::test] #[serial] async fn test_cli_with_memory_user_lifecycle() -> Result<()> { let user_repo = InMemoryUserRepository::new(); let product_repo = InMemoryProductRepository::new(); let user_service = Service::new(user_repo); let product_service = Service::new(product_repo); // Test user create let (username, email) = unique_test_data("test_user"); let cli = Cli::parse_from(&[ "cli", "user", "create", "--username", &username, "--email", &email ]); cli.run(user_service.clone(), product_service.clone()).await?; // Test user list let cli = Cli::parse_from(&["cli", "user", "list"]); cli.run(user_service.clone(), product_service.clone()).await?; // Test user get (we need to get the ID from the list first) let users = user_service.list().await?; assert!(!users.is_empty()); let user_id = users[0].id().to_string(); let cli = Cli::parse_from(&["cli", "user", "get", "--id", &user_id]); cli.run(user_service.clone(), product_service.clone()).await?; // Test user update let new_username = format!("{}_updated", username); let cli = Cli::parse_from(&[ "cli", "user", "update", "--id", &user_id, "--username", &new_username ]); cli.run(user_service.clone(), product_service.clone()).await?; // Test user delete let cli = Cli::parse_from(&["cli", "user", "delete", "--id", &user_id]); cli.run(user_service.clone(), product_service.clone()).await?; Ok(()) } #[tokio::test] #[serial] async fn test_cli_with_memory_product_lifecycle() -> Result<()> { let user_repo = InMemoryUserRepository::new(); let product_repo = InMemoryProductRepository::new(); let user_service = Service::new(user_repo); let product_service = Service::new(product_repo); // Test product create let (name, description) = unique_test_data("test_product"); let cli = Cli::parse_from(&[ "cli", "product", "create", "--name", &name, "--description", &description ]); cli.run(user_service.clone(), product_service.clone()).await?; // Test product list let cli = Cli::parse_from(&["cli", "product", "list"]); cli.run(user_service.clone(), product_service.clone()).await?; // Test product get (we need to get the ID from the list first) let products = product_service.list().await?; assert!(!products.is_empty()); let product_id = products[0].id().to_string(); let cli = Cli::parse_from(&["cli", "product", "get", "--id", &product_id]); cli.run(user_service.clone(), product_service.clone()).await?; // Test product update let new_name = format!("{}_updated", name); let cli = Cli::parse_from(&[ "cli", "product", "update", "--id", &product_id, "--name", &new_name ]); cli.run(user_service.clone(), product_service.clone()).await?; // Test product delete let cli = Cli::parse_from(&["cli", "product", "delete", "--id", &product_id]); cli.run(user_service.clone(), product_service.clone()).await?; Ok(()) } #[tokio::test] #[serial] async fn test_cli_with_memory_mixed_operations() -> Result<()> { let user_repo = InMemoryUserRepository::new(); let product_repo = InMemoryProductRepository::new(); let user_service = Service::new(user_repo); let product_service = Service::new(product_repo); // Create multiple users and products for i in 1..=3 { let (username, email) = unique_test_data(&format!("user_{}", i)); let cli = Cli::parse_from(&[ "cli", "user", "create", "--username", &username, "--email", &email ]); cli.run(user_service.clone(), product_service.clone()).await?; let (name, description) = unique_test_data(&format!("product_{}", i)); let cli = Cli::parse_from(&[ "cli", "product", "create", "--name", &name, "--description", &description ]); cli.run(user_service.clone(), product_service.clone()).await?; } // List all users and products let cli = Cli::parse_from(&["cli", "user", "list"]); cli.run(user_service.clone(), product_service.clone()).await?; let cli = Cli::parse_from(&["cli", "product", "list"]); cli.run(user_service.clone(), product_service.clone()).await?; // Verify counts let users = user_service.list().await?; let products = product_service.list().await?; assert_eq!(users.len(), 3); assert_eq!(products.len(), 3); Ok(()) } // Test CLI with PostgreSQL repository #[tokio::test] #[serial] async fn test_cli_with_postgres_user_lifecycle() -> Result<()> { let pool = setup_test_db().await; let user_repo = PostgresUserRepository::new(pool.clone()); let product_repo = PostgresProductRepository::new(pool.clone()); let user_service = Service::new(user_repo); let product_service = Service::new(product_repo); // Test user create let (username, email) = unique_test_data("test_user"); let cli = Cli::parse_from(&[ "cli", "user", "create", "--username", &username, "--email", &email ]); cli.run(user_service.clone(), product_service.clone()).await?; // Test user list let cli = Cli::parse_from(&["cli", "user", "list"]); cli.run(user_service.clone(), product_service.clone()).await?; // Test user get (we need to get the ID from the list first) let users = user_service.list().await?; assert!(!users.is_empty()); let user_id = users[0].id().to_string(); let cli = Cli::parse_from(&["cli", "user", "get", "--id", &user_id]); cli.run(user_service.clone(), product_service.clone()).await?; // Test user update let new_username = format!("{}_updated", username); let cli = Cli::parse_from(&[ "cli", "user", "update", "--id", &user_id, "--username", &new_username ]); cli.run(user_service.clone(), product_service.clone()).await?; // Test user delete let cli = Cli::parse_from(&["cli", "user", "delete", "--id", &user_id]); cli.run(user_service.clone(), product_service.clone()).await?; Ok(()) } #[tokio::test] #[serial] async fn test_cli_with_postgres_product_lifecycle() -> Result<()> { let pool = setup_test_db().await; let user_repo = PostgresUserRepository::new(pool.clone()); let product_repo = PostgresProductRepository::new(pool.clone()); let user_service = Service::new(user_repo); let product_service = Service::new(product_repo); // Test product create let (name, description) = unique_test_data("test_product"); let cli = Cli::parse_from(&[ "cli", "product", "create", "--name", &name, "--description", &description ]); cli.run(user_service.clone(), product_service.clone()).await?; // Test product list let cli = Cli::parse_from(&["cli", "product", "list"]); cli.run(user_service.clone(), product_service.clone()).await?; // Test product get (we need to get the ID from the list first) let products = product_service.list().await?; assert!(!products.is_empty()); let product_id = products[0].id().to_string(); let cli = Cli::parse_from(&["cli", "product", "get", "--id", &product_id]); cli.run(user_service.clone(), product_service.clone()).await?; // Test product update let new_name = format!("{}_updated", name); let cli = Cli::parse_from(&[ "cli", "product", "update", "--id", &product_id, "--name", &new_name ]); cli.run(user_service.clone(), product_service.clone()).await?; // Test product delete let cli = Cli::parse_from(&["cli", "product", "delete", "--id", &product_id]); cli.run(user_service.clone(), product_service.clone()).await?; Ok(()) } #[tokio::test] #[serial] async fn test_cli_with_postgres_mixed_operations() -> Result<()> { let pool = setup_test_db().await; let user_repo = PostgresUserRepository::new(pool.clone()); let product_repo = PostgresProductRepository::new(pool.clone()); let user_service = Service::new(user_repo); let product_service = Service::new(product_repo); // Create multiple users and products for i in 1..=3 { let (username, email) = unique_test_data(&format!("user_{}", i)); let cli = Cli::parse_from(&[ "cli", "user", "create", "--username", &username, "--email", &email ]); cli.run(user_service.clone(), product_service.clone()).await?; let (name, description) = unique_test_data(&format!("product_{}", i)); let cli = Cli::parse_from(&[ "cli", "product", "create", "--name", &name, "--description", &description ]); cli.run(user_service.clone(), product_service.clone()).await?; } // List all users and products let cli = Cli::parse_from(&["cli", "user", "list"]); cli.run(user_service.clone(), product_service.clone()).await?; let cli = Cli::parse_from(&["cli", "product", "list"]); cli.run(user_service.clone(), product_service.clone()).await?; // Verify counts let users = user_service.list().await?; let products = product_service.list().await?; assert_eq!(users.len(), 3); assert_eq!(products.len(), 3); Ok(()) } // Test error handling #[tokio::test] #[serial] async fn test_cli_error_handling() -> Result<()> { let user_repo = InMemoryUserRepository::new(); let product_repo = InMemoryProductRepository::new(); let user_service = Service::new(user_repo); let product_service = Service::new(product_repo); // Test getting non-existent user let non_existent_id = Uuid::new_v4(); let cli = Cli::parse_from(&["cli", "user", "get", "--id", &non_existent_id.to_string()]); let result = cli.run(user_service.clone(), product_service.clone()).await; assert!(result.is_err()); // Test getting non-existent product let cli = Cli::parse_from(&["cli", "product", "get", "--id", &non_existent_id.to_string()]); let result = cli.run(user_service.clone(), product_service.clone()).await; assert!(result.is_err()); // Test updating non-existent user let cli = Cli::parse_from(&[ "cli", "user", "update", "--id", &non_existent_id.to_string(), "--username", "new_name" ]); let result = cli.run(user_service.clone(), product_service.clone()).await; assert!(result.is_err()); // Test deleting non-existent user let cli = Cli::parse_from(&["cli", "user", "delete", "--id", &non_existent_id.to_string()]); let result = cli.run(user_service.clone(), product_service.clone()).await; assert!(result.is_err()); Ok(()) } // Test edge cases #[tokio::test] #[serial] async fn test_cli_edge_cases() -> Result<()> { let user_repo = InMemoryUserRepository::new(); let product_repo = InMemoryProductRepository::new(); let user_service = Service::new(user_repo); let product_service = Service::new(product_repo); // Test empty 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 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; 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); let cli = Cli::parse_from(&[ "cli", "user", "create", "--username", &long_string, "--email", "test@example.com" ]); let _result = cli.run(user_service.clone(), product_service.clone()).await; // Note: Very long strings are currently allowed by the domain layer // This test documents the current behavior let cli = Cli::parse_from(&[ "cli", "product", "create", "--name", &long_string, "--description", "test" ]); let _result = cli.run(user_service.clone(), product_service.clone()).await; // Note: Very long strings are currently allowed by the domain layer // This test documents the current behavior // Test special characters let special_chars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"; let cli = Cli::parse_from(&[ "cli", "user", "create", "--username", special_chars, "--email", "test@example.com" ]); let _result = cli.run(user_service.clone(), product_service.clone()).await; // Note: Special characters are currently allowed by the domain layer // This test documents the current behavior Ok(()) } // Test no command provided #[tokio::test] #[serial] async fn test_cli_no_command() -> Result<()> { let user_repo = InMemoryUserRepository::new(); let product_repo = InMemoryProductRepository::new(); let user_service = Service::new(user_repo); let product_service = Service::new(product_repo); let cli = Cli::parse_from(&["cli"]); cli.run(user_service, product_service).await?; Ok(()) }