use super::*;
#[test]
fn cover_move_renames_without_fetching() {
let mut manifest = Manifest::new();
let mut e = entry("a.mp3", AudioFormat::Mp3);
e.cover_jpg = Some(ArtifactState {
path: "old/cover.jpg".to_owned(),
hash: "h".to_owned(),
});
manifest.insert("a", e);
let fs = MemFs::new().with_file("old/cover.jpg", b"JPGBYTES".to_vec());
let plan = Plan {
actions: vec![Action::MoveArtifact {
kind: ArtifactKind::CoverJpg,
from: "old/cover.jpg".to_owned(),
to: "new/cover.jpg".to_owned(),
source_url: "https://art.suno.ai/a/large.jpg".to_owned(),
hash: "h".to_owned(),
owner_id: "a".to_owned(),
}],
};
let outcome = run(
&plan,
&mut manifest,
&[],
&ScriptedHttp::new(),
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.failed(), 0);
assert_eq!(outcome.renamed, 1, "counted as a rename, not a write");
assert_eq!(fs.read_file("new/cover.jpg").unwrap(), b"JPGBYTES");
assert!(!fs.exists("old/cover.jpg"));
assert_eq!(
manifest.get("a").unwrap().cover_jpg.as_ref().unwrap().path,
"new/cover.jpg"
);
}
#[test]
fn cover_move_falls_back_to_fetch_when_old_file_missing() {
let mut manifest = Manifest::new();
let mut e = entry("a.mp3", AudioFormat::Mp3);
e.cover_jpg = Some(ArtifactState {
path: "old/cover.jpg".to_owned(),
hash: "h".to_owned(),
});
manifest.insert("a", e);
let fs = MemFs::new(); let http = ScriptedHttp::new().route("a/large.jpg", Reply::ok(b"FETCHED".to_vec()));
let plan = Plan {
actions: vec![Action::MoveArtifact {
kind: ArtifactKind::CoverJpg,
from: "old/cover.jpg".to_owned(),
to: "new/cover.jpg".to_owned(),
source_url: "https://art.suno.ai/a/large.jpg".to_owned(),
hash: "h".to_owned(),
owner_id: "a".to_owned(),
}],
};
let outcome = run(
&plan,
&mut manifest,
&[],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.failed(), 0);
assert_eq!(fs.read_file("new/cover.jpg").unwrap(), b"FETCHED");
assert_eq!(
manifest.get("a").unwrap().cover_jpg.as_ref().unwrap().path,
"new/cover.jpg"
);
}
#[test]
fn cover_move_falls_back_when_source_co_referenced() {
let mut manifest = Manifest::new();
let mut a = entry("a.mp3", AudioFormat::Mp3);
a.cover_jpg = Some(ArtifactState {
path: "old/cover.jpg".to_owned(),
hash: "h".to_owned(),
});
manifest.insert("a", a);
let mut b = entry("b.mp3", AudioFormat::Mp3);
b.cover_jpg = Some(ArtifactState {
path: "old/cover.jpg".to_owned(),
hash: "h".to_owned(),
});
manifest.insert("b", b);
let fs = MemFs::new().with_file("old/cover.jpg", b"SHARED".to_vec());
let http = ScriptedHttp::new().route("a/large.jpg", Reply::ok(b"FETCHED-A".to_vec()));
let plan = Plan {
actions: vec![Action::MoveArtifact {
kind: ArtifactKind::CoverJpg,
from: "old/cover.jpg".to_owned(),
to: "a/cover.jpg".to_owned(),
source_url: "https://art.suno.ai/a/large.jpg".to_owned(),
hash: "h".to_owned(),
owner_id: "a".to_owned(),
}],
};
let outcome = run(
&plan,
&mut manifest,
&[],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.failed(), 0);
assert_eq!(fs.read_file("a/cover.jpg").unwrap(), b"FETCHED-A");
assert_eq!(
fs.read_file("old/cover.jpg").unwrap(),
b"SHARED",
"the co-referenced file must survive"
);
}
#[test]
fn stem_move_renames_without_refetch() {
let mut manifest = Manifest::new();
let mut e = entry("a.flac", AudioFormat::Flac);
e.stems.insert(
"voc".to_owned(),
ArtifactState {
path: "old.stems/voc.mp3".to_owned(),
hash: "h1".to_owned(),
},
);
manifest.insert("a", e);
let fs = MemFs::new().with_file("old.stems/voc.mp3", b"STEMBYTES".to_vec());
let plan = Plan {
actions: vec![Action::MoveStem {
clip_id: "a".to_owned(),
key: "voc".to_owned(),
stem_id: "voc".to_owned(),
from: "old.stems/voc.mp3".to_owned(),
to: "new.stems/voc.mp3".to_owned(),
source_url: "https://cdn1.suno.ai/voc.mp3".to_owned(),
format: StemFormat::Mp3,
hash: "h1".to_owned(),
}],
};
let outcome = run(
&plan,
&mut manifest,
&[],
&ScriptedHttp::new(),
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.failed(), 0);
assert_eq!(outcome.renamed, 1);
assert_eq!(fs.read_file("new.stems/voc.mp3").unwrap(), b"STEMBYTES");
assert!(!fs.exists("old.stems/voc.mp3"));
assert_eq!(
manifest.get("a").unwrap().stems.get("voc").unwrap().path,
"new.stems/voc.mp3"
);
}
#[test]
fn stem_move_falls_back_to_fetch_when_source_co_referenced() {
let mut manifest = Manifest::new();
let mut a = entry("a.flac", AudioFormat::Flac);
a.stems.insert(
"voc".to_owned(),
ArtifactState {
path: "shared.stems/voc.mp3".to_owned(),
hash: "h".to_owned(),
},
);
manifest.insert("a", a);
let mut b = entry("b.flac", AudioFormat::Flac);
b.stems.insert(
"voc".to_owned(),
ArtifactState {
path: "shared.stems/voc.mp3".to_owned(),
hash: "h".to_owned(),
},
);
manifest.insert("b", b);
let fs = MemFs::new().with_file("shared.stems/voc.mp3", b"A-STEM".to_vec());
let http = ScriptedHttp::new().route("bvoc.mp3", Reply::ok(b"B-STEM".to_vec()));
let plan = Plan {
actions: vec![Action::MoveStem {
clip_id: "b".to_owned(),
key: "voc".to_owned(),
stem_id: "bvoc".to_owned(),
from: "shared.stems/voc.mp3".to_owned(),
to: "b.stems/voc.mp3".to_owned(),
source_url: "https://cdn1.suno.ai/bvoc.mp3".to_owned(),
format: StemFormat::Mp3,
hash: "h".to_owned(),
}],
};
let outcome = run(
&plan,
&mut manifest,
&[],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.failed(), 0);
assert_eq!(fs.read_file("b.stems/voc.mp3").unwrap(), b"B-STEM");
assert_eq!(
fs.read_file("shared.stems/voc.mp3").unwrap(),
b"A-STEM",
"the co-referenced stem must survive"
);
}
#[test]
fn write_stem_keeps_shared_stem_when_co_referenced() {
let mut manifest = Manifest::new();
let mut a = entry("a.flac", AudioFormat::Flac);
a.stems.insert(
"voc".to_owned(),
ArtifactState {
path: "shared.stems/voc.mp3".to_owned(),
hash: "h".to_owned(),
},
);
manifest.insert("a", a);
let mut b = entry("b.flac", AudioFormat::Flac);
b.stems.insert(
"voc".to_owned(),
ArtifactState {
path: "shared.stems/voc.mp3".to_owned(),
hash: "h".to_owned(),
},
);
manifest.insert("b", b);
let fs = MemFs::new().with_file("shared.stems/voc.mp3", b"A-STEM".to_vec());
let http = ScriptedHttp::new().route("bvoc.mp3", Reply::ok(b"B-STEM".to_vec()));
let plan = Plan {
actions: vec![Action::WriteStem {
clip_id: "b".to_owned(),
key: "voc".to_owned(),
stem_id: "bvoc".to_owned(),
path: "b.stems/voc.mp3".to_owned(),
source_url: "https://cdn1.suno.ai/bvoc.mp3".to_owned(),
format: StemFormat::Mp3,
hash: "bh".to_owned(),
}],
};
let outcome = run(
&plan,
&mut manifest,
&[],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.failed(), 0);
assert_eq!(fs.read_file("b.stems/voc.mp3").unwrap(), b"B-STEM");
assert_eq!(
fs.read_file("shared.stems/voc.mp3").unwrap(),
b"A-STEM",
"the co-referenced stem must survive"
);
}
#[test]
fn co_delete_executes_audio_delete_then_artifact_delete() {
let fs = MemFs::new()
.with_file("gone.mp3", b"DATA".to_vec())
.with_file("gone/cover.jpg", b"jpg".to_vec());
let mut manifest = Manifest::new();
let mut e = entry("gone.mp3", AudioFormat::Mp3);
e.cover_jpg = Some(ArtifactState {
path: "gone/cover.jpg".to_owned(),
hash: "h1".to_owned(),
});
manifest.insert("gone", e);
let plan = Plan {
actions: vec![
Action::Delete {
path: "gone.mp3".to_owned(),
clip_id: "gone".to_owned(),
},
Action::DeleteArtifact {
kind: ArtifactKind::CoverJpg,
path: "gone/cover.jpg".to_owned(),
owner_id: "gone".to_owned(),
},
],
};
let outcome = run(
&plan,
&mut manifest,
&[],
&ScriptedHttp::new(),
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.deleted, 1);
assert_eq!(outcome.artifacts_deleted, 1);
assert_eq!(outcome.failed(), 0);
assert!(!fs.exists("gone.mp3"));
assert!(!fs.exists("gone/cover.jpg"));
assert!(manifest.get("gone").is_none());
}
#[test]
fn write_stem_mp3_stores_raw_and_records_slot() {
let mut manifest = Manifest::new();
manifest.insert("a", entry("a.flac", AudioFormat::Flac));
let plan = Plan {
actions: vec![Action::WriteStem {
clip_id: "a".to_owned(),
key: "voc".to_owned(),
stem_id: "voc".to_owned(),
path: "a.stems/a - Vocals [voc].mp3".to_owned(),
source_url: "https://cdn1.suno.ai/voc.mp3".to_owned(),
format: StemFormat::Mp3,
hash: "vh".to_owned(),
}],
};
let http = ScriptedHttp::new().route("voc.mp3", Reply::ok(b"stem-bytes".to_vec()));
let fs = MemFs::new();
let outcome = run(
&plan,
&mut manifest,
&[],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.artifacts_written, 1);
assert_eq!(outcome.failed(), 0);
assert_eq!(
fs.read_file("a.stems/a - Vocals [voc].mp3").unwrap(),
b"stem-bytes"
);
assert_eq!(http.count("convert_wav"), 0);
assert_eq!(http.count("/api/gen/"), 0);
assert_eq!(
manifest.get("a").unwrap().stems.get("voc"),
Some(&ArtifactState {
path: "a.stems/a - Vocals [voc].mp3".to_owned(),
hash: "vh".to_owned(),
})
);
}
#[test]
fn write_stem_wav_renders_via_convert_wav_and_stores_raw() {
let mut manifest = Manifest::new();
manifest.insert("a", entry("a.flac", AudioFormat::Flac));
let plan = Plan {
actions: vec![Action::WriteStem {
clip_id: "a".to_owned(),
key: "voc".to_owned(),
stem_id: "stemvoc".to_owned(),
path: "a.stems/a - Vocals [stemvoc].wav".to_owned(),
source_url: "https://cdn1.suno.ai/stemvoc.mp3".to_owned(),
format: StemFormat::Wav,
hash: "vh".to_owned(),
}],
};
let http = ScriptedHttp::new()
.with_auth()
.route_seq(
"stemvoc/wav_file/",
vec![
Reply::json("{}"),
Reply::json(r#"{"wav_file_url": "https://cdn1.suno.ai/stemvoc.wav"}"#),
],
)
.route("stemvoc/convert_wav/", Reply::status(200))
.route("stemvoc.wav", Reply::ok(b"RIFFwav-bytes".to_vec()));
let fs = MemFs::new();
let outcome = run(
&plan,
&mut manifest,
&[],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&small_poll(),
);
assert_eq!(outcome.artifacts_written, 1);
assert_eq!(outcome.failed(), 0);
assert_eq!(
fs.read_file("a.stems/a - Vocals [stemvoc].wav").unwrap(),
b"RIFFwav-bytes"
);
assert!(!fs.exists("a.stems/a - Vocals [stemvoc].flac"));
assert_eq!(http.count("convert_wav"), 1);
assert_eq!(http.count("stem_task"), 0);
assert_eq!(http.count("separate"), 0);
assert_eq!(
manifest.get("a").unwrap().stems.get("voc").unwrap().path,
"a.stems/a - Vocals [stemvoc].wav"
);
}
#[test]
fn write_stem_is_skipped_when_owner_audio_is_absent() {
let mut manifest = Manifest::new();
let plan = Plan {
actions: vec![Action::WriteStem {
clip_id: "ghost".to_owned(),
key: "voc".to_owned(),
stem_id: "voc".to_owned(),
path: "ghost.stems/voc.mp3".to_owned(),
source_url: "https://cdn1.suno.ai/voc.mp3".to_owned(),
format: StemFormat::Mp3,
hash: "vh".to_owned(),
}],
};
let http = ScriptedHttp::new();
let fs = MemFs::new();
let outcome = run(
&plan,
&mut manifest,
&[],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.skipped, 1);
assert_eq!(outcome.artifacts_written, 0);
assert_eq!(outcome.failed(), 0);
assert!(!fs.exists("ghost.stems/voc.mp3"));
}
#[test]
fn write_stem_relocates_the_old_file_on_a_path_move() {
let fs = MemFs::new().with_file("old.stems/voc.mp3", b"old".to_vec());
let mut manifest = Manifest::new();
let mut e = entry("new.flac", AudioFormat::Flac);
e.stems.insert(
"voc".to_owned(),
ArtifactState {
path: "old.stems/voc.mp3".to_owned(),
hash: "vh".to_owned(),
},
);
manifest.insert("a", e);
let plan = Plan {
actions: vec![Action::WriteStem {
clip_id: "a".to_owned(),
key: "voc".to_owned(),
stem_id: "voc".to_owned(),
path: "new.stems/voc.mp3".to_owned(),
source_url: "https://cdn1.suno.ai/voc.mp3".to_owned(),
format: StemFormat::Mp3,
hash: "vh".to_owned(),
}],
};
let http = ScriptedHttp::new().route("voc.mp3", Reply::ok(b"new".to_vec()));
let outcome = run(
&plan,
&mut manifest,
&[],
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.artifacts_written, 1);
assert!(fs.exists("new.stems/voc.mp3"));
assert!(
!fs.exists("old.stems/voc.mp3"),
"the old stem is moved, not left behind"
);
assert_eq!(
manifest.get("a").unwrap().stems.get("voc").unwrap().path,
"new.stems/voc.mp3"
);
}
#[test]
fn delete_stem_removes_file_and_clears_slot() {
let fs = MemFs::new().with_file("a.stems/voc.mp3", b"stem".to_vec());
let mut manifest = Manifest::new();
let mut e = entry("a.flac", AudioFormat::Flac);
e.stems.insert(
"voc".to_owned(),
ArtifactState {
path: "a.stems/voc.mp3".to_owned(),
hash: "vh".to_owned(),
},
);
manifest.insert("a", e);
let plan = Plan {
actions: vec![Action::DeleteStem {
clip_id: "a".to_owned(),
key: "voc".to_owned(),
path: "a.stems/voc.mp3".to_owned(),
}],
};
let outcome = run(
&plan,
&mut manifest,
&[],
&ScriptedHttp::new(),
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.artifacts_deleted, 1);
assert!(!fs.exists("a.stems/voc.mp3"));
assert!(manifest.get("a").unwrap().stems.is_empty());
}
#[test]
fn co_deleting_the_last_stem_prunes_the_stems_folder() {
let fs = MemFs::new()
.with_file("song.flac", b"DATA".to_vec())
.with_file("song.stems/voc.mp3", b"stem".to_vec());
assert!(fs.has_dir("song.stems"));
let mut manifest = Manifest::new();
let mut e = entry("song.flac", AudioFormat::Flac);
e.stems.insert(
"voc".to_owned(),
ArtifactState {
path: "song.stems/voc.mp3".to_owned(),
hash: "vh".to_owned(),
},
);
manifest.insert("a", e);
let plan = Plan {
actions: vec![
Action::Delete {
path: "song.flac".to_owned(),
clip_id: "a".to_owned(),
},
Action::DeleteStem {
clip_id: "a".to_owned(),
key: "voc".to_owned(),
path: "song.stems/voc.mp3".to_owned(),
},
],
};
let outcome = run(
&plan,
&mut manifest,
&[],
&ScriptedHttp::new(),
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.deleted, 1);
assert_eq!(outcome.artifacts_deleted, 1);
assert!(!fs.exists("song.flac"));
assert!(!fs.exists("song.stems/voc.mp3"));
assert!(
!fs.has_dir("song.stems"),
"the emptied .stems folder is pruned"
);
assert!(manifest.get("a").is_none());
}
#[test]
fn full_stems_mirror_mp3_is_get_only_with_zero_gen_traffic() {
let http = ScriptedHttp::new()
.with_auth()
.route("clip1/stems/pages", Reply::json(r#"{"pages": 1}"#))
.route(
"clip1/stems?page=0",
Reply::json(
r#"{"stems":[
{"id":"s1","title":"Song (Vocals)","status":"complete","audio_url":"https://cdn1.suno.ai/s1.mp3"},
{"id":"s2","title":"Song (Drums)","status":"complete","audio_url":"https://cdn1.suno.ai/s2.mp3"}
]}"#,
),
)
.route("s1.mp3", Reply::ok(b"vocals-bytes".to_vec()))
.route("s2.mp3", Reply::ok(b"drums-bytes".to_vec()));
let auth = ClerkAuth::new("eyJtoken");
pollster::block_on(auth.authenticate(&http)).unwrap();
let client = SunoClient::new(auth, RecordingClock::new());
let (stems, complete) = pollster::block_on(client.list_stems(&http, "clip1")).unwrap();
assert!(complete);
assert_eq!(stems.len(), 2);
assert_eq!(stems[0].label, "Vocals");
let mut manifest = Manifest::new();
manifest.insert("clip1", entry("clip1.flac", AudioFormat::Flac));
let desired_stems: Vec<crate::reconcile::DesiredStem> = stems
.iter()
.map(|s| crate::reconcile::DesiredStem {
key: s.id.clone(),
stem_id: s.id.clone(),
path: format!("clip1.stems/{}.mp3", s.id),
source_url: s.url.clone(),
format: StemFormat::Mp3,
hash: crate::art_url_hash(&s.url),
})
.collect();
let d = Desired {
path: "clip1.flac".to_owned(),
stems: Some(desired_stems),
..desired(clip("clip1"), AudioFormat::Flac)
};
let local: HashMap<String, crate::reconcile::LocalFile> = [(
"clip1".to_owned(),
crate::reconcile::LocalFile {
exists: true,
size: 100,
},
)]
.into_iter()
.collect();
let sources = [crate::reconcile::SourceStatus {
mode: SourceMode::Mirror,
fully_enumerated: true,
}];
let plan = crate::reconcile::reconcile(&manifest, std::slice::from_ref(&d), &local, &sources);
assert_eq!(plan.stem_writes(), 2);
let fs = MemFs::new();
let outcome = run(
&plan,
&mut manifest,
std::slice::from_ref(&d),
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&ExecOptions::default(),
);
assert_eq!(outcome.artifacts_written, 2, "both stems downloaded");
assert_eq!(fs.read_file("clip1.stems/s1.mp3").unwrap(), b"vocals-bytes");
assert_eq!(fs.read_file("clip1.stems/s2.mp3").unwrap(), b"drums-bytes");
assert_eq!(http.count("/api/gen/"), 0);
assert_eq!(http.count("stem_task"), 0);
assert_eq!(http.count("separate"), 0);
assert_eq!(http.count("generate"), 0);
assert!(!fs.exists("clip1.stems/s1.flac"));
}
#[test]
fn full_stems_mirror_wav_default_renders_free_wav_and_no_generation() {
let http = ScriptedHttp::new()
.with_auth()
.route("clip1/stems/pages", Reply::json(r#"{"pages": 1}"#))
.route(
"clip1/stems?page=0",
Reply::json(
r#"{"stems":[
{"id":"s1","title":"Song (Vocals)","status":"complete","audio_url":"https://cdn1.suno.ai/s1.mp3"},
{"id":"s2","title":"Song (Drums)","status":"complete","audio_url":"https://cdn1.suno.ai/s2.mp3"}
]}"#,
),
)
.route(
"s1/wav_file/",
Reply::json(r#"{"wav_file_url": "https://cdn1.suno.ai/s1.wav"}"#),
)
.route(
"s2/wav_file/",
Reply::json(r#"{"wav_file_url": "https://cdn1.suno.ai/s2.wav"}"#),
)
.route("s1.wav", Reply::ok(b"RIFFvocals".to_vec()))
.route("s2.wav", Reply::ok(b"RIFFdrums".to_vec()));
let auth = ClerkAuth::new("eyJtoken");
pollster::block_on(auth.authenticate(&http)).unwrap();
let client = SunoClient::new(auth, RecordingClock::new());
let (stems, _complete) = pollster::block_on(client.list_stems(&http, "clip1")).unwrap();
let mut manifest = Manifest::new();
manifest.insert("clip1", entry("clip1.flac", AudioFormat::Flac));
let desired_stems: Vec<crate::reconcile::DesiredStem> = stems
.iter()
.map(|s| crate::reconcile::DesiredStem {
key: s.id.clone(),
stem_id: s.id.clone(),
path: format!("clip1.stems/{}.wav", s.id),
source_url: s.url.clone(),
format: StemFormat::Wav,
hash: crate::art_url_hash(&s.url),
})
.collect();
let d = Desired {
path: "clip1.flac".to_owned(),
stems: Some(desired_stems),
..desired(clip("clip1"), AudioFormat::Flac)
};
let local: HashMap<String, crate::reconcile::LocalFile> = [(
"clip1".to_owned(),
crate::reconcile::LocalFile {
exists: true,
size: 100,
},
)]
.into_iter()
.collect();
let sources = [crate::reconcile::SourceStatus {
mode: SourceMode::Mirror,
fully_enumerated: true,
}];
let plan = crate::reconcile::reconcile(&manifest, std::slice::from_ref(&d), &local, &sources);
let fs = MemFs::new();
let outcome = run(
&plan,
&mut manifest,
std::slice::from_ref(&d),
&http,
&fs,
&StubFfmpeg::flac(),
&RecordingClock::new(),
&small_poll(),
);
assert_eq!(outcome.artifacts_written, 2);
assert_eq!(fs.read_file("clip1.stems/s1.wav").unwrap(), b"RIFFvocals");
assert_eq!(fs.read_file("clip1.stems/s2.wav").unwrap(), b"RIFFdrums");
assert!(!fs.exists("clip1.stems/s1.flac"));
assert_eq!(http.count("stem_task"), 0);
assert_eq!(http.count("separate"), 0);
assert_eq!(http.count("generate"), 0);
}