sharenet/backend/crates/integration-tests/src/cli_tests.rs
continuist 61117b6fa6
Some checks are pending
CI/CD Pipeline / Test Backend (push) Waiting to run
CI/CD Pipeline / Test Frontend (push) Waiting to run
CI/CD Pipeline / Build and Push Docker Images (push) Blocked by required conditions
CI/CD Pipeline / Deploy to Production (push) Blocked by required conditions
Created test_setup.rs file that consolidates test setup code for interface tests, including db migrations
2025-06-28 01:57:12 -04:00

405 lines
No EOL
15 KiB
Rust

/*
* 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 <continuist02@gmail.com>
*/
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(())
}