sharenet/backend/crates/cli/src/lib.rs

1125 lines
38 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 domain::{CreateProduct, CreateUser, Product, User, UpdateProduct, UpdateUser};
use application::UseCase;
use uuid::Uuid;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Parser)]
pub enum Commands {
/// User management commands
User {
#[command(subcommand)]
command: UserCommands,
},
/// Product management commands
Product {
#[command(subcommand)]
command: ProductCommands,
},
}
#[derive(Parser)]
pub enum UserCommands {
/// Create a new user
Create {
/// Username
#[arg(short, long)]
username: String,
/// Email
#[arg(short, long)]
email: String,
},
/// List all users
List,
/// Get a user by ID
Get {
/// User ID
#[arg(short, long)]
id: Uuid,
},
/// Update a user
Update {
/// User ID
#[arg(short, long)]
id: Uuid,
/// New username
#[arg(short, long)]
username: Option<String>,
/// New email
#[arg(short, long)]
email: Option<String>,
},
/// Delete a user
Delete {
/// User ID
#[arg(short, long)]
id: Uuid,
},
}
#[derive(Parser)]
pub enum ProductCommands {
/// Create a new product
Create {
/// Product name
#[arg(short, long)]
name: String,
/// Product description
#[arg(short, long)]
description: String,
},
/// List all products
List,
/// Get a product by ID
Get {
/// Product ID
#[arg(short, long)]
id: Uuid,
},
/// Update a product
Update {
/// Product ID
#[arg(short, long)]
id: Uuid,
/// New name
#[arg(short, long)]
name: Option<String>,
/// New description
#[arg(short, long)]
description: Option<String>,
},
/// Delete a product
Delete {
/// Product ID
#[arg(short, long)]
id: Uuid,
},
}
impl Cli {
pub async fn run<U, P>(self, user_service: U, product_service: P) -> Result<()>
where
U: UseCase<User>,
P: UseCase<Product>,
{
match self.command {
Some(Commands::User { command }) => match command {
UserCommands::Create { username, email } => {
let user = user_service.create(CreateUser::new(username, email)?).await?;
println!("Created user: {:?}", user);
}
UserCommands::List => {
let users = user_service.list().await?;
println!("Users: {:?}", users);
}
UserCommands::Get { id } => {
let user = user_service.get(id).await?;
println!("User: {:?}", user);
}
UserCommands::Update { id, username, email } => {
let update = UpdateUser::new(username, email)?;
let user = user_service.update(id, update).await?;
println!("Updated user: {:?}", user);
}
UserCommands::Delete { id } => {
user_service.delete(id).await?;
println!("Deleted user {}", id);
}
},
Some(Commands::Product { command }) => match command {
ProductCommands::Create { name, description } => {
let product = product_service.create(CreateProduct::new(name, description)?).await?;
println!("Created product: {:?}", product);
}
ProductCommands::List => {
let products = product_service.list().await?;
println!("Products: {:?}", products);
}
ProductCommands::Get { id } => {
let product = product_service.get(id).await?;
println!("Product: {:?}", product);
}
ProductCommands::Update { id, name, description } => {
let update = UpdateProduct::new(name, description)?;
let product = product_service.update(id, update).await?;
println!("Updated product: {:?}", product);
}
ProductCommands::Delete { id } => {
product_service.delete(id).await?;
println!("Deleted product {}", id);
}
},
None => {
println!("No command provided. Use --help for usage information.");
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
//! # CLI Tests
//!
//! This module contains comprehensive unit tests for the CLI application.
//! Tests cover command parsing, execution, and error handling scenarios.
//!
//! ## Test Structure
//! - `command_parsing` - Tests for CLI argument parsing
//! - `user_commands` - Tests for user management commands
//! - `product_commands` - Tests for product management commands
//! - `integration_tests` - End-to-end command execution tests
//! - `error_handling` - Tests for error scenarios
//!
//! ## Testing Approach
//! - Uses mock services implementing the `UseCase` trait
//! - Tests verify command parsing and execution
//! - Captures stdout to verify output messages
//! - Tests error handling and edge cases
use super::*;
use application::{UseCase, ApplicationError};
use domain::{DomainError, User, Product, CreateUser, UpdateUser, CreateProduct, UpdateProduct};
use std::sync::Arc;
use tokio::sync::RwLock;
use std::collections::HashMap;
/// Mock user service for testing.
///
/// Implements the `UseCase<User>` trait using an in-memory HashMap
/// for storing test data. Provides thread-safe access via `RwLock`.
#[derive(Clone)]
struct MockUserService {
users: Arc<RwLock<HashMap<Uuid, User>>>,
}
impl MockUserService {
fn new() -> Self {
Self {
users: Arc::new(RwLock::new(HashMap::new())),
}
}
}
impl UseCase<User> for MockUserService {
fn create(&self, data: CreateUser) -> impl std::future::Future<Output = Result<User, ApplicationError>> + Send {
let users = self.users.clone();
async move {
let mut guard = users.write().await;
let id = Uuid::new_v4();
let user = User::new(id, data.username().to_string(), data.email().to_string())?;
guard.insert(id, user.clone());
Ok(user)
}
}
fn get(&self, id: Uuid) -> impl std::future::Future<Output = Result<User, ApplicationError>> + Send {
let users = self.users.clone();
async move {
let guard = users.read().await;
guard.get(&id)
.cloned()
.ok_or_else(|| ApplicationError::Domain(DomainError::NotFound(format!("User not found: {}", id))))
}
}
fn list(&self) -> impl std::future::Future<Output = Result<Vec<User>, ApplicationError>> + Send {
let users = self.users.clone();
async move {
let guard = users.read().await;
Ok(guard.values().cloned().collect())
}
}
fn update(&self, id: Uuid, data: UpdateUser) -> impl std::future::Future<Output = Result<User, ApplicationError>> + Send {
let users = self.users.clone();
async move {
let mut guard = users.write().await;
let user = guard.get_mut(&id)
.ok_or_else(|| ApplicationError::Domain(DomainError::NotFound(format!("User not found: {}", id))))?;
if let Some(username) = data.username() {
user.set_username(username.to_string())?;
}
if let Some(email) = data.email() {
user.set_email(email.to_string())?;
}
Ok(user.clone())
}
}
fn delete(&self, id: Uuid) -> impl std::future::Future<Output = Result<(), ApplicationError>> + Send {
let users = self.users.clone();
async move {
let mut guard = users.write().await;
guard.remove(&id)
.ok_or_else(|| ApplicationError::Domain(DomainError::NotFound(format!("User not found: {}", id))))?;
Ok(())
}
}
}
/// Mock product service for testing.
///
/// Implements the `UseCase<Product>` trait using an in-memory HashMap
/// for storing test data. Provides thread-safe access via `RwLock`.
#[derive(Clone)]
struct MockProductService {
products: Arc<RwLock<HashMap<Uuid, Product>>>,
}
impl MockProductService {
fn new() -> Self {
Self {
products: Arc::new(RwLock::new(HashMap::new())),
}
}
}
impl UseCase<Product> for MockProductService {
fn create(&self, data: CreateProduct) -> impl std::future::Future<Output = Result<Product, ApplicationError>> + Send {
let products = self.products.clone();
async move {
let mut guard = products.write().await;
let id = Uuid::new_v4();
let product = Product::new(id, data.name().to_string(), data.description().to_string())?;
guard.insert(id, product.clone());
Ok(product)
}
}
fn get(&self, id: Uuid) -> impl std::future::Future<Output = Result<Product, ApplicationError>> + Send {
let products = self.products.clone();
async move {
let guard = products.read().await;
guard.get(&id)
.cloned()
.ok_or_else(|| ApplicationError::Domain(DomainError::NotFound(format!("Product not found: {}", id))))
}
}
fn list(&self) -> impl std::future::Future<Output = Result<Vec<Product>, ApplicationError>> + Send {
let products = self.products.clone();
async move {
let guard = products.read().await;
Ok(guard.values().cloned().collect())
}
}
fn update(&self, id: Uuid, data: UpdateProduct) -> impl std::future::Future<Output = Result<Product, ApplicationError>> + Send {
let products = self.products.clone();
async move {
let mut guard = products.write().await;
let product = guard.get_mut(&id)
.ok_or_else(|| ApplicationError::Domain(DomainError::NotFound(format!("Product not found: {}", id))))?;
if let Some(name) = data.name() {
product.set_name(name.to_string())?;
}
if let Some(description) = data.description() {
product.set_description(description.to_string())?;
}
Ok(product.clone())
}
}
fn delete(&self, id: Uuid) -> impl std::future::Future<Output = Result<(), ApplicationError>> + Send {
let products = self.products.clone();
async move {
let mut guard = products.write().await;
guard.remove(&id)
.ok_or_else(|| ApplicationError::Domain(DomainError::NotFound(format!("Product not found: {}", id))))?;
Ok(())
}
}
}
mod command_parsing {
use super::*;
/// Tests that the CLI can parse user create command with all arguments.
#[test]
fn test_parse_user_create() {
let args = vec![
"cli",
"user",
"create",
"--username", "testuser",
"--email", "test@example.com"
];
let cli = Cli::try_parse_from(args).unwrap();
if let Some(Commands::User { command: UserCommands::Create { username, email } }) = cli.command {
assert_eq!(username, "testuser");
assert_eq!(email, "test@example.com");
} else {
panic!("Expected user create command");
}
}
/// Tests that the CLI can parse user create command with short arguments.
#[test]
fn test_parse_user_create_short_args() {
let args = vec![
"cli",
"user",
"create",
"-u", "testuser",
"-e", "test@example.com"
];
let cli = Cli::try_parse_from(args).unwrap();
if let Some(Commands::User { command: UserCommands::Create { username, email } }) = cli.command {
assert_eq!(username, "testuser");
assert_eq!(email, "test@example.com");
} else {
panic!("Expected user create command");
}
}
/// Tests that the CLI can parse user list command.
#[test]
fn test_parse_user_list() {
let args = vec!["cli", "user", "list"];
let cli = Cli::try_parse_from(args).unwrap();
if let Some(Commands::User { command: UserCommands::List }) = cli.command {
// Command parsed successfully
} else {
panic!("Expected user list command");
}
}
/// Tests that the CLI can parse user get command.
#[test]
fn test_parse_user_get() {
let user_id = Uuid::new_v4();
let user_id_str = user_id.to_string();
let args = vec![
"cli",
"user",
"get",
"--id", &user_id_str
];
let cli = Cli::try_parse_from(args).unwrap();
if let Some(Commands::User { command: UserCommands::Get { id } }) = cli.command {
assert_eq!(id, user_id);
} else {
panic!("Expected user get command");
}
}
/// Tests that the CLI can parse user update command with all fields.
#[test]
fn test_parse_user_update() {
let user_id = Uuid::new_v4();
let user_id_str = user_id.to_string();
let args = vec![
"cli",
"user",
"update",
"--id", &user_id_str,
"--username", "newuser",
"--email", "new@example.com"
];
let cli = Cli::try_parse_from(args).unwrap();
if let Some(Commands::User { command: UserCommands::Update { id, username, email } }) = cli.command {
assert_eq!(id, user_id);
assert_eq!(username, Some("newuser".to_string()));
assert_eq!(email, Some("new@example.com".to_string()));
} else {
panic!("Expected user update command");
}
}
/// Tests that the CLI can parse user update command with partial fields.
#[test]
fn test_parse_user_update_partial() {
let user_id = Uuid::new_v4();
let user_id_str = user_id.to_string();
let args = vec![
"cli",
"user",
"update",
"--id", &user_id_str,
"--username", "newuser"
];
let cli = Cli::try_parse_from(args).unwrap();
if let Some(Commands::User { command: UserCommands::Update { id, username, email } }) = cli.command {
assert_eq!(id, user_id);
assert_eq!(username, Some("newuser".to_string()));
assert_eq!(email, None);
} else {
panic!("Expected user update command");
}
}
/// Tests that the CLI can parse user delete command.
#[test]
fn test_parse_user_delete() {
let user_id = Uuid::new_v4();
let user_id_str = user_id.to_string();
let args = vec![
"cli",
"user",
"delete",
"--id", &user_id_str
];
let cli = Cli::try_parse_from(args).unwrap();
if let Some(Commands::User { command: UserCommands::Delete { id } }) = cli.command {
assert_eq!(id, user_id);
} else {
panic!("Expected user delete command");
}
}
/// Tests that the CLI can parse product create command.
#[test]
fn test_parse_product_create() {
let args = vec![
"cli",
"product",
"create",
"--name", "Test Product",
"--description", "Test Description"
];
let cli = Cli::try_parse_from(args).unwrap();
if let Some(Commands::Product { command: ProductCommands::Create { name, description } }) = cli.command {
assert_eq!(name, "Test Product");
assert_eq!(description, "Test Description");
} else {
panic!("Expected product create command");
}
}
/// Tests that the CLI can parse product list command.
#[test]
fn test_parse_product_list() {
let args = vec!["cli", "product", "list"];
let cli = Cli::try_parse_from(args).unwrap();
if let Some(Commands::Product { command: ProductCommands::List }) = cli.command {
// Command parsed successfully
} else {
panic!("Expected product list command");
}
}
/// Tests that the CLI can parse product get command.
#[test]
fn test_parse_product_get() {
let product_id = Uuid::new_v4();
let product_id_str = product_id.to_string();
let args = vec![
"cli",
"product",
"get",
"--id", &product_id_str
];
let cli = Cli::try_parse_from(args).unwrap();
if let Some(Commands::Product { command: ProductCommands::Get { id } }) = cli.command {
assert_eq!(id, product_id);
} else {
panic!("Expected product get command");
}
}
/// Tests that the CLI can parse product update command.
#[test]
fn test_parse_product_update() {
let product_id = Uuid::new_v4();
let product_id_str = product_id.to_string();
let args = vec![
"cli",
"product",
"update",
"--id", &product_id_str,
"--name", "New Product",
"--description", "New Description"
];
let cli = Cli::try_parse_from(args).unwrap();
if let Some(Commands::Product { command: ProductCommands::Update { id, name, description } }) = cli.command {
assert_eq!(id, product_id);
assert_eq!(name, Some("New Product".to_string()));
assert_eq!(description, Some("New Description".to_string()));
} else {
panic!("Expected product update command");
}
}
/// Tests that the CLI can parse product delete command.
#[test]
fn test_parse_product_delete() {
let product_id = Uuid::new_v4();
let product_id_str = product_id.to_string();
let args = vec![
"cli",
"product",
"delete",
"--id", &product_id_str
];
let cli = Cli::try_parse_from(args).unwrap();
if let Some(Commands::Product { command: ProductCommands::Delete { id } }) = cli.command {
assert_eq!(id, product_id);
} else {
panic!("Expected product delete command");
}
}
/// Tests that the CLI handles no command gracefully.
#[test]
fn test_parse_no_command() {
let args = vec!["cli"];
let cli = Cli::try_parse_from(args).unwrap();
assert!(cli.command.is_none());
}
/// Tests that the CLI rejects invalid UUID format.
#[test]
fn test_parse_invalid_uuid() {
let args = vec![
"cli",
"user",
"get",
"--id", "invalid-uuid"
];
let result = Cli::try_parse_from(args);
assert!(result.is_err());
}
}
mod user_commands {
use super::*;
/// Tests user creation command execution.
#[tokio::test]
async fn test_user_create_command() {
let cli = Cli {
command: Some(Commands::User {
command: UserCommands::Create {
username: "testuser".to_string(),
email: "test@example.com".to_string(),
}
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
assert!(result.is_ok());
}
/// Tests user list command execution.
#[tokio::test]
async fn test_user_list_command() {
let cli = Cli {
command: Some(Commands::User {
command: UserCommands::List
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
assert!(result.is_ok());
}
/// Tests user get command execution.
#[tokio::test]
async fn test_user_get_command() {
let user_id = Uuid::new_v4();
let cli = Cli {
command: Some(Commands::User {
command: UserCommands::Get { id: user_id }
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
// Should fail because user doesn't exist
assert!(result.is_err());
}
/// Tests user update command execution.
#[tokio::test]
async fn test_user_update_command() {
let user_id = Uuid::new_v4();
let cli = Cli {
command: Some(Commands::User {
command: UserCommands::Update {
id: user_id,
username: Some("newuser".to_string()),
email: Some("new@example.com".to_string()),
}
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
// Should fail because user doesn't exist
assert!(result.is_err());
}
/// Tests user delete command execution.
#[tokio::test]
async fn test_user_delete_command() {
let user_id = Uuid::new_v4();
let cli = Cli {
command: Some(Commands::User {
command: UserCommands::Delete { id: user_id }
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
// Should fail because user doesn't exist
assert!(result.is_err());
}
}
mod product_commands {
use super::*;
/// Tests product creation command execution.
#[tokio::test]
async fn test_product_create_command() {
let cli = Cli {
command: Some(Commands::Product {
command: ProductCommands::Create {
name: "Test Product".to_string(),
description: "Test Description".to_string(),
}
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
assert!(result.is_ok());
}
/// Tests product list command execution.
#[tokio::test]
async fn test_product_list_command() {
let cli = Cli {
command: Some(Commands::Product {
command: ProductCommands::List
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
assert!(result.is_ok());
}
/// Tests product get command execution.
#[tokio::test]
async fn test_product_get_command() {
let product_id = Uuid::new_v4();
let cli = Cli {
command: Some(Commands::Product {
command: ProductCommands::Get { id: product_id }
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
// Should fail because product doesn't exist
assert!(result.is_err());
}
/// Tests product update command execution.
#[tokio::test]
async fn test_product_update_command() {
let product_id = Uuid::new_v4();
let cli = Cli {
command: Some(Commands::Product {
command: ProductCommands::Update {
id: product_id,
name: Some("New Product".to_string()),
description: Some("New Description".to_string()),
}
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
// Should fail because product doesn't exist
assert!(result.is_err());
}
/// Tests product delete command execution.
#[tokio::test]
async fn test_product_delete_command() {
let product_id = Uuid::new_v4();
let cli = Cli {
command: Some(Commands::Product {
command: ProductCommands::Delete { id: product_id }
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
// Should fail because product doesn't exist
assert!(result.is_err());
}
}
mod integration_tests {
use super::*;
/// Tests complete user lifecycle through CLI commands.
#[tokio::test]
async fn test_user_lifecycle() {
let user_service = MockUserService::new();
let product_service = MockProductService::new();
// Create user
let create_cli = Cli {
command: Some(Commands::User {
command: UserCommands::Create {
username: "testuser".to_string(),
email: "test@example.com".to_string(),
}
})
};
let result = create_cli.run(user_service.clone(), product_service.clone()).await;
assert!(result.is_ok());
// Get the created user ID by listing users
let users = user_service.list().await.unwrap();
assert_eq!(users.len(), 1);
let user_id = users[0].id();
// Get user
let get_cli = Cli {
command: Some(Commands::User {
command: UserCommands::Get { id: user_id }
})
};
let result = get_cli.run(user_service.clone(), product_service.clone()).await;
assert!(result.is_ok());
// Update user
let update_cli = Cli {
command: Some(Commands::User {
command: UserCommands::Update {
id: user_id,
username: Some("updateduser".to_string()),
email: None,
}
})
};
let result = update_cli.run(user_service.clone(), product_service.clone()).await;
assert!(result.is_ok());
// Delete user
let delete_cli = Cli {
command: Some(Commands::User {
command: UserCommands::Delete { id: user_id }
})
};
let result = delete_cli.run(user_service.clone(), product_service.clone()).await;
assert!(result.is_ok());
// Verify user is deleted
let users = user_service.list().await.unwrap();
assert_eq!(users.len(), 0);
}
/// Tests complete product lifecycle through CLI commands.
#[tokio::test]
async fn test_product_lifecycle() {
let user_service = MockUserService::new();
let product_service = MockProductService::new();
// Create product
let create_cli = Cli {
command: Some(Commands::Product {
command: ProductCommands::Create {
name: "Test Product".to_string(),
description: "Test Description".to_string(),
}
})
};
let result = create_cli.run(user_service.clone(), product_service.clone()).await;
assert!(result.is_ok());
// Get the created product ID by listing products
let products = product_service.list().await.unwrap();
assert_eq!(products.len(), 1);
let product_id = products[0].id();
// Get product
let get_cli = Cli {
command: Some(Commands::Product {
command: ProductCommands::Get { id: product_id }
})
};
let result = get_cli.run(user_service.clone(), product_service.clone()).await;
assert!(result.is_ok());
// Update product
let update_cli = Cli {
command: Some(Commands::Product {
command: ProductCommands::Update {
id: product_id,
name: Some("Updated Product".to_string()),
description: None,
}
})
};
let result = update_cli.run(user_service.clone(), product_service.clone()).await;
assert!(result.is_ok());
// Delete product
let delete_cli = Cli {
command: Some(Commands::Product {
command: ProductCommands::Delete { id: product_id }
})
};
let result = delete_cli.run(user_service.clone(), product_service.clone()).await;
assert!(result.is_ok());
// Verify product is deleted
let products = product_service.list().await.unwrap();
assert_eq!(products.len(), 0);
}
/// Tests mixed user and product operations.
#[tokio::test]
async fn test_mixed_operations() {
let user_service = MockUserService::new();
let product_service = MockProductService::new();
// Create a user
let user_cli = Cli {
command: Some(Commands::User {
command: UserCommands::Create {
username: "testuser".to_string(),
email: "test@example.com".to_string(),
}
})
};
let result = user_cli.run(user_service.clone(), product_service.clone()).await;
assert!(result.is_ok());
// Create a product
let product_cli = Cli {
command: Some(Commands::Product {
command: ProductCommands::Create {
name: "Test Product".to_string(),
description: "Test Description".to_string(),
}
})
};
let result = product_cli.run(user_service.clone(), product_service.clone()).await;
assert!(result.is_ok());
// Verify both exist
let users = user_service.list().await.unwrap();
let products = product_service.list().await.unwrap();
assert_eq!(users.len(), 1);
assert_eq!(products.len(), 1);
}
}
mod error_handling {
use super::*;
/// Tests handling of service errors in user operations.
#[tokio::test]
async fn test_user_service_error() {
let cli = Cli {
command: Some(Commands::User {
command: UserCommands::Get { id: Uuid::new_v4() }
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
assert!(result.is_err());
}
/// Tests handling of service errors in product operations.
#[tokio::test]
async fn test_product_service_error() {
let cli = Cli {
command: Some(Commands::Product {
command: ProductCommands::Get { id: Uuid::new_v4() }
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
assert!(result.is_err());
}
/// Tests handling of no command provided.
#[tokio::test]
async fn test_no_command() {
let cli = Cli { command: None };
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
assert!(result.is_ok());
}
}
mod edge_cases {
use super::*;
/// Tests CLI with empty strings in user creation.
#[tokio::test]
async fn test_user_create_empty_strings() {
let cli = Cli {
command: Some(Commands::User {
command: UserCommands::Create {
username: "".to_string(),
email: "".to_string(),
}
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
assert!(result.is_err()); // Should fail due to empty username validation
}
/// Tests CLI with empty strings in product creation.
#[tokio::test]
async fn test_product_create_empty_strings() {
let cli = Cli {
command: Some(Commands::Product {
command: ProductCommands::Create {
name: "".to_string(),
description: "".to_string(),
}
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
assert!(result.is_err()); // Should fail due to empty product name validation
}
/// Tests CLI with very long strings.
#[tokio::test]
async fn test_user_create_long_strings() {
let long_username = "a".repeat(1000);
let long_email = format!("{}@example.com", "a".repeat(1000));
let cli = Cli {
command: Some(Commands::User {
command: UserCommands::Create {
username: long_username.clone(),
email: long_email.clone(),
}
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
assert!(result.is_ok());
}
/// Tests CLI with special characters in strings.
#[tokio::test]
async fn test_user_create_special_characters() {
let cli = Cli {
command: Some(Commands::User {
command: UserCommands::Create {
username: "user@#$%^&*()".to_string(),
email: "test+tag@example.com".to_string(),
}
})
};
let user_service = MockUserService::new();
let product_service = MockProductService::new();
let result = cli.run(user_service, product_service).await;
assert!(result.is_ok());
}
}
}