use super::*;
use std::fs;
use tempfile::tempdir;
#[test]
fn test_config_default() {
let config = Config::default();
assert_eq!(config.transition.fps, 30);
assert_eq!(config.transition.duration, 5);
assert_eq!(config.timer.interval, 30);
assert_eq!(config.timer.randomize, "5m");
assert_eq!(config.sources.default, "wallhaven");
assert_eq!(config.cleanup.keep_count, 10);
assert!(config.cleanup.auto_cleanup);
assert_eq!(config.advanced.parallel_downloads, 0); assert_eq!(config.advanced.retry_attempts, 0);
assert_eq!(config.advanced.timeout, 0);
assert!(!config.integration.reload_apps); assert_eq!(config.sources.local.formats, Vec::<String>::new()); }
#[test]
fn test_config_serialization() {
let config = Config::default();
let yaml = serde_yaml::to_string(&config).expect("Failed to serialize config");
assert!(!yaml.is_empty());
let deserialized: Config = serde_yaml::from_str(&yaml).expect("Failed to deserialize config");
assert_eq!(deserialized.timer.interval, config.timer.interval);
assert_eq!(deserialized.sources.default, config.sources.default);
assert_eq!(deserialized.cleanup.keep_count, config.cleanup.keep_count);
}
#[test]
fn test_config_load_from_file() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let config_path = temp_dir.path().join("test_config.yml");
let yaml_content = r#"
paths:
local: /home/user/wallpapers
downloads: /home/user/downloads
transition:
type: fade
duration: 10
fps: 60
timer:
interval: 60
randomize: "10m"
sources:
default: local
cleanup:
keep_count: 5
auto_cleanup: false
integration:
reload_apps: false
logging:
enabled: false
level: debug
"#;
fs::write(&config_path, yaml_content).expect("Failed to write test config");
let config = Config::load(&config_path).expect("Failed to load config");
assert_eq!(config.paths.local, "/home/user/wallpapers");
assert_eq!(config.paths.downloads, "/home/user/downloads");
assert_eq!(config.timer.interval, 60);
assert_eq!(config.sources.default, "local");
assert_eq!(config.cleanup.keep_count, 5);
assert!(!config.cleanup.auto_cleanup);
assert!(!config.logging.enabled);
assert_eq!(config.logging.level, "debug");
}
#[test]
fn test_config_load_missing_file() {
let path = PathBuf::from("/nonexistent/config.yml");
let result = Config::load(&path);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Failed to read config file"));
}
#[test]
fn test_config_load_invalid_yaml() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let config_path = temp_dir.path().join("invalid_config.yml");
let invalid_yaml = r#"
paths:
local: /home/user
downloads: [invalid: structure
"#;
fs::write(&config_path, invalid_yaml).expect("Failed to write invalid config");
let result = Config::load(&config_path);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Failed to parse YAML config"));
}
#[test]
fn test_config_load_or_default_existing() {
let config = Config::load_or_default().expect("Failed to load config");
assert!(!config.sources.default.is_empty());
}
#[test]
fn test_transition_type_variants() {
let single_yaml = r#"
paths:
local: /test
downloads: /test
transition:
type: fade
duration: 5
timer:
interval: 30
randomize: "5m"
sources:
default: local
category: nature
cleanup:
keep_count: 10
integration:
reload_apps: true
logging:
enabled: true
level: info
"#;
let config: Config = serde_yaml::from_str(single_yaml).expect("Failed to parse single transition");
match config.transition.transition_type {
TransitionType::Single(ref t) => assert_eq!(t, "fade"),
_ => panic!("Expected Single transition type"),
}
let multiple_yaml = r#"
paths:
local: /test
downloads: /test
transition:
type: [fade, slide, random]
duration: 5
timer:
interval: 30
randomize: "5m"
sources:
default: local
category: nature
cleanup:
keep_count: 10
integration:
reload_apps: true
logging:
enabled: true
level: info
"#;
let config: Config = serde_yaml::from_str(multiple_yaml).expect("Failed to parse multiple transitions");
match config.transition.transition_type {
TransitionType::Multiple(ref types) => {
assert_eq!(types.len(), 3);
assert!(types.contains(&"fade".to_string()));
assert!(types.contains(&"slide".to_string()));
assert!(types.contains(&"random".to_string()));
}
_ => panic!("Expected Multiple transition type"),
}
}
#[test]
fn test_wallhaven_config_defaults() {
let config = WallhavenConfig::default();
assert!(config.resolution.is_none());
assert_eq!(config.q, "");
let minimal_yaml = r#"{}"#;
let config: WallhavenConfig = serde_yaml::from_str(minimal_yaml).expect("Failed to parse minimal wallhaven config");
assert!(config.resolution.is_none());
assert_eq!(config.q, "large");
}
#[test]
fn test_picsum_config_defaults() {
let config = PicsumConfig::default();
assert!(config.width.is_none());
assert!(config.height.is_none());
}
#[test]
fn test_local_config_defaults() {
let config = LocalConfig::default();
assert!(!config.recursive); assert!(config.formats.is_empty());
let minimal_yaml = r#"{}"#;
let config: LocalConfig = serde_yaml::from_str(minimal_yaml).expect("Failed to parse minimal local config");
assert!(config.recursive);
assert_eq!(config.formats, vec!["jpg", "jpeg", "png", "webp"]);
}
#[test]
fn test_advanced_config_defaults() {
let config = AdvancedConfig::default();
assert_eq!(config.parallel_downloads, 0);
assert_eq!(config.retry_attempts, 0);
assert_eq!(config.timeout, 0);
let minimal_yaml = r#"{}"#;
let config: AdvancedConfig = serde_yaml::from_str(minimal_yaml).expect("Failed to parse minimal advanced config");
assert_eq!(config.parallel_downloads, 3);
assert_eq!(config.retry_attempts, 3);
assert_eq!(config.timeout, 30);
}
#[test]
fn test_logging_config_defaults() {
let config = LoggingConfig::default();
assert!(!config.enabled); assert_eq!(config.level, ""); assert!(config.file.is_none());
assert!(!config.timestamp);
let minimal_yaml = r#"{}"#;
let config: LoggingConfig = serde_yaml::from_str(minimal_yaml).expect("Failed to parse minimal logging config");
assert!(config.enabled);
assert_eq!(config.level, "info");
assert!(config.file.is_none());
assert!(config.timestamp);
}
#[test]
fn test_expand_paths() {
let mut config = Config::default();
config.paths.local = "~/Pictures".to_string();
config.paths.downloads = "$HOME/Downloads".to_string();
let result = config.expand_paths();
assert!(result.is_ok());
assert!(!config.paths.local.contains('~'));
assert!(!config.paths.downloads.contains('$'));
}
#[test]
fn test_expand_paths_invalid() {
let mut config = Config::default();
config.paths.local = "${NONEXISTENT_VAR}/path".to_string();
let result = config.expand_paths();
let _result = result;
}
#[test]
fn test_config_serde_attributes() {
let yaml = r#"
paths:
local: /test
downloads: /test
transition:
type: fade
duration: 5
timer:
interval: 30
randomize: "5m"
sources:
default: local
category: nature
cleanup:
keep_count: 10
integration:
reload_apps: true
logging:
enabled: true
level: info
"#;
let config: Config = serde_yaml::from_str(yaml).expect("Failed to parse config with renamed field");
match config.transition.transition_type {
TransitionType::Single(t) => assert_eq!(t, "fade"),
_ => panic!("Expected single transition type"),
}
}
#[test]
fn test_config_optional_fields() {
let minimal_yaml = r#"
paths:
local: /test
downloads: /test
transition:
type: fade
duration: 5
timer:
interval: 30
randomize: "5m"
sources:
default: local
category: nature
local: {}
cleanup:
keep_count: 10
integration:
reload_apps: true
logging:
enabled: true
level: info
"#;
let config: Config = serde_yaml::from_str(minimal_yaml).expect("Failed to parse minimal config");
assert_eq!(config.transition.fps, 30); assert!(config.sources.local.recursive); assert_eq!(config.sources.local.formats, vec!["jpg", "jpeg", "png", "webp"]); assert!(config.logging.enabled); assert_eq!(config.logging.level, "info"); }
#[cfg(test)]
mod property_tests {
use super::*;
use quickcheck::TestResult;
use quickcheck_macros::quickcheck;
#[quickcheck]
fn config_roundtrip_property(interval: u32, duration: u32, keep_count: u32) -> TestResult {
if interval == 0 || duration == 0 || keep_count == 0 {
return TestResult::discard();
}
let mut config = Config::default();
config.timer.interval = interval % 3600; config.transition.duration = duration % 60; config.cleanup.keep_count = keep_count % 1000;
let yaml = match serde_yaml::to_string(&config) {
Ok(y) => y,
Err(_) => return TestResult::failed(),
};
let deserialized: Config = match serde_yaml::from_str(&yaml) {
Ok(c) => c,
Err(_) => return TestResult::failed(),
};
TestResult::from_bool(
deserialized.timer.interval == config.timer.interval
&& deserialized.transition.duration == config.transition.duration
&& deserialized.cleanup.keep_count == config.cleanup.keep_count,
)
}
}