105 lines
4.4 KiB
Swift
105 lines
4.4 KiB
Swift
import XCTest
|
|
@testable import TVAnarchyCore
|
|
|
|
/// The deterministic metadata core — filename → structured fields. Cases are
|
|
/// real-shaped release names (incl. the messy registry style with " — " notes).
|
|
final class FilenameParserTests: XCTestCase {
|
|
func testEpisodeRelease() {
|
|
let p = FilenameParser.parse(filename: "Psych.S01E04.720p.WEB-DL.x264-GROUP")
|
|
XCTAssertEqual(p.title, "Psych")
|
|
XCTAssertEqual(p.season, 1)
|
|
XCTAssertEqual(p.episode, 4)
|
|
XCTAssertEqual(p.quality, "720p")
|
|
XCTAssertEqual(p.codec?.lowercased(), "x264")
|
|
XCTAssertEqual(p.releaseSource?.lowercased(), "web-dl")
|
|
}
|
|
|
|
func testYearKeptOutOfTitle() {
|
|
let p = FilenameParser.parse(filename: "Psych 2006 Season 6 Complete 720p AMZN WEB-DL x264")
|
|
XCTAssertEqual(p.title, "Psych")
|
|
XCTAssertEqual(p.year, 2006)
|
|
XCTAssertEqual(p.quality, "720p")
|
|
}
|
|
|
|
func testDottedNameAndHevc() {
|
|
let p = FilenameParser.parse(filename: "Twin.Peaks.S01.COMPLETE.1080p.BluRay.x265.HEVC-PSA")
|
|
XCTAssertEqual(p.title, "Twin Peaks")
|
|
XCTAssertEqual(p.season, 1)
|
|
XCTAssertEqual(p.quality, "1080p")
|
|
XCTAssertEqual(p.codec?.lowercased(), "x265")
|
|
}
|
|
|
|
func testParseFromPathStripsDirAndExt() {
|
|
let p = FilenameParser.parse(path: "/m/cartoons/Futurama/Futurama S03E01 The Honking.mp4")
|
|
XCTAssertEqual(p.title, "Futurama")
|
|
XCTAssertEqual(p.season, 3)
|
|
XCTAssertEqual(p.episode, 1)
|
|
}
|
|
|
|
func testMovieWithYearNoEpisode() {
|
|
let p = FilenameParser.parse(filename: "The.Mists.Of.Avalon.2001.DVDRip.XviD-DiVAS")
|
|
XCTAssertEqual(p.title, "The Mists Of Avalon")
|
|
XCTAssertEqual(p.year, 2001)
|
|
XCTAssertNil(p.season)
|
|
XCTAssertNil(p.episode)
|
|
XCTAssertEqual(p.codec?.lowercased(), "xvid")
|
|
}
|
|
|
|
func testEnrichResultPartialDecode() throws {
|
|
// The real graceful-degrade shape (TMDB key unset, IMDb present).
|
|
let json = #"""
|
|
{"query":"Psych","year":2006,"tmdb_error":"TMDB_API_KEY unset",
|
|
"imdb_rating":8.4,"imdb_votes":120400,"genres":["Comedy","Crime","Mystery"]}
|
|
"""#
|
|
let r = try JSONDecoder().decode(EnrichResult.self, from: Data(json.utf8))
|
|
XCTAssertEqual(r.imdb_rating, 8.4)
|
|
XCTAssertEqual(r.imdb_votes, 120400)
|
|
XCTAssertEqual(r.genres, ["Comedy", "Crime", "Mystery"])
|
|
XCTAssertNil(r.poster_url)
|
|
XCTAssertEqual(r.tmdb_error, "TMDB_API_KEY unset")
|
|
|
|
var meta = MediaMeta(path: "/m/Psych", parsed: ParsedFilename(title: "Psych", year: 2006))
|
|
meta.apply(r, at: Date(timeIntervalSince1970: 0))
|
|
XCTAssertEqual(meta.imdbRating, 8.4)
|
|
XCTAssertNil(meta.posterURL)
|
|
XCTAssertNotNil(meta.enrichedAt)
|
|
}
|
|
|
|
// MARK: TitleRefiner seam
|
|
|
|
/// The refiner is consulted ONLY when regex yields a degenerate (<2 char)
|
|
/// title, and its answer wins; a clean regex title never pays for the model.
|
|
func testRefinerConsultedOnlyForDegenerateTitles() {
|
|
final class SpyRefiner: TitleRefiner, @unchecked Sendable {
|
|
var asked: [String] = []
|
|
func refineTitle(from filename: String) -> String? {
|
|
asked.append(filename)
|
|
return "Refined Title"
|
|
}
|
|
}
|
|
let spy = SpyRefiner()
|
|
FilenameParser.refiner = spy
|
|
defer { FilenameParser.refiner = nil }
|
|
|
|
// Noise-first name: every marker sits at index 0 → empty regex title.
|
|
let messy = FilenameParser.parse(filename: "2160p.x265.GROUP-RELEASE")
|
|
XCTAssertEqual(messy.title, "Refined Title")
|
|
XCTAssertEqual(spy.asked, ["2160p.x265.GROUP-RELEASE"])
|
|
|
|
let clean = FilenameParser.parse(filename: "Psych.S01E04.720p.WEB-DL.x264-GROUP")
|
|
XCTAssertEqual(clean.title, "Psych")
|
|
XCTAssertEqual(spy.asked.count, 1, "clean titles must not consult the model")
|
|
}
|
|
|
|
/// A refiner with no answer (nil) leaves the regex result intact.
|
|
func testRefinerNilAnswerKeepsRegexResult() {
|
|
struct MuteRefiner: TitleRefiner {
|
|
func refineTitle(from filename: String) -> String? { nil }
|
|
}
|
|
FilenameParser.refiner = MuteRefiner()
|
|
defer { FilenameParser.refiner = nil }
|
|
let p = FilenameParser.parse(filename: "2160p.x265.GROUP-RELEASE")
|
|
XCTAssertEqual(p.title, "2160p.x265.GROUP-RELEASE",
|
|
"with no usable title anywhere, the raw name survives")
|
|
}
|
|
}
|