#[cfg(test)]
mod cli_commands_tests {
use dm_database_sqllog2db::config::Config;
use std::fs;
use std::path::Path;
fn generate_config_content() -> String {
r#"[sqllog]
directory = "sqllogs"
[error]
file = "export/errors.log"
[logging]
file = "logs/sqllog2db.log"
level = "info"
retention_days = 7
[features.replace_parameters]
enable = false
[exporter.csv]
file = "outputs/sqllog.csv"
overwrite = true
append = false
"#
.to_string()
}
#[test]
fn test_init_command_generates_config() {
let test_dir = "target/test_outputs/cli_init";
let _ = fs::remove_dir_all(test_dir);
fs::create_dir_all(test_dir).ok();
let config_path = format!("{test_dir}/config.toml");
let content = generate_config_content();
let result = fs::write(&config_path, &content);
assert!(result.is_ok(), "Failed to write config file");
assert!(Path::new(&config_path).exists(), "Config file should exist");
let read_content = fs::read_to_string(&config_path).unwrap();
assert_eq!(read_content, content, "Config content should match");
let _ = fs::remove_dir_all(test_dir);
}
#[test]
fn test_init_command_file_exists_no_force() {
let test_dir = "target/test_outputs/cli_init_exists";
let _ = fs::remove_dir_all(test_dir);
fs::create_dir_all(test_dir).ok();
let config_path = format!("{test_dir}/config.toml");
let original_content = "original content";
fs::write(&config_path, original_content).unwrap();
let existing = Path::new(&config_path).exists();
assert!(existing, "File should exist before check");
let content = fs::read_to_string(&config_path).unwrap();
assert_eq!(
content, original_content,
"File should not be overwritten without force"
);
let _ = fs::remove_dir_all(test_dir);
}
#[test]
fn test_init_command_file_exists_with_force() {
let test_dir = "target/test_outputs/cli_init_force";
let _ = fs::remove_dir_all(test_dir);
fs::create_dir_all(test_dir).ok();
let config_path = format!("{test_dir}/config.toml");
let original_content = "original content";
let new_content = generate_config_content();
fs::write(&config_path, original_content).unwrap();
fs::write(&config_path, &new_content).unwrap();
let content = fs::read_to_string(&config_path).unwrap();
assert_eq!(
content, new_content,
"File should be overwritten with force"
);
let _ = fs::remove_dir_all(test_dir);
}
#[test]
fn test_init_command_creates_nested_dir() {
let test_dir = "target/test_outputs/cli_init_nested/deep/path";
let _ = fs::remove_dir_all("target/test_outputs/cli_init_nested");
fs::create_dir_all(test_dir).ok();
let config_path = format!("{test_dir}/config.toml");
let content = generate_config_content();
fs::write(&config_path, content).unwrap();
assert!(Path::new(&config_path).exists());
let _ = fs::remove_dir_all("target/test_outputs/cli_init_nested");
}
#[test]
fn test_init_config_format() {
let content = generate_config_content();
assert!(
content.contains("[sqllog]"),
"Should contain [sqllog] section"
);
assert!(
content.contains("[error]"),
"Should contain [error] section"
);
assert!(
content.contains("[logging]"),
"Should contain [logging] section"
);
assert!(
content.contains("[features.replace_parameters]"),
"Should contain [features.replace_parameters] section"
);
assert!(
content.contains("[exporter.csv]"),
"Should contain [exporter.csv] section"
);
}
#[test]
fn test_init_generated_config_is_loadable() {
let test_dir = "target/test_outputs/cli_init_loadable";
let _ = fs::remove_dir_all(test_dir);
fs::create_dir_all(test_dir).ok();
let config_path = format!("{test_dir}/config.toml");
let content = generate_config_content();
fs::write(&config_path, content).unwrap();
let config = Config::from_file(&config_path);
assert!(config.is_ok(), "Generated config should be loadable");
let _ = fs::remove_dir_all(test_dir);
}
#[test]
fn test_validate_command_valid_config() {
let config = Config::default();
assert!(config.validate().is_ok(), "Default config should be valid");
}
#[test]
fn test_validate_command_invalid_log_level() {
use dm_database_sqllog2db::config::LoggingConfig;
let config = LoggingConfig {
file: "app.log".to_string(),
level: "INVALID_LEVEL".to_string(),
retention_days: 7,
};
assert!(
config.validate().is_err(),
"Invalid log level should fail validation"
);
}
#[test]
fn test_validate_command_invalid_retention_days() {
use dm_database_sqllog2db::config::LoggingConfig;
let config = LoggingConfig {
file: "app.log".to_string(),
level: "info".to_string(),
retention_days: 0,
};
assert!(config.validate().is_err(), "Retention days 0 should fail");
let config = LoggingConfig {
file: "app.log".to_string(),
level: "info".to_string(),
retention_days: 366,
};
assert!(
config.validate().is_err(),
"Retention days > 365 should fail"
);
}
#[test]
fn test_validate_command_no_exporters() {
use dm_database_sqllog2db::config::{
ErrorConfig, ExporterConfig, FeaturesConfig, LoggingConfig, SqllogConfig,
};
let config = Config {
sqllog: SqllogConfig {
directory: "sqllogs".to_string(),
},
error: ErrorConfig {
file: "errors.log".to_string(),
},
logging: LoggingConfig {
file: "app.log".to_string(),
level: "info".to_string(),
retention_days: 7,
},
features: FeaturesConfig::default(),
exporter: ExporterConfig {
#[cfg(feature = "csv")]
csv: None,
#[cfg(feature = "parquet")]
parquet: None,
#[cfg(feature = "jsonl")]
jsonl: None,
#[cfg(feature = "sqlite")]
sqlite: None,
#[cfg(feature = "duckdb")]
duckdb: None,
#[cfg(feature = "postgres")]
postgres: None,
#[cfg(feature = "dm")]
dm: None,
},
};
assert!(
config.validate().is_err(),
"Config without exporters should fail"
);
}
#[test]
fn test_validate_command_empty_sqllog_dir() {
use dm_database_sqllog2db::config::SqllogConfig;
let config = SqllogConfig {
directory: String::new(),
};
assert!(
config.validate().is_err(),
"Empty sqllog directory should fail"
);
}
#[test]
fn test_validate_command_all_log_levels() {
use dm_database_sqllog2db::config::LoggingConfig;
let levels = vec!["trace", "debug", "info", "warn", "error"];
for level in levels {
let config = LoggingConfig {
file: "app.log".to_string(),
level: level.to_string(),
retention_days: 7,
};
assert!(
config.validate().is_ok(),
"Log level '{level}' should be valid"
);
}
}
#[test]
fn test_init_config_completeness() {
let content = generate_config_content();
let _config_result = toml::from_str::<Config>(&content);
assert!(
content.contains("directory"),
"Should contain directory field"
);
assert!(content.contains("level"), "Should contain level field");
assert!(
content.contains("retention_days"),
"Should contain retention_days field"
);
assert!(content.contains("file"), "Should contain file field");
}
#[test]
fn test_validate_config_path_handling() {
let paths = vec![
"config.toml",
"./config.toml",
"path/to/config.toml",
"path/to/my.config.toml",
];
for path in paths {
assert!(!path.is_empty());
assert!(
path.contains(".toml"),
"Config path should reference toml file"
);
}
}
#[test]
fn test_init_and_validate_commands_interaction() {
let test_dir = "target/test_outputs/cli_init_validate";
let _ = fs::remove_dir_all(test_dir);
fs::create_dir_all(test_dir).ok();
let config_path = format!("{test_dir}/config.toml");
let content = generate_config_content();
fs::write(&config_path, &content).unwrap();
assert!(Path::new(&config_path).exists());
let config = Config::from_file(&config_path);
assert!(config.is_ok());
assert!(config.unwrap().validate().is_ok());
let _ = fs::remove_dir_all(test_dir);
}
#[test]
fn test_init_output_path_options() {
let test_dir = "target/test_outputs/cli_init_paths";
let _ = fs::remove_dir_all(test_dir);
fs::create_dir_all(test_dir).ok();
let paths = vec![
format!("{}/config.toml", test_dir),
format!("{}/my_config.toml", test_dir),
format!("{}/config.prod.toml", test_dir),
];
for path in paths {
let content = generate_config_content();
fs::write(&path, content).ok();
assert!(Path::new(&path).exists());
}
let _ = fs::remove_dir_all(test_dir);
}
}