use crate::config::OracleConfig;
use std::io::Write;
use std::path::PathBuf;
use tempfile::NamedTempFile;
static ENV_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
fn lock_env() -> std::sync::MutexGuard<'static, ()> {
ENV_MUTEX.lock().unwrap_or_else(|e| e.into_inner())
}
#[test]
fn config_coverage_from_env_auto_fix_true_lowercase() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_PATTERNS");
std::env::remove_var("DECY_ORACLE_THRESHOLD");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_AUTO_FIX", "true");
let config = OracleConfig::from_env();
assert!(config.auto_fix, "auto_fix should be true for 'true'");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
}
#[test]
fn config_coverage_from_env_auto_fix_true_uppercase() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_PATTERNS");
std::env::remove_var("DECY_ORACLE_THRESHOLD");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_AUTO_FIX", "TRUE");
let config = OracleConfig::from_env();
assert!(config.auto_fix, "auto_fix should be true for 'TRUE'");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
}
#[test]
fn config_coverage_from_env_auto_fix_true_mixed_case() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_PATTERNS");
std::env::remove_var("DECY_ORACLE_THRESHOLD");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_AUTO_FIX", "True");
let config = OracleConfig::from_env();
assert!(config.auto_fix, "auto_fix should be true for 'True'");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
}
#[test]
fn config_coverage_from_env_auto_fix_false_explicit() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_PATTERNS");
std::env::remove_var("DECY_ORACLE_THRESHOLD");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_AUTO_FIX", "false");
let config = OracleConfig::from_env();
assert!(!config.auto_fix, "auto_fix should be false for 'false'");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
}
#[test]
fn config_coverage_from_env_auto_fix_arbitrary_string_is_false() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_PATTERNS");
std::env::remove_var("DECY_ORACLE_THRESHOLD");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_AUTO_FIX", "yes");
let config = OracleConfig::from_env();
assert!(!config.auto_fix, "'yes' != 'true', should be false");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
}
#[test]
fn config_coverage_from_env_auto_fix_empty_string_is_false() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_PATTERNS");
std::env::remove_var("DECY_ORACLE_THRESHOLD");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_AUTO_FIX", "");
let config = OracleConfig::from_env();
assert!(!config.auto_fix, "empty string != 'true', should be false");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
}
#[test]
fn config_coverage_from_env_all_vars_set() {
let _lock = lock_env();
std::env::set_var("DECY_ORACLE_PATTERNS", "/tmp/test_oracle.apr");
std::env::set_var("DECY_ORACLE_THRESHOLD", "0.95");
std::env::set_var("DECY_ORACLE_AUTO_FIX", "true");
let config = OracleConfig::from_env();
assert_eq!(config.patterns_path, PathBuf::from("/tmp/test_oracle.apr"));
assert!(
(config.confidence_threshold - 0.95_f32).abs() < 0.01,
"threshold: {}",
config.confidence_threshold
);
assert!(config.auto_fix);
assert_eq!(config.max_suggestions, 5);
assert_eq!(config.max_retries, 3);
std::env::remove_var("DECY_ORACLE_PATTERNS");
std::env::remove_var("DECY_ORACLE_THRESHOLD");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
}
#[test]
fn config_coverage_from_env_threshold_zero() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_PATTERNS");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_THRESHOLD", "0.0");
let config = OracleConfig::from_env();
assert!(
config.confidence_threshold.abs() < f32::EPSILON,
"threshold should be 0.0, got {}",
config.confidence_threshold
);
std::env::remove_var("DECY_ORACLE_THRESHOLD");
}
#[test]
fn config_coverage_from_env_threshold_one() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_PATTERNS");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_THRESHOLD", "1.0");
let config = OracleConfig::from_env();
assert!(
(config.confidence_threshold - 1.0_f32).abs() < f32::EPSILON,
"threshold should be 1.0, got {}",
config.confidence_threshold
);
std::env::remove_var("DECY_ORACLE_THRESHOLD");
}
#[test]
fn config_coverage_from_env_threshold_negative_parses() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_PATTERNS");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_THRESHOLD", "-0.5");
let config = OracleConfig::from_env();
assert!(
(config.confidence_threshold - (-0.5_f32)).abs() < f32::EPSILON,
"threshold should be -0.5, got {}",
config.confidence_threshold
);
std::env::remove_var("DECY_ORACLE_THRESHOLD");
}
#[test]
fn config_coverage_from_env_threshold_empty_string_uses_default() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_PATTERNS");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_THRESHOLD", "");
let config = OracleConfig::from_env();
assert!(
(config.confidence_threshold - 0.7_f32).abs() < f32::EPSILON,
"empty string should fallback to default 0.7, got {}",
config.confidence_threshold
);
std::env::remove_var("DECY_ORACLE_THRESHOLD");
}
#[test]
fn config_coverage_from_env_patterns_relative_path() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_THRESHOLD");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_PATTERNS", "relative/path.apr");
let config = OracleConfig::from_env();
assert_eq!(config.patterns_path, PathBuf::from("relative/path.apr"));
std::env::remove_var("DECY_ORACLE_PATTERNS");
}
#[test]
fn config_coverage_from_env_patterns_empty_string() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_THRESHOLD");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_PATTERNS", "");
let config = OracleConfig::from_env();
assert_eq!(config.patterns_path, PathBuf::from(""));
std::env::remove_var("DECY_ORACLE_PATTERNS");
}
#[test]
fn config_coverage_from_file_with_patterns_path() {
let mut file = NamedTempFile::new().unwrap();
writeln!(
file,
r#"
patterns_path = "/custom/oracle/patterns.apr"
confidence_threshold = 0.6
max_suggestions = 3
auto_fix = false
max_retries = 1
"#
)
.unwrap();
let config = OracleConfig::from_file(file.path()).unwrap();
assert_eq!(config.patterns_path, PathBuf::from("/custom/oracle/patterns.apr"));
assert!((config.confidence_threshold - 0.6_f32).abs() < f32::EPSILON);
assert_eq!(config.max_suggestions, 3);
assert!(!config.auto_fix);
assert_eq!(config.max_retries, 1);
}
#[test]
fn config_coverage_from_file_only_patterns_path() {
let mut file = NamedTempFile::new().unwrap();
writeln!(
file,
r#"
patterns_path = "/specific/path.apr"
"#
)
.unwrap();
let config = OracleConfig::from_file(file.path()).unwrap();
assert_eq!(config.patterns_path, PathBuf::from("/specific/path.apr"));
assert!((config.confidence_threshold - 0.7_f32).abs() < f32::EPSILON);
assert_eq!(config.max_suggestions, 5);
assert!(!config.auto_fix);
assert_eq!(config.max_retries, 3);
}
#[test]
fn config_coverage_from_file_only_auto_fix() {
let mut file = NamedTempFile::new().unwrap();
writeln!(file, "auto_fix = true").unwrap();
let config = OracleConfig::from_file(file.path()).unwrap();
assert!(config.auto_fix);
assert!((config.confidence_threshold - 0.7_f32).abs() < f32::EPSILON);
assert_eq!(config.max_suggestions, 5);
assert_eq!(config.max_retries, 3);
}
#[test]
fn config_coverage_from_file_only_max_retries() {
let mut file = NamedTempFile::new().unwrap();
writeln!(file, "max_retries = 100").unwrap();
let config = OracleConfig::from_file(file.path()).unwrap();
assert_eq!(config.max_retries, 100);
assert!((config.confidence_threshold - 0.7_f32).abs() < f32::EPSILON);
assert_eq!(config.max_suggestions, 5);
assert!(!config.auto_fix);
}
#[test]
fn config_coverage_from_file_only_max_suggestions() {
let mut file = NamedTempFile::new().unwrap();
writeln!(file, "max_suggestions = 0").unwrap();
let config = OracleConfig::from_file(file.path()).unwrap();
assert_eq!(config.max_suggestions, 0);
}
#[test]
fn config_coverage_from_file_invalid_field_type_returns_error() {
let mut file = NamedTempFile::new().unwrap();
writeln!(file, r#"confidence_threshold = "not_a_float""#).unwrap();
let result = OracleConfig::from_file(file.path());
assert!(result.is_err());
}
#[test]
fn config_coverage_from_file_unknown_field_ignored() {
let mut file = NamedTempFile::new().unwrap();
writeln!(
file,
r#"
confidence_threshold = 0.5
unknown_field = "ignored"
"#
)
.unwrap();
let result = OracleConfig::from_file(file.path());
if let Ok(config) = result {
assert!((config.confidence_threshold - 0.5_f32).abs() < f32::EPSILON);
}
}
#[test]
fn config_coverage_serde_json_round_trip() {
let config = OracleConfig {
patterns_path: PathBuf::from("/test/serde.apr"),
confidence_threshold: 0.42,
max_suggestions: 7,
auto_fix: true,
max_retries: 2,
};
let json = serde_json::to_string(&config).unwrap();
let deserialized: OracleConfig = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.patterns_path, config.patterns_path);
assert!((deserialized.confidence_threshold - config.confidence_threshold).abs() < f32::EPSILON);
assert_eq!(deserialized.max_suggestions, config.max_suggestions);
assert_eq!(deserialized.auto_fix, config.auto_fix);
assert_eq!(deserialized.max_retries, config.max_retries);
}
#[test]
fn config_coverage_serde_json_round_trip_defaults() {
let config = OracleConfig::default();
let json = serde_json::to_string(&config).unwrap();
let deserialized: OracleConfig = serde_json::from_str(&json).unwrap();
assert!((deserialized.confidence_threshold - 0.7_f32).abs() < f32::EPSILON);
assert_eq!(deserialized.max_suggestions, 5);
assert!(!deserialized.auto_fix);
assert_eq!(deserialized.max_retries, 3);
}
#[test]
fn config_coverage_toml_round_trip_all_fields() {
let config = OracleConfig {
patterns_path: PathBuf::from("/round/trip.apr"),
confidence_threshold: 0.33,
max_suggestions: 42,
auto_fix: false,
max_retries: 99,
};
let toml_str = toml::to_string(&config).unwrap();
let deserialized: OracleConfig = toml::from_str(&toml_str).unwrap();
assert_eq!(deserialized.patterns_path, config.patterns_path);
assert!((deserialized.confidence_threshold - config.confidence_threshold).abs() < 0.01);
assert_eq!(deserialized.max_suggestions, config.max_suggestions);
assert_eq!(deserialized.auto_fix, config.auto_fix);
assert_eq!(deserialized.max_retries, config.max_retries);
}
#[test]
fn config_coverage_serde_json_from_partial() {
let json = r#"{"confidence_threshold": 0.99}"#;
let config: OracleConfig = serde_json::from_str(json).unwrap();
assert!((config.confidence_threshold - 0.99_f32).abs() < 0.01);
assert_eq!(config.max_suggestions, 5);
assert!(!config.auto_fix);
assert_eq!(config.max_retries, 3);
}
#[test]
fn config_coverage_serde_json_from_empty_object() {
let json = "{}";
let config: OracleConfig = serde_json::from_str(json).unwrap();
assert!((config.confidence_threshold - 0.7_f32).abs() < f32::EPSILON);
assert_eq!(config.max_suggestions, 5);
assert!(!config.auto_fix);
assert_eq!(config.max_retries, 3);
}
#[test]
fn config_coverage_default_patterns_path_parent_is_decy_dir() {
let config = OracleConfig::default();
let parent = config.patterns_path.parent().unwrap();
assert!(
parent.to_string_lossy().ends_with(".decy"),
"parent dir should be .decy, got: {:?}",
parent
);
}
#[test]
fn config_coverage_default_confidence_threshold_in_range() {
let config = OracleConfig::default();
assert!(
config.confidence_threshold >= 0.0 && config.confidence_threshold <= 1.0,
"default threshold {} should be in [0.0, 1.0]",
config.confidence_threshold
);
}
#[test]
fn config_coverage_default_max_suggestions_nonzero() {
let config = OracleConfig::default();
assert!(config.max_suggestions > 0);
}
#[test]
fn config_coverage_default_max_retries_nonzero() {
let config = OracleConfig::default();
assert!(config.max_retries > 0);
}
#[test]
fn config_coverage_default_auto_fix_disabled() {
let config = OracleConfig::default();
assert!(!config.auto_fix);
}
#[test]
fn config_coverage_clone_preserves_all_fields() {
let config = OracleConfig {
patterns_path: PathBuf::from("/clone/test.apr"),
confidence_threshold: 0.123,
max_suggestions: 77,
auto_fix: true,
max_retries: 11,
};
let cloned = config.clone();
assert_eq!(cloned.patterns_path, PathBuf::from("/clone/test.apr"));
assert!((cloned.confidence_threshold - 0.123_f32).abs() < f32::EPSILON);
assert_eq!(cloned.max_suggestions, 77);
assert!(cloned.auto_fix);
assert_eq!(cloned.max_retries, 11);
}
#[test]
fn config_coverage_debug_contains_all_field_names() {
let config = OracleConfig::default();
let debug = format!("{:?}", config);
assert!(debug.contains("patterns_path"));
assert!(debug.contains("confidence_threshold"));
assert!(debug.contains("max_suggestions"));
assert!(debug.contains("auto_fix"));
assert!(debug.contains("max_retries"));
}
#[test]
fn config_coverage_debug_format_with_custom_values() {
let config = OracleConfig {
patterns_path: PathBuf::from("/debug/format.apr"),
confidence_threshold: 0.5,
max_suggestions: 1,
auto_fix: true,
max_retries: 0,
};
let debug = format!("{config:?}");
assert!(debug.contains("/debug/format.apr"));
assert!(debug.contains("true"));
}
#[test]
fn config_coverage_field_mutation() {
let mut config = OracleConfig::default();
config.patterns_path = PathBuf::from("/mutated.apr");
config.confidence_threshold = 0.1;
config.max_suggestions = 100;
config.auto_fix = true;
config.max_retries = 50;
assert_eq!(config.patterns_path, PathBuf::from("/mutated.apr"));
assert!((config.confidence_threshold - 0.1_f32).abs() < f32::EPSILON);
assert_eq!(config.max_suggestions, 100);
assert!(config.auto_fix);
assert_eq!(config.max_retries, 50);
}
#[test]
fn config_coverage_from_env_patterns_absolute_path() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_THRESHOLD");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_PATTERNS", "/absolute/path/to/patterns.apr");
let config = OracleConfig::from_env();
assert_eq!(config.patterns_path, PathBuf::from("/absolute/path/to/patterns.apr"));
assert!(config.patterns_path.is_absolute());
std::env::remove_var("DECY_ORACLE_PATTERNS");
}
#[test]
fn config_coverage_from_env_patterns_with_spaces() {
let _lock = lock_env();
std::env::remove_var("DECY_ORACLE_THRESHOLD");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
std::env::set_var("DECY_ORACLE_PATTERNS", "/path with spaces/patterns.apr");
let config = OracleConfig::from_env();
assert_eq!(config.patterns_path, PathBuf::from("/path with spaces/patterns.apr"));
std::env::remove_var("DECY_ORACLE_PATTERNS");
}
#[test]
fn config_coverage_from_file_whitespace_only_uses_defaults() {
let mut file = NamedTempFile::new().unwrap();
writeln!(file, " \n\n ").unwrap();
let config = OracleConfig::from_file(file.path()).unwrap();
assert!((config.confidence_threshold - 0.7_f32).abs() < f32::EPSILON);
assert_eq!(config.max_suggestions, 5);
}
#[test]
fn config_coverage_from_file_comments_only_uses_defaults() {
let mut file = NamedTempFile::new().unwrap();
writeln!(file, "# This is a comment\n# Another comment").unwrap();
let config = OracleConfig::from_file(file.path()).unwrap();
assert!((config.confidence_threshold - 0.7_f32).abs() < f32::EPSILON);
}
#[test]
fn config_coverage_from_file_all_fields_with_comments() {
let mut file = NamedTempFile::new().unwrap();
writeln!(
file,
r#"
# Oracle patterns location
patterns_path = "/commented/path.apr"
# Confidence threshold
confidence_threshold = 0.75
# Max suggestions
max_suggestions = 8
# Auto-fix
auto_fix = true
# Max retries
max_retries = 4
"#
)
.unwrap();
let config = OracleConfig::from_file(file.path()).unwrap();
assert_eq!(config.patterns_path, PathBuf::from("/commented/path.apr"));
assert!((config.confidence_threshold - 0.75_f32).abs() < f32::EPSILON);
assert_eq!(config.max_suggestions, 8);
assert!(config.auto_fix);
assert_eq!(config.max_retries, 4);
}
#[test]
fn config_coverage_from_file_extreme_values() {
let mut file = NamedTempFile::new().unwrap();
writeln!(
file,
r#"
confidence_threshold = 0.0
max_suggestions = 0
auto_fix = false
max_retries = 0
"#
)
.unwrap();
let config = OracleConfig::from_file(file.path()).unwrap();
assert!(config.confidence_threshold.abs() < f32::EPSILON);
assert_eq!(config.max_suggestions, 0);
assert!(!config.auto_fix);
assert_eq!(config.max_retries, 0);
}
#[test]
fn config_coverage_from_file_large_values() {
let mut file = NamedTempFile::new().unwrap();
writeln!(
file,
r#"
confidence_threshold = 1.0
max_suggestions = 999999
max_retries = 999999
"#
)
.unwrap();
let config = OracleConfig::from_file(file.path()).unwrap();
assert!((config.confidence_threshold - 1.0_f32).abs() < f32::EPSILON);
assert_eq!(config.max_suggestions, 999_999);
assert_eq!(config.max_retries, 999_999);
}
#[test]
fn config_coverage_env_overrides_are_independent_of_file() {
let _lock = lock_env();
std::env::set_var("DECY_ORACLE_PATTERNS", "/env/path.apr");
std::env::remove_var("DECY_ORACLE_THRESHOLD");
std::env::remove_var("DECY_ORACLE_AUTO_FIX");
let env_config = OracleConfig::from_env();
assert_eq!(env_config.patterns_path, PathBuf::from("/env/path.apr"));
let mut file = NamedTempFile::new().unwrap();
writeln!(file, r#"patterns_path = "/file/path.apr""#).unwrap();
let file_config = OracleConfig::from_file(file.path()).unwrap();
assert_eq!(file_config.patterns_path, PathBuf::from("/file/path.apr"));
std::env::remove_var("DECY_ORACLE_PATTERNS");
}
#[test]
fn config_coverage_from_file_invalid_toml_syntax() {
let mut file = NamedTempFile::new().unwrap();
writeln!(file, "[[[nested").unwrap();
let result = OracleConfig::from_file(file.path());
assert!(result.is_err());
}
#[test]
fn config_coverage_from_file_wrong_value_type_bool_as_string() {
let mut file = NamedTempFile::new().unwrap();
writeln!(file, r#"auto_fix = "yes""#).unwrap();
let result = OracleConfig::from_file(file.path());
assert!(result.is_err());
}
#[test]
fn config_coverage_from_file_wrong_value_type_int_as_string() {
let mut file = NamedTempFile::new().unwrap();
writeln!(file, r#"max_retries = "ten""#).unwrap();
let result = OracleConfig::from_file(file.path());
assert!(result.is_err());
}
#[test]
fn config_coverage_toml_serialization_default() {
let config = OracleConfig::default();
let toml_str = toml::to_string(&config).unwrap();
assert!(toml_str.contains("patterns_path"));
assert!(toml_str.contains("confidence_threshold"));
assert!(toml_str.contains("max_suggestions"));
assert!(toml_str.contains("auto_fix"));
assert!(toml_str.contains("max_retries"));
}
#[test]
fn config_coverage_toml_serialization_custom() {
let config = OracleConfig {
patterns_path: PathBuf::from("/ser/test.apr"),
confidence_threshold: 0.55,
max_suggestions: 3,
auto_fix: true,
max_retries: 7,
};
let toml_str = toml::to_string(&config).unwrap();
assert!(toml_str.contains("/ser/test.apr"));
assert!(toml_str.contains("auto_fix = true"));
assert!(toml_str.contains("max_suggestions = 3"));
assert!(toml_str.contains("max_retries = 7"));
}