use serial_test::serial;
use std::io::Write;
use std::process::Command;
use tempfile::{NamedTempFile, TempDir};
#[test]
#[serial]
fn test_validate_config_command() {
let config_content = r#"
proxy:
listen_port: 5433
listen_host: "127.0.0.1"
max_connections: 1000
targets:
local:
host: "localhost"
port: 5432
connection_management:
health_check_interval_seconds: 30
health_check_timeout_seconds: 5
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(config_content.as_bytes()).unwrap();
let output = Command::new("cargo")
.args(&[
"run",
"--",
"--config",
temp_file.path().to_str().unwrap(),
"validate-config",
])
.output()
.expect("Failed to execute command");
assert!(output.status.success(), "Config validation should succeed");
}
#[test]
#[serial]
fn test_validate_invalid_config() {
let invalid_config = r#"
proxy:
listen_port: 0 # Invalid port
listen_host: "127.0.0.1"
targets: {} # Empty targets
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(invalid_config.as_bytes()).unwrap();
let output = Command::new("cargo")
.args(&[
"run",
"--",
"--config",
temp_file.path().to_str().unwrap(),
"validate-config",
])
.output()
.expect("Failed to execute command");
assert!(
!output.status.success(),
"Invalid config validation should fail"
);
}
#[test]
#[serial]
fn test_init_config_command() {
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("test_config.yaml");
let output = Command::new("cargo")
.args(&[
"run",
"--",
"init-config",
"--output",
config_path.to_str().unwrap(),
])
.output()
.expect("Failed to execute command");
assert!(
output.status.success(),
"Config initialization should succeed"
);
assert!(config_path.exists(), "Config file should be created");
let content = std::fs::read_to_string(&config_path).unwrap();
assert!(content.contains("proxy:"));
assert!(content.contains("targets:"));
assert!(content.contains("local:"));
}
#[test]
#[serial]
fn test_show_config_command() {
let config_content = r#"
proxy:
listen_port: 5433
listen_host: "127.0.0.1"
targets:
test:
host: "test.example.com"
port: 5432
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(config_content.as_bytes()).unwrap();
let output = Command::new("cargo")
.args(&[
"run",
"--",
"--config",
temp_file.path().to_str().unwrap(),
"show-config",
])
.output()
.expect("Failed to execute command");
assert!(output.status.success(), "Show config should succeed");
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(stdout.contains("test.example.com"));
assert!(stdout.contains("5433"));
}
#[test]
#[serial]
fn test_list_targets_command() {
let config_content = r#"
proxy:
listen_port: 5433
listen_host: "127.0.0.1"
targets:
local:
host: "localhost"
port: 5432
production:
host: "prod.example.com"
port: 5432
development:
host: "dev.example.com"
port: 5432
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(config_content.as_bytes()).unwrap();
let output = Command::new("cargo")
.args(&[
"run",
"--",
"--config",
temp_file.path().to_str().unwrap(),
"list-targets",
])
.output()
.expect("Failed to execute command");
assert!(output.status.success(), "List targets should succeed");
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(stdout.contains("local"));
assert!(stdout.contains("production"));
assert!(stdout.contains("development"));
}
#[test]
#[serial]
fn test_health_check_command() {
let config_content = r#"
proxy:
listen_port: 5433
listen_host: "127.0.0.1"
targets:
local:
host: "localhost"
port: 5432
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(config_content.as_bytes()).unwrap();
let output = Command::new("cargo")
.args(&[
"run",
"--",
"--config",
temp_file.path().to_str().unwrap(),
"health-check",
"--target",
"local",
])
.output()
.expect("Failed to execute command");
let stderr = String::from_utf8(output.stderr).unwrap();
assert!(stderr.contains("local") || output.status.success());
}
#[test]
#[serial]
fn test_cli_help() {
let output = Command::new("cargo")
.args(&["run", "--", "--help"])
.output()
.expect("Failed to execute command");
assert!(output.status.success(), "Help command should succeed");
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(stdout.contains("tcproxy"));
assert!(stdout.contains("start"));
assert!(stdout.contains("init-config"));
assert!(stdout.contains("validate-config"));
}
#[test]
#[serial]
fn test_version_command() {
let output = Command::new("cargo")
.args(&["run", "--", "--version"])
.output()
.expect("Failed to execute command");
assert!(output.status.success(), "Version command should succeed");
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(stdout.contains("0.1.0"));
}
#[test]
#[serial]
fn test_start_without_target() {
let output = Command::new("cargo")
.args(&["run", "--", "start"])
.output()
.expect("Failed to execute command");
assert!(!output.status.success(), "Start without target should fail");
}
#[test]
#[serial]
fn test_nonexistent_config_file() {
let output = Command::new("cargo")
.args(&[
"run",
"--",
"--config",
"/nonexistent/config.yaml",
"validate-config",
])
.output()
.expect("Failed to execute command");
if output.status.success() {
let stderr = String::from_utf8(output.stderr).unwrap();
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(
stderr.contains("No such file")
|| stderr.contains("not found")
|| stdout.contains("No such file")
|| stdout.contains("not found"),
"Should indicate file not found"
);
} else {
assert!(!output.status.success(), "Nonexistent config should fail");
}
}
#[test]
#[serial]
fn test_json_logging() {
let config_content = r#"
proxy:
listen_port: 5433
listen_host: "127.0.0.1"
targets:
local:
host: "localhost"
port: 5432
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(config_content.as_bytes()).unwrap();
let output = Command::new("cargo")
.args(&[
"run",
"--",
"--config",
temp_file.path().to_str().unwrap(),
"--json-logs",
"show-config",
])
.output()
.expect("Failed to execute command");
assert!(output.status.success(), "JSON logging should work");
}
#[test]
#[serial]
fn test_log_levels() {
let config_content = r#"
proxy:
listen_port: 5433
listen_host: "127.0.0.1"
targets:
local:
host: "localhost"
port: 5432
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(config_content.as_bytes()).unwrap();
let log_levels = ["trace", "debug", "info", "warn", "error"];
for level in log_levels {
let output = Command::new("cargo")
.args(&[
"run",
"--",
"--config",
temp_file.path().to_str().unwrap(),
"--log-level",
level,
"validate-config",
])
.output()
.expect("Failed to execute command");
assert!(output.status.success(), "Log level {} should work", level);
}
}
#[test]
#[serial]
fn test_invalid_log_level() {
let config_content = r#"
proxy:
listen_port: 5433
listen_host: "127.0.0.1"
targets:
local:
host: "localhost"
port: 5432
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(config_content.as_bytes()).unwrap();
let output = Command::new("cargo")
.args(&[
"run",
"--",
"--config",
temp_file.path().to_str().unwrap(),
"--log-level",
"invalid",
"validate-config",
])
.output()
.expect("Failed to execute command");
assert!(!output.status.success(), "Invalid log level should fail");
}