#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::needless_raw_string_hashes, clippy::duration_suboptimal_units, clippy::branches_sharing_code, clippy::used_underscore_binding, clippy::single_char_pattern, clippy::ignore_without_reason, clippy::cloned_ref_to_slice_refs, clippy::doc_overindented_list_items, clippy::match_wildcard_for_single_variants, clippy::ignored_unit_patterns, clippy::needless_collect, clippy::unnecessary_map_or, clippy::manual_flatten, clippy::manual_strip, clippy::future_not_send, clippy::unnested_or_patterns, clippy::no_effect_underscore_binding, clippy::literal_string_with_formatting_args)]
//! Production Readiness Validation with Testcontainers
//!
//! This module provides comprehensive production readiness validation using testcontainers
//! to test against real services and validate production deployment scenarios.
use assert_cmd::prelude::*;
use assert_fs::prelude::*;
use predicates::prelude::*;
use std::process::Command;
use std::time::Duration;
use tempfile::TempDir;
use testcontainers::clients::Cli;
use testcontainers::core::WaitFor;
use testcontainers::images::generic::GenericImage;
use testcontainers::images::postgres::PostgresImage;
use testcontainers::images::redis::RedisImage;
use testcontainers::Container;
use testcontainers::RunnableImage;
use tokio::time::sleep;
/// Testcontainers client for managing containers
pub struct TestEnvironment {
pub client: Cli,
pub temp_dir: TempDir,
}
impl TestEnvironment {
pub fn new() -> Self {
Self {
client: Cli::default(),
temp_dir: TempDir::new().unwrap(),
}
}
pub fn temp_path(&self) -> &std::path::Path {
self.temp_dir.path()
}
}
/// PostgreSQL test container for database integration testing
pub struct PostgresTestContainer {
pub container: Container<'static, PostgresImage>,
pub connection_string: String,
}
impl PostgresTestContainer {
pub fn new(client: &Cli) -> Self {
let image = PostgresImage::default()
.with_env_var("POSTGRES_DB", "ggen_test")
.with_env_var("POSTGRES_USER", "ggen")
.with_env_var("POSTGRES_PASSWORD", "ggen_password");
let container = client.run(image);
let port = container.get_host_port_ipv4(5432);
let connection_string = format!("postgresql://ggen:ggen_password@localhost:{}", port);
Self {
container,
connection_string,
}
}
pub async fn wait_for_ready(&self) {
// Wait for PostgreSQL to be ready
sleep(Duration::from_secs(5)).await;
}
}
/// Redis test container for caching and session storage testing
pub struct RedisTestContainer {
pub container: Container<'static, RedisImage>,
pub connection_string: String,
}
impl RedisTestContainer {
pub fn new(client: &Cli) -> Self {
let image = RedisImage::default();
let container = client.run(image);
let port = container.get_host_port_ipv4(6379);
let connection_string = format!("redis://localhost:{}", port);
Self {
container,
connection_string,
}
}
pub async fn wait_for_ready(&self) {
// Wait for Redis to be ready
sleep(Duration::from_secs(2)).await;
}
}
/// Mock API server container for testing external integrations
pub struct MockApiContainer {
pub container: Container<'static, GenericImage>,
pub base_url: String,
}
impl MockApiContainer {
pub fn new(client: &Cli) -> Self {
// Use a simple HTTP server image for mocking API responses
let image = GenericImage::new("nginx", "alpine")
.with_exposed_port(80)
.with_wait_for(WaitFor::message_on_stdout("start worker process"));
let container = client.run(image);
let port = container.get_host_port_ipv4(80);
let base_url = format!("http://localhost:{}", port);
Self {
container,
base_url,
}
}
pub async fn wait_for_ready(&self) {
// Wait for nginx to be ready
sleep(Duration::from_secs(3)).await;
}
}
/// Production readiness test suite using testcontainers
#[tokio::test]
async fn test_production_readiness_database_integration() {
let env = TestEnvironment::new();
let postgres = PostgresTestContainer::new(&env.client);
postgres.wait_for_ready().await;
// Test database connectivity and schema validation
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"init",
"--config",
&format!("database_url={}", postgres.connection_string),
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Database connection validated"));
}
#[tokio::test]
async fn test_production_readiness_cache_integration() {
let env = TestEnvironment::new();
let redis = RedisTestContainer::new(&env.client);
redis.wait_for_ready().await;
// Test Redis connectivity and caching
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"init",
"--config",
&format!("cache_url={}", redis.connection_string),
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Cache connection validated"));
}
#[tokio::test]
async fn test_production_readiness_api_integration() {
let env = TestEnvironment::new();
let mock_api = MockApiContainer::new(&env.client);
mock_api.wait_for_ready().await;
// Test external API integration
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"ci",
"github",
"pages",
"status",
"--repo",
"test/repo",
"--api-base",
&mock_api.base_url,
]);
let assert = cmd.assert();
// Should handle API errors gracefully
assert.failure().stderr(predicate::str::contains("API integration validated"));
}
#[tokio::test]
async fn test_production_readiness_concurrent_operations() {
let env = TestEnvironment::new();
let postgres = PostgresTestContainer::new(&env.client);
let redis = RedisTestContainer::new(&env.client);
postgres.wait_for_ready().await;
redis.wait_for_ready().await;
// Test concurrent operations across multiple services
let mut handles = Vec::new();
// Spawn multiple concurrent operations
for i in 0..5 {
let postgres_url = postgres.connection_string.clone();
let redis_url = redis.connection_string.clone();
let handle = tokio::spawn(async move {
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"build",
"--config",
&format!("database_url={}&cache_url={}", postgres_url, redis_url),
]);
let assert = cmd.assert();
assert.success()
});
handles.push(handle);
}
// Wait for all operations to complete
for handle in handles {
handle.await.unwrap();
}
}
#[tokio::test]
async fn test_production_readiness_error_handling() {
let env = TestEnvironment::new();
// Test with invalid database connection
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"init",
"--config",
"database_url=postgresql://invalid:invalid@localhost:9999/invalid",
]);
let assert = cmd.assert();
assert.failure().stderr(predicate::str::contains("Connection failed"));
}
#[tokio::test]
async fn test_production_readiness_performance_validation() {
let env = TestEnvironment::new();
let postgres = PostgresTestContainer::new(&env.client);
postgres.wait_for_ready().await;
let start = std::time::Instant::now();
// Test performance under load
let mut handles = Vec::new();
for _ in 0..10 {
let postgres_url = postgres.connection_string.clone();
let handle = tokio::spawn(async move {
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"test",
"--config",
&format!("database_url={}", postgres_url),
]);
let assert = cmd.assert();
assert.success()
});
handles.push(handle);
}
// Wait for all operations to complete
for handle in handles {
handle.await.unwrap();
}
let duration = start.elapsed();
// Performance assertion: all operations should complete within 30 seconds
assert!(duration.as_secs() < 30, "Performance test failed: took {} seconds", duration.as_secs());
}
#[tokio::test]
async fn test_production_readiness_security_validation() {
let env = TestEnvironment::new();
// Test SQL injection protection
let malicious_input = "'; DROP TABLE users; --";
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"template",
"render",
"test.tmpl",
"--var",
&format!("user_input={}", malicious_input),
]);
let assert = cmd.assert();
// Should sanitize input and not execute malicious SQL
assert.success().stdout(predicate::str::contains("Input sanitized"));
}
#[tokio::test]
async fn test_production_readiness_resource_cleanup() {
let env = TestEnvironment::new();
let postgres = PostgresTestContainer::new(&env.client);
postgres.wait_for_ready().await;
// Test resource cleanup after operations
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"cleanup",
"--config",
&format!("database_url={}", postgres.connection_string),
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Resources cleaned up"));
}
#[tokio::test]
async fn test_production_readiness_monitoring_integration() {
let env = TestEnvironment::new();
// Test monitoring and observability features
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"monitor",
"--metrics",
"--tracing",
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Monitoring enabled"));
}
#[tokio::test]
async fn test_production_readiness_health_checks() {
let env = TestEnvironment::new();
let postgres = PostgresTestContainer::new(&env.client);
let redis = RedisTestContainer::new(&env.client);
postgres.wait_for_ready().await;
redis.wait_for_ready().await;
// Test health check endpoints
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"health",
"--config",
&format!("database_url={}&cache_url={}", postgres.connection_string, redis.connection_string),
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("All services healthy"));
}
#[tokio::test]
async fn test_production_readiness_backup_and_restore() {
let env = TestEnvironment::new();
let postgres = PostgresTestContainer::new(&env.client);
postgres.wait_for_ready().await;
// Test backup functionality
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"backup",
"--config",
&format!("database_url={}", postgres.connection_string),
"--output",
env.temp_path().join("backup.sql").to_str().unwrap(),
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Backup completed"));
// Test restore functionality
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"restore",
"--config",
&format!("database_url={}", postgres.connection_string),
"--input",
env.temp_path().join("backup.sql").to_str().unwrap(),
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Restore completed"));
}
#[tokio::test]
async fn test_production_readiness_disaster_recovery() {
let env = TestEnvironment::new();
let postgres = PostgresTestContainer::new(&env.client);
postgres.wait_for_ready().await;
// Simulate disaster recovery scenario
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"disaster-recovery",
"--config",
&format!("database_url={}", postgres.connection_string),
"--scenario",
"database-failure",
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Recovery completed"));
}
#[tokio::test]
async fn test_production_readiness_load_balancing() {
let env = TestEnvironment::new();
// Test load balancing across multiple instances
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"load-test",
"--instances",
"3",
"--requests",
"100",
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Load balancing validated"));
}
#[tokio::test]
async fn test_production_readiness_graceful_shutdown() {
let env = TestEnvironment::new();
// Test graceful shutdown handling
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"graceful-shutdown",
"--timeout",
"10",
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Graceful shutdown completed"));
}
#[tokio::test]
async fn test_production_readiness_configuration_validation() {
let env = TestEnvironment::new();
// Test configuration validation
let config_file = env.temp_path().join("config.toml");
std::fs::write(&config_file, r#"
[database]
url = "postgresql://test:test@localhost:5432/test"
max_connections = 10
[cache]
url = "redis://localhost:6379"
ttl = 3600
[monitoring]
enabled = true
metrics_port = 9090
"#).unwrap();
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"validate-config",
"--config",
config_file.to_str().unwrap(),
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Configuration valid"));
}
#[tokio::test]
async fn test_production_readiness_secrets_management() {
let env = TestEnvironment::new();
// Test secrets management
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"secrets-test",
"--secret",
"api_key",
"--secret",
"database_password",
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Secrets management validated"));
}
#[tokio::test]
async fn test_production_readiness_circuit_breaker() {
let env = TestEnvironment::new();
// Test circuit breaker pattern
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"circuit-breaker-test",
"--failure-threshold",
"3",
"--timeout",
"5",
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Circuit breaker validated"));
}
#[tokio::test]
async fn test_production_readiness_rate_limiting() {
let env = TestEnvironment::new();
// Test rate limiting
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"rate-limit-test",
"--requests-per-second",
"10",
"--duration",
"5",
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Rate limiting validated"));
}
#[tokio::test]
async fn test_production_readiness_data_consistency() {
let env = TestEnvironment::new();
let postgres = PostgresTestContainer::new(&env.client);
postgres.wait_for_ready().await;
// Test data consistency across operations
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"consistency-test",
"--config",
&format!("database_url={}", postgres.connection_string),
"--transactions",
"100",
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Data consistency validated"));
}
#[tokio::test]
async fn test_production_readiness_comprehensive_validation() {
let env = TestEnvironment::new();
let postgres = PostgresTestContainer::new(&env.client);
let redis = RedisTestContainer::new(&env.client);
let mock_api = MockApiContainer::new(&env.client);
postgres.wait_for_ready().await;
redis.wait_for_ready().await;
mock_api.wait_for_ready().await;
// Comprehensive production readiness validation
let mut cmd = Command::cargo_bin("ggen").unwrap();
cmd.args([
"lifecycle",
"run",
"production-readiness",
"--config",
&format!(
"database_url={}&cache_url={}&api_base_url={}",
postgres.connection_string,
redis.connection_string,
mock_api.base_url
),
"--validate-all",
]);
let assert = cmd.assert();
assert.success().stdout(predicate::str::contains("Production readiness validated"));
}