use std::process::Stdio; use tempfile::TempDir; use tokio::process::Command; fn witryna_bin() -> std::path::PathBuf { let mut path = std::path::PathBuf::from(env!("CARGO_BIN_EXE_witryna")); if !path.exists() { path = std::path::PathBuf::from("target/debug/witryna"); } path } async fn write_validate_config(dir: &std::path::Path, content: &str) -> std::path::PathBuf { let config_path = dir.join("witryna.toml"); tokio::fs::write(&config_path, content).await.unwrap(); config_path } // --------------------------------------------------------------------------- // Tier 1: no container runtime / git needed // --------------------------------------------------------------------------- #[tokio::test] async fn cli_validate_valid_config() { let tempdir = TempDir::new().unwrap(); let base_dir = tempdir.path().join("data"); tokio::fs::create_dir_all(&base_dir).await.unwrap(); let config = format!( r#"listen_address = "127.0.0.1:8080" container_runtime = "podman" base_dir = "{base_dir}" log_level = "info" [[sites]] name = "site-a" repo_url = "https://example.com/a.git" branch = "main" [[sites]] name = "site-b" repo_url = "https://example.com/b.git" branch = "main" "#, base_dir = base_dir.display(), ); let config_path = write_validate_config(tempdir.path(), &config).await; let output = Command::new(witryna_bin()) .args(["validate", "--config", config_path.to_str().unwrap()]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() .await .unwrap(); assert!( output.status.success(), "should exit 0, stderr: {}", String::from_utf8_lossy(&output.stderr) ); let stderr = String::from_utf8_lossy(&output.stderr); assert!( stderr.contains("Configuration valid:"), "should say 'Configuration valid:', got: {stderr}" ); assert!( stderr.contains("127.0.0.1:8080"), "should show listen address, got: {stderr}" ); assert!( stderr.contains("podman"), "should show runtime, got: {stderr}" ); assert!( stderr.contains("Sites: 2"), "should show site count, got: {stderr}" ); assert!( stderr.contains("site-a"), "should list site-a, got: {stderr}" ); assert!( stderr.contains("site-b"), "should list site-b, got: {stderr}" ); } #[tokio::test] async fn cli_validate_missing_config_file() { let output = Command::new(witryna_bin()) .args(["validate", "--config", "/nonexistent/witryna.toml"]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() .await .unwrap(); assert!(!output.status.success(), "should exit non-zero"); let stderr = String::from_utf8_lossy(&output.stderr); assert!( stderr.contains("config file not found"), "should mention 'config file not found', got: {stderr}" ); } #[tokio::test] async fn cli_validate_invalid_config() { let tempdir = TempDir::new().unwrap(); // listen_address = "" is invalid — config validation rejects it let config = r#"listen_address = "" container_runtime = "podman" base_dir = "/tmp/witryna" log_level = "info" [[sites]] name = "test" repo_url = "https://example.com/test.git" branch = "main" "#; let config_path = write_validate_config(tempdir.path(), config).await; let output = Command::new(witryna_bin()) .args(["validate", "--config", config_path.to_str().unwrap()]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() .await .unwrap(); assert!( !output.status.success(), "should exit non-zero for invalid config" ); }