use std::time::Duration;
use super::harness::{
ClipSpec, clean_mirror, desired_set, fast_opts, path_of, probe_local, run_clean, run_sync,
world,
};
use crate::auth::ClerkAuth;
use crate::client::SunoClient;
use crate::executor::{ExecOptions, ExecOutcome, Ports, execute};
use crate::manifest::Manifest;
use crate::reconcile::{Desired, Plan, reconcile};
use crate::testutil::{ChaosHttp, MemFs, Outcome, RecordingClock, StubFfmpeg};
use crate::vocab::AudioFormat;
fn mp3_origin(spec: &ClipSpec, audio: Vec<Outcome>) -> ChaosHttp {
let mut http = ChaosHttp::new().program(&format!("/{}.mp3", spec.id), audio);
if !spec.art.is_empty() {
http = http.serve(&spec.art, format!("cover-{}", spec.id).into_bytes());
}
http
}
fn drive_capturing(
plan: &Plan,
manifest: &mut Manifest,
desired: &[Desired],
http: &ChaosHttp,
fs: &MemFs,
opts: &ExecOptions,
) -> (ExecOutcome, Vec<Duration>) {
let client = SunoClient::new(ClerkAuth::new("eyJtoken"), RecordingClock::new());
let clock = RecordingClock::new();
let ffmpeg = StubFfmpeg::flac();
let mut albums = std::collections::BTreeMap::new();
let mut playlists = std::collections::BTreeMap::new();
let synced = std::collections::HashMap::new();
let outcome = pollster::block_on(execute(
plan,
manifest,
&mut albums,
&mut playlists,
desired,
&synced,
Ports {
client: &client,
http,
fs,
ffmpeg: &ffmpeg,
clock: &clock,
},
opts,
));
(outcome, clock.sleeps())
}
fn sync_capturing(
specs: &[ClipSpec],
fs: &MemFs,
manifest: &mut Manifest,
http: &ChaosHttp,
) -> (Plan, ExecOutcome, Vec<Duration>) {
let desired = desired_set(specs);
let local = probe_local(manifest, fs);
let plan = reconcile(manifest, &desired, &local, &clean_mirror());
let (outcome, sleeps) = drive_capturing(&plan, manifest, &desired, http, fs, &fast_opts());
(plan, outcome, sleeps)
}
#[test]
fn a_permanent_download_failure_never_advances_the_manifest() {
let spec = ClipSpec::mirror("c100", "Lost Signal");
let fs = MemFs::new();
let mut manifest = Manifest::new();
let http = mp3_origin(&spec, vec![Outcome::status(404)]);
let (_plan, outcome) = run_sync(
std::slice::from_ref(&spec),
&clean_mirror(),
&fs,
&mut manifest,
&http,
&fast_opts(),
);
assert_eq!(outcome.downloaded, 0);
assert_eq!(outcome.failed(), 1);
assert!(
manifest.get("c100").is_none(),
"manifest must not record a failed download"
);
assert!(
!fs.exists(&path_of(&spec)),
"no partial file must be left behind"
);
}
#[test]
fn a_truncated_download_is_rejected_and_never_advances_the_manifest() {
let spec = ClipSpec::mirror("c101", "Half A Song");
let fs = MemFs::new();
let mut manifest = Manifest::new();
let http = mp3_origin(&spec, vec![Outcome::truncated(b"short".to_vec(), 9_999)]);
let (_plan, outcome) = run_sync(
std::slice::from_ref(&spec),
&clean_mirror(),
&fs,
&mut manifest,
&http,
&fast_opts(),
);
assert_eq!(outcome.downloaded, 0);
assert_eq!(outcome.failed(), 1);
assert!(
manifest.get("c101").is_none(),
"a truncated download must not be recorded"
);
assert!(
!fs.exists(&path_of(&spec)),
"a truncated body must never be written"
);
}
#[test]
fn a_failed_write_leaves_the_existing_good_file_untouched() {
let mut spec = ClipSpec::mirror("c102", "Steady");
let fs = MemFs::new();
let mut manifest = Manifest::new();
run_clean(std::slice::from_ref(&spec), &fs, &mut manifest);
let path = path_of(&spec);
let good_bytes = fs.read_file(&path).expect("first sync wrote the file");
let good_hash = manifest.get("c102").unwrap().meta_hash.clone();
spec = spec.with_tags("a brand new mood");
fs.arm_fail_write(&path);
let (plan, outcome) = run_clean(std::slice::from_ref(&spec), &fs, &mut manifest);
assert_eq!(
plan.retags(),
1,
"the change should still be planned as a retag"
);
assert_eq!(outcome.retagged, 0);
assert_eq!(outcome.failed(), 1);
assert_eq!(
fs.read_file(&path).unwrap(),
good_bytes,
"the good file must be byte-for-byte intact"
);
assert_eq!(
&manifest.get("c102").unwrap().meta_hash,
&good_hash,
"a failed retag must not advance the stored hash",
);
fs.disarm_fail_write(&path);
let (_plan, outcome) = run_clean(std::slice::from_ref(&spec), &fs, &mut manifest);
assert_eq!(
outcome.retagged, 1,
"the retag recovers once the disk works"
);
assert_ne!(
fs.read_file(&path).unwrap(),
good_bytes,
"the recovered run re-tags the file"
);
}
#[test]
fn a_corrupt_retag_write_over_an_existing_good_file_is_caught_and_never_trusted() {
let mut spec = ClipSpec::mirror("c114", "Tag Over Rot");
let fs = MemFs::new();
let mut manifest = Manifest::new();
run_clean(std::slice::from_ref(&spec), &fs, &mut manifest);
let path = path_of(&spec);
let good_bytes = fs.read_file(&path).expect("first sync wrote the file");
let good_entry = manifest.get("c114").unwrap().clone();
spec = spec.with_tags("a brand new mood");
fs.arm_corrupt_write(&path);
let (plan, outcome) = run_clean(std::slice::from_ref(&spec), &fs, &mut manifest);
assert_eq!(plan.retags(), 1, "the change is planned as a retag");
assert_eq!(
outcome.retagged, 0,
"a corrupt retag must not count as done"
);
assert_eq!(outcome.failed(), 1, "the size check must record a failure");
let after = manifest.get("c114").unwrap();
assert_eq!(
after.meta_hash, good_entry.meta_hash,
"a failed retag must not advance the stored meta hash"
);
assert_eq!(
after.size, good_entry.size,
"a failed retag must not advance the stored size"
);
let on_disk = fs.read_file(&path).unwrap();
assert_ne!(
on_disk, good_bytes,
"current behaviour: the in-place write replaces the good file before the size check"
);
assert!(
!good_bytes.iter().all(|&b| b == 0),
"sanity: the original good audio is not all zeros"
);
assert!(
on_disk.iter().all(|&b| b == 0),
"the destination now holds the corrupt body, not the verified original"
);
}
#[test]
fn a_transient_download_error_backs_off_then_recovers() {
let spec = ClipSpec::mirror("c400", "Flaky CDN");
let fs = MemFs::new();
let mut manifest = Manifest::new();
let http = mp3_origin(
&spec,
vec![
Outcome::transport("connection reset"),
Outcome::status(500),
Outcome::ok(b"recovered-audio".to_vec()),
],
);
let (_plan, outcome, sleeps) =
sync_capturing(std::slice::from_ref(&spec), &fs, &mut manifest, &http);
assert_eq!(
outcome.downloaded, 1,
"the download recovers after transient errors"
);
assert_eq!(outcome.failed(), 0);
assert!(fs.exists(&path_of(&spec)));
assert_eq!(
sleeps,
vec![Duration::from_secs(1), Duration::from_secs(2)],
"each retry must back off with doubling delays",
);
}
#[test]
fn a_rate_limit_is_transient_and_recovers() {
let spec = ClipSpec::mirror("c401", "Throttled");
let fs = MemFs::new();
let mut manifest = Manifest::new();
let http = mp3_origin(
&spec,
vec![Outcome::status(429), Outcome::ok(b"audio".to_vec())],
);
let (_plan, outcome, sleeps) =
sync_capturing(std::slice::from_ref(&spec), &fs, &mut manifest, &http);
assert_eq!(outcome.downloaded, 1, "a 429 is retried, not fatal");
assert_eq!(outcome.failed(), 0);
assert_eq!(
sleeps,
vec![Duration::from_secs(1)],
"one backoff before the retry"
);
}
#[test]
fn a_failed_reformat_write_keeps_the_old_file_and_format() {
let mut spec = ClipSpec::mirror("c500", "Reshape");
let fs = MemFs::new();
let mut manifest = Manifest::new();
run_clean(std::slice::from_ref(&spec), &fs, &mut manifest);
let old_path = path_of(&spec);
let old_bytes = fs.read_file(&old_path).expect("first sync wrote the mp3");
spec = spec.with_format(AudioFormat::Flac);
let new_path = path_of(&spec);
fs.arm_fail_write(&new_path);
let http = world(std::slice::from_ref(&spec));
let (plan, outcome) = run_sync(
std::slice::from_ref(&spec),
&clean_mirror(),
&fs,
&mut manifest,
&http,
&fast_opts(),
);
assert_eq!(plan.reformats(), 1);
assert_eq!(outcome.reformatted, 0);
assert_eq!(outcome.failed(), 1);
assert!(
fs.exists(&old_path),
"the old file must survive a failed reformat"
);
assert_eq!(
fs.read_file(&old_path).unwrap(),
old_bytes,
"the old file is untouched"
);
assert!(
!fs.exists(&new_path),
"no new file must be left after a failed write"
);
let entry = manifest.get("c500").expect("the entry survives");
assert_eq!(
entry.format,
AudioFormat::Mp3,
"the manifest still tracks the old format"
);
assert_eq!(
entry.path, old_path,
"the manifest still points at the old file"
);
}
#[test]
fn a_failed_reformat_remove_keeps_the_manifest_on_the_intact_old_file() {
let mut spec = ClipSpec::mirror("c501", "Lingering");
let fs = MemFs::new();
let mut manifest = Manifest::new();
run_clean(std::slice::from_ref(&spec), &fs, &mut manifest);
let old_path = path_of(&spec);
let old_bytes = fs.read_file(&old_path).expect("first sync wrote the mp3");
spec = spec.with_format(AudioFormat::Flac);
let new_path = path_of(&spec);
fs.arm_fail_remove(&old_path);
let http = world(std::slice::from_ref(&spec));
let (_plan, outcome) = run_sync(
std::slice::from_ref(&spec),
&clean_mirror(),
&fs,
&mut manifest,
&http,
&fast_opts(),
);
assert_eq!(outcome.reformatted, 0);
assert_eq!(outcome.failed(), 1);
let entry = manifest.get("c501").expect("the entry survives");
assert_eq!(
entry.format,
AudioFormat::Mp3,
"the manifest stays on the old format"
);
assert_eq!(entry.path, old_path);
assert!(fs.exists(&old_path), "the old, tracked file is intact");
assert_eq!(fs.read_file(&old_path).unwrap(), old_bytes);
fs.disarm_fail_remove(&old_path);
let (_plan, outcome) = run_clean(std::slice::from_ref(&spec), &fs, &mut manifest);
assert_eq!(
outcome.reformatted, 1,
"the reformat completes once the remove works"
);
assert!(!fs.exists(&old_path), "the old file is finally gone");
assert!(fs.exists(&new_path), "the new FLAC is in place");
assert_eq!(manifest.get("c501").unwrap().format, AudioFormat::Flac);
}