use super::*;
#[test]
fn failed_write_leaves_the_prior_file_intact() {
let (_c, d, action) = download("f", AudioFormat::Mp3);
let plan = Plan {
actions: vec![action],
};
let http = ScriptedHttp::new().route("f.mp3", Reply::ok(b"new-body".to_vec()));
let fs = MemFs::new()
.with_file("f.mp3", b"OLD-CONTENT".to_vec())
.fail_write("f.mp3");
let mut manifest = Manifest::new();
let outcome = run(
&plan,
&mut manifest,
&[d],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.downloaded, 0);
assert_eq!(outcome.failed(), 1);
assert_eq!(fs.read_file("f.mp3").unwrap(), b"OLD-CONTENT");
assert!(manifest.get("f").is_none());
}
#[test]
fn size_mismatch_after_write_is_a_failure() {
let (_c, d, action) = download("g", AudioFormat::Mp3);
let plan = Plan {
actions: vec![action],
};
let http = ScriptedHttp::new().route("g.mp3", Reply::ok(b"body".to_vec()));
let fs = MemFs::new().corrupt_write("g.mp3");
let mut manifest = Manifest::new();
let outcome = run(
&plan,
&mut manifest,
&[d],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.downloaded, 0);
assert_eq!(outcome.failed(), 1);
assert!(outcome.failures[0].reason.contains("expected"));
assert!(manifest.get("g").is_none());
}
#[test]
fn transient_failure_is_retried_then_skipped() {
let (_c, d, action) = download("h", AudioFormat::Mp3);
let plan = Plan {
actions: vec![action],
};
let http = ScriptedHttp::new().route("h.mp3", Reply::status(500));
let fs = MemFs::new();
let clock = RecordingClock::new();
let opts = ExecOptions {
max_retries: 2,
..ExecOptions::default()
};
let mut manifest = Manifest::new();
let outcome = run(
&plan,
&mut manifest,
&[d],
&http,
&fs,
&StubFfmpeg::flac(),
&clock,
&opts,
);
assert_eq!(outcome.downloaded, 0);
assert_eq!(outcome.failed(), 1);
assert_eq!(http.count("h.mp3"), 3);
assert_eq!(clock.sleeps().len(), 2);
}
#[test]
fn truncated_download_is_retried_then_succeeds() {
let (_c, d, action) = download("i", AudioFormat::Mp3);
let plan = Plan {
actions: vec![action],
};
let http = ScriptedHttp::new().route_seq(
"i.mp3",
vec![
Reply::ok(b"short".to_vec()).with_content_length(999),
Reply::ok(b"good-body".to_vec()),
],
);
let fs = MemFs::new();
let clock = RecordingClock::new();
let mut manifest = Manifest::new();
let outcome = run(
&plan,
&mut manifest,
&[d],
&http,
&fs,
&StubFfmpeg::flac(),
&clock,
&ExecOptions::default(),
);
assert_eq!(outcome.downloaded, 1);
assert_eq!(http.count("i.mp3"), 2);
assert_eq!(clock.sleeps().len(), 1);
}
#[test]
fn rate_limit_backs_off_using_retry_after() {
let (_c, d, action) = download("j", AudioFormat::Mp3);
let plan = Plan {
actions: vec![action],
};
let http = ScriptedHttp::new().route_seq(
"j.mp3",
vec![
Reply::status(429).with_retry_after(7),
Reply::ok(b"body".to_vec()),
],
);
let fs = MemFs::new();
let clock = RecordingClock::new();
let mut manifest = Manifest::new();
let outcome = run(
&plan,
&mut manifest,
&[d],
&http,
&fs,
&StubFfmpeg::flac(),
&clock,
&ExecOptions::default(),
);
assert_eq!(outcome.downloaded, 1);
assert_eq!(clock.sleeps(), vec![Duration::from_secs(7)]);
}
#[test]
fn auth_failure_aborts_the_run() {
let (_c1, d1, a1) = download("k1", AudioFormat::Flac);
let (_c2, d2, a2) = download("k2", AudioFormat::Flac);
let plan = Plan {
actions: vec![a1, a2],
};
let http = ScriptedHttp::new()
.with_auth()
.route("/wav_file/", Reply::status(401));
let fs = MemFs::new();
let mut manifest = Manifest::new();
let outcome = run(
&plan,
&mut manifest,
&[d1, d2],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&small_poll(),
);
assert_eq!(outcome.status, RunStatus::AuthAborted);
assert_eq!(outcome.failed(), 1);
assert_eq!(outcome.failures[0].clip_id, "k1");
assert_eq!(outcome.downloaded, 0);
}
#[test]
fn disk_full_primary_write_aborts_the_run() {
let (_c1, d1, a1) = download("d1", AudioFormat::Mp3);
let (_c2, d2, a2) = download("d2", AudioFormat::Mp3);
let plan = Plan {
actions: vec![a1, a2],
};
let http = ScriptedHttp::new()
.route("d1.mp3", Reply::ok(b"body-1".to_vec()))
.route("d2.mp3", Reply::ok(b"body-2".to_vec()));
let fs = MemFs::new().fail_write_out_of_space("d1.mp3");
let mut manifest = Manifest::new();
let outcome = run(
&plan,
&mut manifest,
&[d1, d2],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.status, RunStatus::DiskFull);
assert_eq!(outcome.failed(), 1);
assert_eq!(outcome.failures[0].clip_id, "d1");
assert!(outcome.failures[0].reason.contains("disk full"));
assert_eq!(outcome.downloaded, 0);
assert_eq!(http.count("d2.mp3"), 0);
assert!(!fs.exists("d2.mp3"));
}
#[test]
fn disk_full_flac_transcode_aborts_the_run() {
let (_c1, d1, a1) = download("d1", AudioFormat::Flac);
let (_c2, d2, a2) = download("d2", AudioFormat::Flac);
let plan = Plan {
actions: vec![a1, a2],
};
let http = ScriptedHttp::new()
.with_auth()
.route(
"/wav_file/",
Reply::json(r#"{"wav_file_url": "https://cdn1.suno.ai/d1.wav"}"#),
)
.route(".wav", Reply::ok(b"wav".to_vec()));
let fs = MemFs::new();
let mut manifest = Manifest::new();
let outcome = run(
&plan,
&mut manifest,
&[d1, d2],
&http,
&fs,
&StubFfmpeg::out_of_space(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.status, RunStatus::DiskFull);
assert_eq!(outcome.failed(), 1);
assert_eq!(outcome.failures[0].clip_id, "d1");
assert!(outcome.failures[0].reason.contains("disk full"));
assert_eq!(outcome.downloaded, 0);
}
#[test]
fn disk_full_artifact_write_aborts_the_run() {
let mut manifest = Manifest::new();
manifest.insert("a", entry("a.mp3", AudioFormat::Mp3));
let plan = Plan {
actions: vec![Action::WriteArtifact {
kind: ArtifactKind::CoverJpg,
path: "a/cover.jpg".to_owned(),
source_url: "https://art.suno.ai/a/large.jpg".to_owned(),
hash: "h1".to_owned(),
owner_id: "a".to_owned(),
content: None,
}],
};
let http = ScriptedHttp::new().route("a/large.jpg", Reply::ok(b"jpg-bytes".to_vec()));
let fs = MemFs::new().fail_write_out_of_space("a/cover.jpg");
let outcome = run(
&plan,
&mut manifest,
&[],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.status, RunStatus::DiskFull);
assert_eq!(outcome.failed(), 1);
assert!(outcome.failures[0].reason.contains("disk full"));
assert_eq!(outcome.artifacts_written, 0);
assert_eq!(manifest.get("a").unwrap().cover_jpg, None);
}
#[test]
fn disk_full_leaves_the_failed_clips_manifest_entry_unchanged() {
let (_c, d, action) = download("m", AudioFormat::Mp3);
let plan = Plan {
actions: vec![action],
};
let http = ScriptedHttp::new().route("m.mp3", Reply::ok(b"new-body".to_vec()));
let fs = MemFs::new()
.with_file("m.mp3", b"OLD-CONTENT".to_vec())
.fail_write_out_of_space("m.mp3");
let mut manifest = Manifest::new();
let before = entry("m.mp3", AudioFormat::Mp3);
manifest.insert("m", before.clone());
let outcome = run(
&plan,
&mut manifest,
&[d],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.status, RunStatus::DiskFull);
assert_eq!(manifest.get("m"), Some(&before));
assert_eq!(fs.read_file("m.mp3").unwrap(), b"OLD-CONTENT");
}
#[test]
fn disk_full_unlink_aborts_the_run_before_a_later_delete() {
let r = clip("r");
let d = desired(r.clone(), AudioFormat::Mp3);
let plan = Plan {
actions: vec![
Action::Reformat {
clip: r.clone(),
path: "r.mp3".to_owned(),
from_path: "r.flac".to_owned(),
from: AudioFormat::Flac,
to: AudioFormat::Mp3,
},
Action::Delete {
path: "z.mp3".to_owned(),
clip_id: "z".to_owned(),
},
],
};
let http = ScriptedHttp::new().route("r.mp3", Reply::ok(b"reformatted".to_vec()));
let fs = MemFs::new()
.with_file("r.flac", b"OLD-FLAC".to_vec())
.with_file("z.mp3", b"ORPHAN".to_vec())
.fail_remove_out_of_space("r.flac");
let mut manifest = Manifest::new();
let r_before = entry("r.flac", AudioFormat::Flac);
manifest.insert("r", r_before.clone());
manifest.insert("z", entry("z.mp3", AudioFormat::Mp3));
let outcome = run(
&plan,
&mut manifest,
&[d],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.status, RunStatus::DiskFull);
assert_eq!(outcome.reformatted, 0);
assert_eq!(outcome.deleted, 0);
assert_eq!(outcome.failed(), 1);
assert_eq!(outcome.failures[0].clip_id, "r");
assert!(outcome.failures[0].reason.contains("disk full"));
assert!(fs.exists("r.flac"));
assert_eq!(manifest.get("r"), Some(&r_before));
assert!(fs.exists("z.mp3"));
assert!(manifest.get("z").is_some());
}
#[test]
fn disk_full_superseded_sidecar_unlink_aborts_the_run() {
let mut manifest = Manifest::new();
let mut e = entry("old/a.flac", AudioFormat::Flac);
e.lyrics_txt = Some(ArtifactState {
path: "old/a.lyrics.txt".to_owned(),
hash: "lh".to_owned(),
});
manifest.insert("a", e.clone());
let plan = Plan {
actions: vec![Action::WriteArtifact {
kind: ArtifactKind::LyricsTxt,
path: "new/a.lyrics.txt".to_owned(),
source_url: String::new(),
hash: "lh2".to_owned(),
owner_id: "a".to_owned(),
content: Some("new words\n".to_owned()),
}],
};
let fs = MemFs::new()
.with_file("old/a.flac", b"AUDIO".to_vec())
.with_file("old/a.lyrics.txt", b"old words\n".to_vec())
.fail_remove_out_of_space("old/a.lyrics.txt");
let outcome = run(
&plan,
&mut manifest,
&[],
&ScriptedHttp::new(),
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.status, RunStatus::DiskFull);
assert_eq!(outcome.failed(), 1);
assert_eq!(outcome.failures[0].clip_id, "a");
assert!(outcome.failures[0].reason.contains("disk full"));
assert_eq!(outcome.artifacts_written, 0);
assert_eq!(manifest.get("a"), Some(&e));
assert!(fs.exists("old/a.lyrics.txt"));
}
#[test]
fn cdn_download_rejection_skips_the_clip_without_aborting() {
let (_c1, d1, a1) = download("k1", AudioFormat::Mp3);
let (_c2, d2, a2) = download("k2", AudioFormat::Mp3);
let plan = Plan {
actions: vec![a1, a2],
};
let http = ScriptedHttp::new()
.route("k1.mp3", Reply::status(403))
.route("k2.mp3", Reply::ok(b"body".to_vec()));
let fs = MemFs::new();
let mut manifest = Manifest::new();
let outcome = run(
&plan,
&mut manifest,
&[d1, d2],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_ne!(outcome.status, RunStatus::AuthAborted);
assert_eq!(outcome.downloaded, 1);
assert_eq!(outcome.failed(), 1);
assert_eq!(outcome.failures[0].clip_id, "k1");
}
#[test]
fn one_clip_failure_does_not_abort_the_run() {
let (_c1, d1, a1) = download("l1", AudioFormat::Mp3);
let (_c2, d2, a2) = download("l2", AudioFormat::Mp3);
let plan = Plan {
actions: vec![a1, a2],
};
let http = ScriptedHttp::new()
.route("l1.mp3", Reply::status(404))
.route("l2.mp3", Reply::ok(b"body".to_vec()));
let fs = MemFs::new();
let mut manifest = Manifest::new();
let outcome = run(
&plan,
&mut manifest,
&[d1, d2],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.status, RunStatus::Completed);
assert_eq!(outcome.downloaded, 1);
assert_eq!(outcome.failed(), 1);
assert_eq!(outcome.failures[0].clip_id, "l1");
assert!(fs.exists("l2.mp3"));
assert!(manifest.get("l2").is_some());
assert!(manifest.get("l1").is_none());
}