use anyhow::Result;
use clap::Parser;
use std::time::Instant;
use torrust_tracker_deployer_dependency_installer::Dependency;
use tracing::{error, info};
use torrust_tracker_deployer_lib::bootstrap::logging::{LogFormat, LogOutput, LoggingBuilder};
use torrust_tracker_deployer_lib::testing::e2e::tasks::black_box::{
generate_environment_config, run_preflight_cleanup, verify_required_dependencies, E2eTestRunner,
};
const ENVIRONMENT_NAME: &str = "e2e-infrastructure";
#[derive(Parser)]
#[command(name = "e2e-infrastructure-lifecycle-tests")]
#[command(about = "E2E infrastructure lifecycle tests for Torrust Tracker Deployer")]
struct Cli {
#[arg(long)]
keep: bool,
#[arg(
long,
default_value = "pretty",
help = "Logging format: pretty, json, or compact"
)]
log_format: LogFormat,
}
fn main() -> Result<()> {
let cli = Cli::parse();
LoggingBuilder::new(std::path::Path::new("./data/e2e-infrastructure/logs"))
.with_format(cli.log_format.clone())
.with_output(LogOutput::FileAndStderr)
.init();
info!(
application = "torrust_tracker_deployer",
test_suite = "e2e_infrastructure_lifecycle_tests",
log_format = ?cli.log_format,
"Starting E2E provisioning and destruction tests (black-box)"
);
verify_required_dependencies(&[Dependency::Ansible])?;
run_preflight_cleanup(ENVIRONMENT_NAME)?;
let test_start = Instant::now();
let test_result = run_e2e_test_workflow(ENVIRONMENT_NAME, !cli.keep);
let test_duration = test_start.elapsed();
info!(
performance = "test_execution",
duration_secs = test_duration.as_secs_f64(),
duration = ?test_duration,
"Provisioning and destruction test execution completed"
);
match &test_result {
Ok(()) => {
info!(
test_suite = "e2e_infrastructure_lifecycle_tests",
status = "success",
"All provisioning and destruction tests passed successfully"
);
if cli.keep {
info!(
operation = "post_test_cleanup",
environment = ENVIRONMENT_NAME,
status = "skipped",
reason = "keep flag is set",
"Skipping post-test purge to preserve state for debugging"
);
} else {
let test_runner = E2eTestRunner::new(ENVIRONMENT_NAME);
if let Err(purge_error) = test_runner.purge_environment() {
tracing::warn!(
operation = "post_test_cleanup",
environment = ENVIRONMENT_NAME,
error = %purge_error,
"Failed to purge test state after successful run (test still passed)"
);
} else {
info!(
operation = "post_test_cleanup",
environment = ENVIRONMENT_NAME,
"Test state purged successfully"
);
}
}
}
Err(e) => {
error!(
test_suite = "e2e_infrastructure_lifecycle_tests",
status = "failed",
error = %e,
"E2E test failed"
);
}
}
test_result
}
fn run_e2e_test_workflow(environment_name: &str, destroy: bool) -> Result<()> {
let test_runner = E2eTestRunner::new(environment_name).with_cleanup_on_failure(destroy);
let config_path = generate_environment_config(environment_name)?;
test_runner.create_environment(&config_path)?;
test_runner.provision_infrastructure()?;
if destroy {
test_runner.destroy_infrastructure()?;
} else {
info!(
step = "destroy",
status = "skipped",
reason = "keep flag is set",
"Skipping infrastructure destruction"
);
}
Ok(())
}