use super::*; #[test] fn catalog_and_actual_episode_numbers_remain_distinct() { let media = serde_json::json!({"id":"opaque_ID", "title":{"english":"The Apothecary Diaries", "romaji":"Kusuriya no Hitorigoto"}, "episode_count":12, "episode_counts":{"sub":null}, "episode_duration_minutes":null}); let rows = catalog(vec![media.clone()]).unwrap(); assert_eq!(rows[0].id, "opaque_ID"); assert!(rows[0].metadata["episode_counts"]["sub"].is_null()); assert!(catalog(vec![media.clone(), media.clone()]).is_err()); let episode = serde_json::json!({"episode_number":3,"absolute_number":49,"title":"Returned","synopsis":"Actual synopsis","aired_on":"2026-10-02","duration_seconds":1500}); let detail = || Detail { media: media.clone(), episodes: vec![episode.clone()], }; let data = series("opaque_ID", detail()).unwrap(); assert_eq!(data.episodes.len(), 1); assert_eq!(data.episodes[0].identity.episode, 3); assert_eq!(data.episodes[0].identity.media_id, "opaque_ID"); assert_eq!(data.episodes[0].duration_seconds, Some(1500)); assert_eq!( data.episodes[0].synopsis.as_deref(), Some("Actual synopsis") ); assert!(data.episodes[0].snapshot(&data.media).channel_id.is_none()); assert!(series("other", detail()).is_err()); assert!( series( "opaque_ID", Detail { media: media.clone(), episodes: vec![episode.clone(), episode] } ) .is_err() ); assert!( series( "opaque_ID", Detail { media, episodes: vec![serde_json::json!({"absolute_number":49})] } ) .is_err() ); assert!(slug("薬屋").is_err()); assert_eq!(slug(" A Test!! -- Title ").unwrap(), "a-test-title"); assert_eq!(slug(" --Title-- ").unwrap(), "-title-"); assert_eq!(slug("École déjà-vu").unwrap(), "ecole-deja-vu"); assert_eq!(slug("Cafe\u{0301}").unwrap(), "cafe"); assert_eq!(slug("A薬屋 KK İ").unwrap(), "kk-i"); assert_eq!( slug("\u{feff}A\u{00a0}B\u{2003}C\u{feff}").unwrap(), "a-b-c" ); assert_eq!(slug("A\u{0085}B").unwrap(), "ab"); assert!(slug("!!薬屋!!").is_err()); assert_eq!( title(&serde_json::json!({"id":"id","title":{"english":"AB","romaji":"CD"}})).unwrap(), "AB" ); assert_eq!( title(&serde_json::json!({"id":"id","title":{"english":"𐀀A","romaji":"BC"}})).unwrap(), "BC" ); assert_eq!( title(&serde_json::json!({"id":"id","title":{"native":""}})).unwrap(), "" ); } #[test] fn anilist_catalog_numbers_are_not_planned_totals_or_stream_claims() { let metadata = serde_json::json!({"id":"1","catalog_provider":"anilist","title":{"romaji":"Cowboy Bebop"},"status":"FINISHED","episode_count":26,"catalog_count":26}); let series = anilist_series(metadata).unwrap(); assert_eq!(series.episodes.len(), 26); assert_eq!( series.episodes.last().unwrap().identity.key(), "anilist:1:26" ); let snapshot = series.episodes[0].snapshot(&series.media); assert_eq!(snapshot.channel.as_deref(), Some("AniList")); assert_eq!( crate::youtube::video_url(&snapshot.id).unwrap(), "https://zokoanime.video/stream/ani/1/1/sub" ); let ongoing = anilist_series(serde_json::json!({"id":"2","catalog_provider":"anilist","title":{"romaji":"Ongoing"},"status":"RELEASING","episode_count":24,"nextAiringEpisode":{"episode":4},"catalog_count":3})).unwrap(); assert_eq!(ongoing.episodes.len(), 3); let unknown = anilist_series(serde_json::json!({"id":"3","catalog_provider":"anilist","title":{"romaji":"Unknown"},"status":"RELEASING","episode_count":null,"catalog_count":null})).unwrap(); assert!(unknown.episodes.is_empty()); assert!(unknown.media.metadata["catalog_count"].is_null()); assert_eq!( anilist_episode(&unknown.media, 42).unwrap().identity.key(), "anilist:3:42" ); assert!(anilist_episode(&unknown.media, 0).is_err()); assert!(anilist_episode(&unknown.media, i32::MAX as u32 + 1).is_err()); }