use crate::prelude::*;
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TorrentResponse {
pub group: Group,
pub torrent: Torrent,
}
#[cfg(feature = "mock")]
impl TorrentResponse {
#[must_use]
pub fn mock() -> Self {
Self {
group: Group::mock(),
torrent: Torrent::mock(),
}
}
}
#[cfg(test)]
#[expect(
clippy::indexing_slicing,
reason = "test assertions on known fixture data"
)]
mod tests {
use super::*;
const OPS_RESPONSE: &str = include_str!("../tests/fixtures/torrent_response_ops.json");
const RED_RESPONSE: &str = include_str!("../tests/fixtures/torrent_response_red.json");
const MINIMAL_RESPONSE: &str = include_str!("../tests/fixtures/torrent_response_minimal.json");
const OPS_EMPTY_RELEASE_TYPE: &str =
include_str!("../tests/fixtures/torrent_response_ops_empty_release_type.json");
const RED_EBOOK: &str = include_str!("../tests/fixtures/torrent_response_red_ebook.json");
const RED_APP: &str = include_str!("../tests/fixtures/torrent_response_red_app.json");
const RED_DSD: &str = include_str!("../tests/fixtures/torrent_response_red_dsd.json");
const RED_DTS: &str = include_str!("../tests/fixtures/torrent_response_red_dts.json");
const OPS_AAC: &str = include_str!("../tests/fixtures/torrent_response_ops_aac.json");
const OPS_BD: &str = include_str!("../tests/fixtures/torrent_response_ops_bd.json");
const RED_BLURAY: &str = include_str!("../tests/fixtures/torrent_response_red_bluray.json");
const RED_V0: &str = include_str!("../tests/fixtures/torrent_response_red_v0.json");
const OPS_Q8: &str = include_str!("../tests/fixtures/torrent_response_ops_q8.json");
const RED_CASSETTE: &str = include_str!("../tests/fixtures/torrent_response_red_cassette.json");
#[test]
fn deserialize_ops_torrent_response() {
let response: TorrentResponse = json_from_str(OPS_RESPONSE).expect("should deserialize");
assert_eq!(response.torrent.trumpable, Some(false));
assert!(response.torrent.lossy_web_approved.is_none());
assert!(response.torrent.lossy_master_approved.is_none());
assert!(response.torrent.is_neutralleech.is_none());
assert!(response.torrent.is_freeload.is_none());
assert!(response.group.bb_body.is_none());
assert_eq!(response.group.id, 16352);
assert_eq!(response.group.name, "Test Album");
assert_eq!(response.torrent.id, 1_520_678);
assert_eq!(response.torrent.media, Media::WEB);
assert_eq!(response.torrent.format, Format::FLAC);
assert_eq!(response.torrent.encoding, Quality::Lossless);
assert_eq!(response.group.release_type, ReleaseType::Single);
}
#[test]
fn deserialize_red_torrent_response() {
let response: TorrentResponse = json_from_str(RED_RESPONSE).expect("should deserialize");
assert!(response.group.bb_body.is_some());
assert_eq!(response.torrent.trumpable, Some(false));
assert_eq!(response.torrent.lossy_web_approved, Some(false));
assert_eq!(response.torrent.lossy_master_approved, Some(false));
assert_eq!(response.torrent.is_neutralleech, Some(false));
assert_eq!(response.torrent.is_freeload, Some(false));
assert_eq!(response.torrent.has_snatched, Some(false));
assert_eq!(response.group.id, 8126);
assert_eq!(response.group.name, "Test Album");
assert_eq!(response.torrent.id, 12483);
assert_eq!(response.torrent.media, Media::WEB);
}
#[test]
fn deserialize_minimal_torrent_response() {
let response: TorrentResponse =
json_from_str(MINIMAL_RESPONSE).expect("should deserialize");
assert_eq!(response.group.id, 3);
assert_eq!(response.group.name, "Minimal Album");
assert!(response.group.tags.is_empty());
assert!(response.group.music_info.is_none());
assert_eq!(response.torrent.id, 3000);
assert_eq!(response.torrent.format, Format::MP3);
assert_eq!(response.torrent.remastered, Some(false));
assert!(response.torrent.scene);
}
#[test]
fn deserialize_torrent_music_info() {
let response: TorrentResponse = json_from_str(RED_RESPONSE).expect("should deserialize");
let music_info = response.group.music_info.expect("music_info should exist");
assert_eq!(music_info.artists.len(), 1);
assert_eq!(music_info.artists[0].name, "Test Artist");
}
#[test]
fn deserialize_torrent_numeric_fields() {
let response: TorrentResponse = json_from_str(RED_RESPONSE).expect("should deserialize");
assert_eq!(response.torrent.file_count, 2);
assert_eq!(response.torrent.size, 30_487_522);
assert_eq!(response.torrent.seeders, 15);
assert_eq!(response.torrent.leechers, 0);
assert_eq!(response.torrent.snatched, 55);
assert_eq!(response.group.year, 2015);
assert_eq!(response.torrent.remaster_year, Some(2015));
}
#[test]
fn deserialize_torrent_file_list() {
let response: TorrentResponse = json_from_str(OPS_RESPONSE).expect("should deserialize");
assert!(response.torrent.file_list.contains("Track.flac"));
let flacs = response.torrent.get_flacs();
assert_eq!(flacs.len(), 1);
}
#[test]
fn deserialize_ops_empty_release_type() {
let response: TorrentResponse =
json_from_str(OPS_EMPTY_RELEASE_TYPE).expect("should deserialize");
assert_eq!(response.group.id, 99001);
assert_eq!(response.group.name, "Mock Author - Mock Audiobook Title");
assert_eq!(response.group.category_id, Category::Audiobooks);
assert_eq!(response.group.category_name, "Audiobooks");
assert_eq!(response.torrent.id, 99002);
assert_eq!(response.torrent.format, Format::AAC);
assert_eq!(response.group.release_type, ReleaseType::NonMusic);
}
#[test]
fn deserialize_red_ebook() {
let response: TorrentResponse = json_from_str(RED_EBOOK).expect("should deserialize");
assert_eq!(response.group.id, 99101);
assert_eq!(response.group.category_id, Category::EBooks);
assert_eq!(response.group.category_name, "E-Books");
assert_eq!(response.group.release_type, ReleaseType::NonMusic);
assert_eq!(response.torrent.media, Media::Other(String::new()));
assert_eq!(response.torrent.format, Format::Other(String::new()));
assert_eq!(response.torrent.encoding, Quality::Other(String::new()));
}
#[test]
fn deserialize_red_app() {
let response: TorrentResponse = json_from_str(RED_APP).expect("should deserialize");
assert_eq!(response.group.id, 99201);
assert_eq!(response.group.category_id, Category::Applications);
assert_eq!(response.group.category_name, "Applications");
assert_eq!(response.group.release_type, ReleaseType::NonMusic);
assert_eq!(response.torrent.is_neutralleech, Some(true));
assert_eq!(response.torrent.media, Media::Other(String::new()));
assert_eq!(response.torrent.format, Format::Other(String::new()));
assert_eq!(response.torrent.encoding, Quality::Other(String::new()));
}
#[test]
fn deserialize_red_dsd() {
let response: TorrentResponse = json_from_str(RED_DSD).expect("should deserialize");
assert_eq!(response.group.id, 99301);
assert_eq!(response.group.category_id, Category::Music);
assert_eq!(response.group.release_type, ReleaseType::Album);
assert_eq!(response.torrent.media, Media::SACD);
assert_eq!(response.torrent.format, Format::DSD);
assert_eq!(response.torrent.encoding, Quality::DSD64);
}
#[test]
fn deserialize_red_dts() {
let response: TorrentResponse = json_from_str(RED_DTS).expect("should deserialize");
assert_eq!(response.group.id, 99401);
assert_eq!(response.torrent.media, Media::DVD);
assert_eq!(response.torrent.format, Format::DTS);
assert_eq!(response.torrent.encoding, Quality::Other("1510".to_owned()));
}
#[test]
fn deserialize_ops_aac() {
let response: TorrentResponse = json_from_str(OPS_AAC).expect("should deserialize");
assert_eq!(response.group.id, 99501);
assert_eq!(response.group.release_type, ReleaseType::Single);
assert_eq!(response.torrent.media, Media::WEB);
assert_eq!(response.torrent.format, Format::AAC);
assert_eq!(response.torrent.encoding, Quality::_256);
}
#[test]
fn deserialize_ops_bd() {
let response: TorrentResponse = json_from_str(OPS_BD).expect("should deserialize");
assert_eq!(response.group.id, 99601);
assert_eq!(response.group.release_type, ReleaseType::LiveAlbum);
assert_eq!(response.torrent.media, Media::BD);
assert_eq!(response.torrent.format, Format::AC3);
assert_eq!(
response.torrent.encoding,
Quality::Other("384 kbps".to_owned())
);
}
#[test]
fn deserialize_red_bluray() {
let response: TorrentResponse = json_from_str(RED_BLURAY).expect("should deserialize");
assert_eq!(response.group.id, 99701);
assert_eq!(response.torrent.media, Media::BluRay);
assert_eq!(response.torrent.format, Format::FLAC);
assert_eq!(response.torrent.encoding, Quality::Lossless24);
}
#[test]
fn deserialize_red_v0() {
let response: TorrentResponse = json_from_str(RED_V0).expect("should deserialize");
assert_eq!(response.group.id, 99801);
assert_eq!(response.torrent.media, Media::CD);
assert_eq!(response.torrent.format, Format::MP3);
assert_eq!(response.torrent.encoding, Quality::V0);
}
#[test]
fn deserialize_ops_q8() {
let response: TorrentResponse = json_from_str(OPS_Q8).expect("should deserialize");
assert_eq!(response.group.id, 99901);
assert_eq!(response.group.release_type, ReleaseType::Anthology);
assert_eq!(response.torrent.media, Media::CD);
assert_eq!(response.torrent.format, Format::MP3);
assert_eq!(response.torrent.encoding, Quality::Q8x);
}
#[test]
fn deserialize_red_cassette() {
let response: TorrentResponse = json_from_str(RED_CASSETTE).expect("should deserialize");
assert_eq!(response.group.id, 100_001);
assert_eq!(response.torrent.media, Media::Cassette);
assert_eq!(response.torrent.format, Format::AAC);
assert_eq!(response.torrent.encoding, Quality::_256);
}
#[test]
fn display_round_trip() {
let fixtures = [
OPS_RESPONSE,
RED_RESPONSE,
MINIMAL_RESPONSE,
OPS_EMPTY_RELEASE_TYPE,
RED_EBOOK,
RED_APP,
RED_DSD,
RED_DTS,
OPS_AAC,
OPS_BD,
RED_BLURAY,
RED_V0,
OPS_Q8,
RED_CASSETTE,
];
for fixture in fixtures {
let raw: JsonValue = json_from_str(fixture).expect("should deserialize as value");
let response: TorrentResponse =
json_from_str(fixture).expect("should deserialize as response");
let torrent = &raw["torrent"];
assert_eq!(
response.torrent.media.to_string(),
torrent["media"].as_str().expect("media should be a string"),
"media mismatch in fixture with torrent id {}",
torrent["id"]
);
assert_eq!(
response.torrent.format.to_string(),
torrent["format"]
.as_str()
.expect("format should be a string"),
"format mismatch in fixture with torrent id {}",
torrent["id"]
);
assert_eq!(
response.torrent.encoding.to_string(),
torrent["encoding"]
.as_str()
.expect("encoding should be a string"),
"encoding mismatch in fixture with torrent id {}",
torrent["id"]
);
}
}
}