veza/veza-common/tests/common_tests.rs
2025-12-03 22:24:14 +01:00

429 lines
13 KiB
Rust

//! Common tests for Veza Common Library
//!
//! This module provides integration tests and test utilities for the Veza common library.
use veza_common::utils::{
generate_uuid, format_duration, format_file_size,
validate_email, validate_username, validate_password,
validate_email_result, validate_username_result,
to_json, from_json,
format_timestamp, parse_date, format_relative_time,
format_log_message, StructuredLogEntry,
};
use veza_common::types::{
Track,
};
use veza_common::config::{
DatabaseConfig, RedisConfig,
};
use veza_common::error::{
CommonError, ErrorResponse,
};
/// Test fixtures for common library tests
pub mod fixtures {
use veza_common::types::{User, Track, Playlist};
use veza_common::config::{DatabaseConfig, RedisConfig};
use uuid::Uuid;
use std::collections::HashMap;
/// Create a test user
pub fn create_test_user(id: i64) -> User {
User::new(id, format!("testuser{}", id), format!("test{}@example.com", id))
}
/// Create a test track
pub fn create_test_track(id: Uuid) -> Track {
Track::new(id, "Test Track".to_string(), "Test Artist".to_string(), 180)
}
/// Create a test playlist
pub fn create_test_playlist(id: Uuid, owner_id: Uuid) -> Playlist {
Playlist::new(id, "Test Playlist".to_string(), owner_id)
}
/// Create a test database configuration
pub fn create_test_database_config() -> DatabaseConfig {
DatabaseConfig::new(
"postgresql://test:test@localhost:5432/test_db".to_string(),
10
)
}
/// Create a test Redis configuration
pub fn create_test_redis_config() -> RedisConfig {
RedisConfig::new("redis://localhost:6379".to_string())
}
/// Create test context for logging
pub fn create_test_context() -> HashMap<String, String> {
let mut context = HashMap::new();
context.insert("user_id".to_string(), "123".to_string());
context.insert("ip".to_string(), "192.168.1.1".to_string());
context.insert("request_id".to_string(), Uuid::new_v4().to_string());
context
}
}
/// Test helpers for common library tests
pub mod helpers {
use veza_common::types::{ApiResponse, PaginationParams, PaginatedResponse};
/// Assert that a result is ok
pub fn assert_ok<T, E: std::fmt::Debug>(result: Result<T, E>) -> T {
result.expect("Expected Ok result")
}
/// Assert that a result is an error
pub fn assert_err<T: std::fmt::Debug, E>(result: Result<T, E>) -> E {
result.expect_err("Expected Err result")
}
/// Assert that two values are equal
pub fn assert_eq_expected<T: PartialEq + std::fmt::Debug>(
actual: T,
expected: T,
message: &str,
) {
assert_eq!(
actual, expected,
"{}: expected {:?}, got {:?}",
message, expected, actual
);
}
/// Create a test API response
pub fn create_test_api_response<T>(data: T) -> ApiResponse<T> {
ApiResponse::success(data)
}
/// Create a test error API response
pub fn create_test_error_response(message: &str) -> ApiResponse<String> {
ApiResponse::error(message.to_string())
}
/// Create test pagination parameters
pub fn create_test_pagination(page: u32, limit: u32) -> PaginationParams {
PaginationParams { page, limit }
}
/// Create test paginated response
pub fn create_test_paginated_response<T>(items: Vec<T>, total: u64) -> PaginatedResponse<T> {
PaginatedResponse::new(items, total, 1, 20)
}
}
#[cfg(test)]
mod tests {
use super::*;
use fixtures::*;
use helpers::*;
use uuid::Uuid;
#[test]
fn test_common_utilities() {
// Test UUID generation
let uuid1 = generate_uuid();
let uuid2 = generate_uuid();
assert_ne!(uuid1, uuid2);
// Test duration formatting
let formatted = format_duration(125);
assert_eq!(formatted, "2:05");
// Test file size formatting
let size = format_file_size(1024);
assert_eq!(size, "1.00 KB");
}
#[test]
fn test_validation_utilities() {
// Test email validation
assert!(validate_email("test@example.com"));
assert!(!validate_email("invalid-email"));
// Test username validation
assert!(validate_username("user123"));
assert!(!validate_username("ab")); // Too short
// Test password validation
assert!(validate_password("Password123"));
assert!(!validate_password("short")); // Too short
}
#[test]
fn test_serialization_utilities() {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct TestData {
name: String,
value: i32,
}
let data = TestData {
name: "test".to_string(),
value: 42,
};
// Test serialization
let json = to_json(&data).unwrap();
assert!(json.contains("test"));
assert!(json.contains("42"));
// Test deserialization
let deserialized: TestData = from_json(&json).unwrap();
assert_eq!(data, deserialized);
}
#[test]
fn test_date_utilities() {
use chrono::{Utc, Duration, Datelike};
// Test timestamp formatting
let timestamp = 1609459200; // 2021-01-01 00:00:00 UTC
let formatted = format_timestamp(timestamp);
assert!(formatted.contains("2021"));
// Test date parsing
let date_str = "2021-01-01T00:00:00Z";
let dt = parse_date(date_str).unwrap();
assert_eq!(dt.year(), 2021);
// Test relative time
let now = Utc::now();
let past = now - Duration::hours(2);
let relative = format_relative_time(&past, &now);
assert!(relative.contains("2"));
assert!(relative.contains("hour"));
}
#[test]
fn test_logging_utilities() {
// Test log message formatting
let message = format_log_message("api", "INFO", "Test message");
assert!(message.contains("api"));
assert!(message.contains("INFO"));
assert!(message.contains("Test message"));
// Test structured log entry
let entry = StructuredLogEntry::new("api", "INFO", "Test message")
.with_context("user_id".to_string(), "123".to_string());
let json = entry.to_json();
assert!(json.contains("api"));
assert!(json.contains("user_id"));
assert!(json.contains("123"));
}
#[test]
fn test_type_utilities() {
// Test User
let user = create_test_user(1);
assert_eq!(user.id, 1);
assert_eq!(user.username, "testuser1");
assert!(user.validate().is_ok());
// Test Track
let track_id = Uuid::new_v4();
let track = create_test_track(track_id);
assert_eq!(track.id, track_id);
assert_eq!(track.title, "Test Track");
assert!(track.validate().is_ok());
// Test Playlist
let playlist_id = Uuid::new_v4();
let owner_id = Uuid::new_v4();
let playlist = create_test_playlist(playlist_id, owner_id);
assert_eq!(playlist.id, playlist_id);
assert_eq!(playlist.owner_id, owner_id);
assert!(playlist.validate().is_ok());
}
#[test]
fn test_config_utilities() {
// Test DatabaseConfig
let db_config = create_test_database_config();
assert!(db_config.validate().is_ok());
assert_eq!(db_config.host(), Some("localhost".to_string()));
assert_eq!(db_config.port(), Some(5432));
// Test RedisConfig
let redis_config = create_test_redis_config();
assert!(redis_config.validate().is_ok());
assert_eq!(redis_config.host(), Some("localhost".to_string()));
assert_eq!(redis_config.port(), Some(6379));
}
#[test]
fn test_api_response_utilities() {
// Test success response
let response = create_test_api_response("test data");
assert!(response.success);
assert!(response.data.is_some());
assert_eq!(response.data.unwrap(), "test data");
// Test error response
let error_response = create_test_error_response("Test error");
assert!(!error_response.success);
assert!(error_response.data.is_none());
assert_eq!(error_response.error, Some("Test error".to_string()));
}
#[test]
fn test_pagination_utilities() {
// Test pagination parameters
let params = create_test_pagination(2, 50);
assert_eq!(params.page, 2);
assert_eq!(params.limit, 50);
// Test paginated response
let items = vec![1, 2, 3, 4, 5];
let response = create_test_paginated_response(items.clone(), 100);
assert_eq!(response.items.len(), 5);
assert_eq!(response.total, 100);
assert_eq!(response.total_pages, 5); // 100 / 20
}
#[test]
fn test_error_handling() {
// Test CommonError
let error = CommonError::NotFound("Resource not found".to_string());
assert_eq!(error.code(), "NOT_FOUND");
assert_eq!(error.http_status_code(), 404);
assert_eq!(error.message(), "Resource not found");
// Test ErrorResponse
let error_response: ErrorResponse = (&error).into();
assert_eq!(error_response.code, "NOT_FOUND");
assert_eq!(error_response.status, 404);
}
#[test]
fn test_validation_result() {
// Test validation with Result
assert!(validate_email_result("test@example.com").is_ok());
assert!(validate_email_result("invalid-email").is_err());
assert!(validate_username_result("user123").is_ok());
assert!(validate_username_result("ab").is_err());
}
#[test]
fn test_config_validation() {
// Test DatabaseConfig validation
let mut db_config = create_test_database_config();
assert!(db_config.validate().is_ok());
// Test invalid URL
db_config.url = "invalid-url".to_string();
assert!(db_config.validate().is_err());
// Test RedisConfig validation
let mut redis_config = create_test_redis_config();
assert!(redis_config.validate().is_ok());
// Test invalid URL
redis_config.url = "invalid-url".to_string();
assert!(redis_config.validate().is_err());
}
#[test]
fn test_playlist_operations() {
let playlist_id = Uuid::new_v4();
let owner_id = Uuid::new_v4();
let track_id = Uuid::new_v4();
let mut playlist = create_test_playlist(playlist_id, owner_id);
// Test adding tracks
playlist.add_track(track_id);
assert_eq!(playlist.track_count(), 1);
assert!(!playlist.is_empty());
// Test removing tracks
playlist.remove_track(track_id);
assert_eq!(playlist.track_count(), 0);
assert!(playlist.is_empty());
}
#[test]
fn test_track_utilities() {
let track_id = Uuid::new_v4();
let track = Track::new(track_id, "Test Song".to_string(), "Test Artist".to_string(), 125);
// Test formatted duration
assert_eq!(track.formatted_duration(), "2:05");
// Test validation
assert!(track.validate().is_ok());
}
#[test]
fn test_helper_functions() {
// Test assert_ok
let ok_result: Result<i32, String> = Ok(42);
let value = assert_ok(ok_result);
assert_eq!(value, 42);
// Test assert_err
let err_result: Result<i32, String> = Err("Error".to_string());
let error = assert_err(err_result);
assert_eq!(error, "Error");
// Test assert_eq_expected
assert_eq_expected(5, 5, "Values should be equal");
}
#[test]
fn test_context_creation() {
let context = create_test_context();
assert!(context.contains_key("user_id"));
assert!(context.contains_key("ip"));
assert!(context.contains_key("request_id"));
}
#[test]
fn test_round_trip_serialization() {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct TestStruct {
id: i32,
name: String,
active: bool,
}
let original = TestStruct {
id: 1,
name: "Test".to_string(),
active: true,
};
// Serialize
let json = to_json(&original).unwrap();
// Deserialize
let deserialized: TestStruct = from_json(&json).unwrap();
assert_eq!(original, deserialized);
}
#[test]
fn test_config_url_parsing() {
// Test database URL parsing
let db_config = DatabaseConfig::new(
"postgresql://user:pass@localhost:5433/mydb?sslmode=require".to_string(),
10
);
assert_eq!(db_config.host(), Some("localhost".to_string()));
assert_eq!(db_config.port(), Some(5433));
assert_eq!(db_config.database_name(), Some("mydb".to_string()));
// Test Redis URL parsing
let redis_config = RedisConfig::new("redis://user:pass@localhost:6380/1".to_string());
assert_eq!(redis_config.host(), Some("localhost".to_string()));
assert_eq!(redis_config.port(), Some(6380));
}
}