use super::*;
#[test]
fn animated_covers_defaults_off_and_follows_precedence() {
let cfg = Config::from_toml("[accounts.alice]\n").unwrap();
let eff = cfg.resolve("alice", None, &no_env(), &no_flags()).unwrap();
assert!(!eff.animated_covers);
let toml = r#"
[defaults]
animated_covers = true
[accounts.alice.sources.liked]
animated_covers = false
"#;
let cfg = Config::from_toml(toml).unwrap();
let eff = cfg.resolve("alice", None, &no_env(), &no_flags()).unwrap();
assert!(eff.animated_covers);
let eff = cfg
.resolve("alice", Some("liked"), &no_env(), &no_flags())
.unwrap();
assert!(!eff.animated_covers);
let env: HashMap<String, String> = [("SUNO_ANIMATED_COVERS".into(), "true".into())]
.into_iter()
.collect();
let eff = cfg
.resolve("alice", Some("liked"), &env, &no_flags())
.unwrap();
assert!(eff.animated_covers);
let flags = FlagOverrides {
settings: Settings {
animated_covers: Some(false),
..Default::default()
},
..Default::default()
};
let eff = cfg.resolve("alice", Some("liked"), &env, &flags).unwrap();
assert!(!eff.animated_covers);
}
#[test]
fn video_mp4_defaults_off_and_follows_precedence() {
let cfg = Config::from_toml("[accounts.alice]\n").unwrap();
let eff = cfg.resolve("alice", None, &no_env(), &no_flags()).unwrap();
assert!(!eff.video_mp4);
let toml = r#"
[defaults]
video_mp4 = true
[accounts.alice.sources.liked]
video_mp4 = false
"#;
let cfg = Config::from_toml(toml).unwrap();
assert!(
cfg.resolve("alice", None, &no_env(), &no_flags())
.unwrap()
.video_mp4
);
assert!(
!cfg.resolve("alice", Some("liked"), &no_env(), &no_flags())
.unwrap()
.video_mp4
);
let env: HashMap<String, String> = [("SUNO_VIDEO_MP4".into(), "true".into())]
.into_iter()
.collect();
assert!(
cfg.resolve("alice", Some("liked"), &env, &no_flags())
.unwrap()
.video_mp4
);
let flags = FlagOverrides {
settings: Settings {
video_mp4: Some(false),
..Default::default()
},
..Default::default()
};
assert!(
!cfg.resolve("alice", Some("liked"), &env, &flags)
.unwrap()
.video_mp4
);
}
#[test]
fn download_stems_defaults_off_and_follows_precedence() {
let cfg = Config::from_toml("[accounts.alice]\n").unwrap();
assert!(
!cfg.resolve("alice", None, &no_env(), &no_flags())
.unwrap()
.download_stems
);
let toml = r#"
[defaults]
download_stems = true
[accounts.alice.sources.liked]
download_stems = false
"#;
let cfg = Config::from_toml(toml).unwrap();
assert!(
cfg.resolve("alice", None, &no_env(), &no_flags())
.unwrap()
.download_stems
);
assert!(
!cfg.resolve("alice", Some("liked"), &no_env(), &no_flags())
.unwrap()
.download_stems
);
let env: HashMap<String, String> = [("SUNO_DOWNLOAD_STEMS".into(), "true".into())]
.into_iter()
.collect();
assert!(
cfg.resolve("alice", Some("liked"), &env, &no_flags())
.unwrap()
.download_stems
);
let flags = FlagOverrides {
settings: Settings {
download_stems: Some(false),
..Default::default()
},
..Default::default()
};
assert!(
!cfg.resolve("alice", Some("liked"), &env, &flags)
.unwrap()
.download_stems
);
}
#[test]
fn stem_format_defaults_to_wav_and_follows_precedence() {
let cfg = Config::from_toml("[accounts.alice]\n").unwrap();
assert_eq!(
cfg.resolve("alice", None, &no_env(), &no_flags())
.unwrap()
.stem_format,
StemFormat::Wav
);
let toml = r#"
[defaults]
stem_format = "mp3"
[accounts.alice.sources.liked]
stem_format = "wav"
"#;
let cfg = Config::from_toml(toml).unwrap();
assert_eq!(
cfg.resolve("alice", None, &no_env(), &no_flags())
.unwrap()
.stem_format,
StemFormat::Mp3
);
assert_eq!(
cfg.resolve("alice", Some("liked"), &no_env(), &no_flags())
.unwrap()
.stem_format,
StemFormat::Wav
);
let env: HashMap<String, String> = [("SUNO_STEM_FORMAT".into(), "mp3".into())]
.into_iter()
.collect();
assert_eq!(
cfg.resolve("alice", Some("liked"), &env, &no_flags())
.unwrap()
.stem_format,
StemFormat::Mp3
);
let flags = FlagOverrides {
settings: Settings {
stem_format: Some(StemFormat::Wav),
..Default::default()
},
..Default::default()
};
assert_eq!(
cfg.resolve("alice", Some("liked"), &env, &flags)
.unwrap()
.stem_format,
StemFormat::Wav
);
}
#[test]
fn stem_format_rejects_flac_and_unknown() {
assert!("flac".parse::<StemFormat>().is_err());
assert!("aac".parse::<StemFormat>().is_err());
assert_eq!("WAV".parse::<StemFormat>().unwrap(), StemFormat::Wav);
assert_eq!("Mp3".parse::<StemFormat>().unwrap(), StemFormat::Mp3);
assert!(Config::from_toml("[defaults]\nstem_format = \"flac\"\n").is_err());
}
#[test]
fn video_cover_retention_drives_cover_artifacts_not_the_music_video() {
let resolve = |retention: &str| {
let toml = format!("[accounts.alice]\nvideo_cover_retention = \"{retention}\"\n");
Config::from_toml(&toml)
.unwrap()
.resolve("alice", None, &no_env(), &no_flags())
.unwrap()
};
let neither = resolve("neither");
assert!(!neither.animated_covers && !neither.raw_animated_cover);
assert_eq!(neither.video_cover_retention, VideoCoverRetention::Neither);
let webp = resolve("webp");
assert!(webp.animated_covers && !webp.raw_animated_cover);
assert_eq!(webp.video_cover_retention, VideoCoverRetention::Webp);
let mp4 = resolve("mp4");
assert!(!mp4.animated_covers && mp4.raw_animated_cover);
assert!(!mp4.video_mp4);
assert_eq!(mp4.video_cover_retention, VideoCoverRetention::Mp4);
let both = resolve("both");
assert!(both.animated_covers && both.raw_animated_cover);
assert!(!both.video_mp4);
assert_eq!(both.video_cover_retention, VideoCoverRetention::Both);
}
#[test]
fn video_mp4_is_independent_of_cover_retention() {
let toml = "[accounts.alice]\nvideo_mp4 = true\nvideo_cover_retention = \"webp\"\n";
let cfg = Config::from_toml(toml).unwrap();
let eff = cfg.resolve("alice", None, &no_env(), &no_flags()).unwrap();
assert!(eff.video_mp4);
assert!(eff.animated_covers);
assert!(!eff.raw_animated_cover);
assert_eq!(eff.video_cover_retention, VideoCoverRetention::Webp);
}
#[test]
fn animated_cover_webp_knobs_follow_precedence_and_validate_ranges() {
let toml = r#"
[defaults]
animated_cover_quality = 80
animated_cover_max_fps = 20
animated_cover_max_width = 640
animated_cover_compression_level = 3
[accounts.alice.sources.liked]
animated_cover_quality = 75
"#;
let cfg = Config::from_toml(toml).unwrap();
let eff = cfg
.resolve("alice", Some("liked"), &no_env(), &no_flags())
.unwrap();
assert_eq!(eff.animated_cover_webp.quality, 75);
assert_eq!(eff.animated_cover_webp.max_fps, 20);
assert_eq!(eff.animated_cover_webp.max_width, Some(640));
assert_eq!(eff.animated_cover_webp.compression_level, 3);
let env: HashMap<String, String> = [("SUNO_ANIMATED_COVER_QUALITY".into(), "90".into())]
.into_iter()
.collect();
let eff = cfg
.resolve("alice", Some("liked"), &env, &no_flags())
.unwrap();
assert_eq!(eff.animated_cover_webp.quality, 90);
let flags = FlagOverrides {
settings: Settings {
animated_cover_quality: Some(95),
animated_cover_max_width: Some(512),
animated_cover_compression_level: Some(4),
..Default::default()
},
..Default::default()
};
let eff = cfg.resolve("alice", Some("liked"), &env, &flags).unwrap();
assert_eq!(eff.animated_cover_webp.quality, 95);
assert_eq!(eff.animated_cover_webp.max_width, Some(512));
assert_eq!(eff.animated_cover_webp.compression_level, 4);
let bad_env: HashMap<String, String> = [("SUNO_ANIMATED_COVER_QUALITY".into(), "101".into())]
.into_iter()
.collect();
assert!(cfg.resolve("alice", None, &bad_env, &no_flags()).is_err());
}
#[test]
fn video_cover_retention_parses_formats_and_reports_kept_artifacts() {
assert_eq!(
"NEITHER".parse::<VideoCoverRetention>().unwrap(),
VideoCoverRetention::Neither
);
assert_eq!(
"WebP".parse::<VideoCoverRetention>().unwrap(),
VideoCoverRetention::Webp
);
assert_eq!(
"mp4".parse::<VideoCoverRetention>().unwrap(),
VideoCoverRetention::Mp4
);
assert_eq!(
"Both".parse::<VideoCoverRetention>().unwrap(),
VideoCoverRetention::Both
);
assert!("mkv".parse::<VideoCoverRetention>().is_err());
for mode in [
VideoCoverRetention::Neither,
VideoCoverRetention::Webp,
VideoCoverRetention::Mp4,
VideoCoverRetention::Both,
] {
assert_eq!(
mode.to_string().parse::<VideoCoverRetention>().unwrap(),
mode
);
}
assert!(!VideoCoverRetention::Neither.keeps_webp());
assert!(!VideoCoverRetention::Neither.keeps_mp4());
assert!(VideoCoverRetention::Webp.keeps_webp());
assert!(!VideoCoverRetention::Webp.keeps_mp4());
assert!(!VideoCoverRetention::Mp4.keeps_webp());
assert!(VideoCoverRetention::Mp4.keeps_mp4());
assert!(VideoCoverRetention::Both.keeps_webp());
assert!(VideoCoverRetention::Both.keeps_mp4());
}
#[test]
fn video_cover_retention_resolves_from_env_and_rejects_unknown() {
let cfg = Config::from_toml("[accounts.alice]\n").unwrap();
let env: HashMap<String, String> = [("SUNO_VIDEO_COVER_RETENTION".into(), "both".into())]
.into_iter()
.collect();
let eff = cfg.resolve("alice", None, &env, &no_flags()).unwrap();
assert_eq!(eff.video_cover_retention, VideoCoverRetention::Both);
assert!(eff.animated_covers);
assert!(eff.raw_animated_cover);
let bad_env: HashMap<String, String> = [("SUNO_VIDEO_COVER_RETENTION".into(), "mkv".into())]
.into_iter()
.collect();
assert!(cfg.resolve("alice", None, &bad_env, &no_flags()).is_err());
}
#[test]
fn animated_cover_compression_level_enforces_zero_to_four() {
let cfg =
Config::from_toml("[defaults]\nanimated_cover_compression_level = 4\n[accounts.alice]\n")
.unwrap();
assert_eq!(
cfg.resolve("alice", None, &no_env(), &no_flags())
.unwrap()
.animated_cover_webp
.compression_level,
4
);
let cfg =
Config::from_toml("[defaults]\nanimated_cover_compression_level = 5\n[accounts.alice]\n")
.unwrap();
assert!(cfg.resolve("alice", None, &no_env(), &no_flags()).is_err());
let cfg = Config::from_toml("[accounts.alice]\n").unwrap();
let bad_env: HashMap<String, String> =
[("SUNO_ANIMATED_COVER_COMPRESSION_LEVEL".into(), "5".into())]
.into_iter()
.collect();
assert!(cfg.resolve("alice", None, &bad_env, &no_flags()).is_err());
let junk_env: HashMap<String, String> = [("SUNO_ANIMATED_COVER_MAX_FPS".into(), "abc".into())]
.into_iter()
.collect();
assert!(cfg.resolve("alice", None, &junk_env, &no_flags()).is_err());
}
#[test]
fn animated_cover_lossless_defaults_off_and_follows_precedence() {
let cfg = Config::from_toml("[accounts.alice]\n").unwrap();
let eff = cfg.resolve("alice", None, &no_env(), &no_flags()).unwrap();
assert_eq!(eff.animated_cover_webp.quality, 90);
assert_eq!(eff.animated_cover_webp.compression_level, 4);
assert!(!eff.animated_cover_webp.lossless);
let cfg = Config::from_toml("[defaults]\nanimated_cover_lossless = true\n[accounts.alice]\n")
.unwrap();
assert!(
cfg.resolve("alice", None, &no_env(), &no_flags())
.unwrap()
.animated_cover_webp
.lossless
);
let env: HashMap<String, String> = [("SUNO_ANIMATED_COVER_LOSSLESS".into(), "false".into())]
.into_iter()
.collect();
assert!(
!cfg.resolve("alice", None, &env, &no_flags())
.unwrap()
.animated_cover_webp
.lossless
);
let flags = FlagOverrides {
settings: Settings {
animated_cover_lossless: Some(true),
..Default::default()
},
..Default::default()
};
assert!(
cfg.resolve("alice", None, &env, &flags)
.unwrap()
.animated_cover_webp
.lossless
);
let bad_env: HashMap<String, String> =
[("SUNO_ANIMATED_COVER_LOSSLESS".into(), "maybe".into())]
.into_iter()
.collect();
assert!(cfg.resolve("alice", None, &bad_env, &no_flags()).is_err());
}
#[test]
fn animated_cover_max_width_defaults_to_bounded() {
let cfg = Config::from_toml("[accounts.alice]\n").unwrap();
assert_eq!(
cfg.resolve("alice", None, &no_env(), &no_flags())
.unwrap()
.animated_cover_webp
.max_width,
Some(640)
);
}
#[test]
fn text_sidecars_default_off_and_follow_precedence() {
let cfg = Config::from_toml("[accounts.alice]\n").unwrap();
let eff = cfg.resolve("alice", None, &no_env(), &no_flags()).unwrap();
assert!(!eff.details_sidecar);
assert!(!eff.lyrics_sidecar);
let toml = r#"
[defaults]
details_sidecar = true
[accounts.alice.sources.liked]
details_sidecar = false
"#;
let cfg = Config::from_toml(toml).unwrap();
let eff = cfg.resolve("alice", None, &no_env(), &no_flags()).unwrap();
assert!(eff.details_sidecar);
assert!(!eff.lyrics_sidecar);
let eff = cfg
.resolve("alice", Some("liked"), &no_env(), &no_flags())
.unwrap();
assert!(!eff.details_sidecar);
let env: HashMap<String, String> = [
("SUNO_DETAILS_SIDECAR".into(), "true".into()),
("SUNO_LYRICS_SIDECAR".into(), "true".into()),
]
.into_iter()
.collect();
let eff = cfg
.resolve("alice", Some("liked"), &env, &no_flags())
.unwrap();
assert!(eff.details_sidecar);
assert!(eff.lyrics_sidecar);
let flags = FlagOverrides {
settings: Settings {
lyrics_sidecar: Some(false),
..Default::default()
},
..Default::default()
};
let eff = cfg.resolve("alice", Some("liked"), &env, &flags).unwrap();
assert!(eff.details_sidecar);
assert!(!eff.lyrics_sidecar);
}