Renames Sources/PlumTV→TVAnarchy and PlumTVCore→TVAnarchyCore (the rename the auto-commit service couldn't stage — it git-add'd the old, now-gone paths and aborted every cycle), and commits the accumulated work: - Library: black-built index fast path (LibraryIndex + scanFromIndex) with NFS-walk fallback; incremental --add on download-complete; mtime staleness gate; loose-file series-collapse fix; determinate scan/index progress. - Cover art: keyless TVmaze cartoon-vs-live-action disambiguation (type/year). - Player: sleep timer (timed + end-of-episode); visibility-gated polling. - Home: Continue Watching cover art + live refresh; Recently Added; adult gate. - Logs: multi-line selection + copy; truncated giant tx-list errors. - Hover previews (opt-in) via black ffmpeg + scp. Also gitignores foreign project trees (governor/mcp/fleet/recommender) that sit in this directory but belong to their own repos. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
2.9 KiB
Swift
54 lines
2.9 KiB
Swift
import XCTest
|
|
@testable import TVAnarchyCore
|
|
|
|
/// Locks the torrent wire formats against real `cli.ts` output.
|
|
final class TorrentDecoderTests: XCTestCase {
|
|
func testTransferRowDecodeRPC() throws {
|
|
// Rich JSON-RPC shape from `cli.ts tx-list` (transmissionListRich).
|
|
let json = #"""
|
|
[{"id":1,"name":"Done.S05","percentDone":1,"status":6,"doneDate":1749329821,"addedDate":1749300000,
|
|
"haveValid":8760000000,"sizeWhenDone":8760000000,"rateDownload":0,"rateUpload":1024,"uploadRatio":0.5,"eta":-1},
|
|
{"id":11,"name":"WIP","percentDone":0.15,"status":4,"doneDate":0,"addedDate":1749329000,
|
|
"haveValid":300000000,"sizeWhenDone":2000000000,"rateDownload":1048576,"rateUpload":0,"uploadRatio":0,"eta":3720}]
|
|
"""#
|
|
let rows = try JSONDecoder().decode([TorrentRow].self, from: Data(json.utf8))
|
|
XCTAssertEqual(rows.count, 2)
|
|
|
|
let done = rows[0]
|
|
XCTAssertTrue(done.isComplete)
|
|
XCTAssertEqual(done.statusLabel, "Seeding")
|
|
XCTAssertNotNil(done.completedAt)
|
|
XCTAssertEqual(done.completedAt, Date(timeIntervalSince1970: 1749329821))
|
|
XCTAssertFalse(done.upText.isEmpty) // 1024 B/s → formatted
|
|
|
|
let wip = rows[1]
|
|
XCTAssertFalse(wip.isComplete)
|
|
XCTAssertTrue(wip.isDownloading)
|
|
XCTAssertEqual(wip.progress, 0.15, accuracy: 0.001)
|
|
XCTAssertNil(wip.completedAt) // doneDate 0 → not completed
|
|
XCTAssertEqual(wip.etaText, "1h 2m") // 3720s
|
|
}
|
|
|
|
func testRecencySort() {
|
|
func row(_ id: Int, done: Int, added: Int) -> TorrentRow {
|
|
let j = #"{"id":\#(id),"name":"t\#(id)","percentDone":\#(done > 0 ? 1 : 0),"status":0,"doneDate":\#(done),"addedDate":\#(added),"haveValid":0,"sizeWhenDone":0,"rateDownload":0,"rateUpload":0,"uploadRatio":0,"eta":-1}"#
|
|
return try! JSONDecoder().decode(TorrentRow.self, from: Data(j.utf8))
|
|
}
|
|
let unsorted = [row(1, done: 0, added: 50), row(2, done: 100, added: 10), row(3, done: 200, added: 20)]
|
|
let sorted = unsorted.sorted(by: DownloadsController.byRecency)
|
|
XCTAssertEqual(sorted.map(\.id), [3, 2, 1]) // newest-completed first; in-progress last
|
|
}
|
|
|
|
func testSearchResultDecodeAndAddable() throws {
|
|
let json = #"""
|
|
[{"filename":"Show.S01.1080p","source":"1337x","size":"3.2 GB","seeders":45,"leechers":3,"magnet":"magnet:?xt=urn:btih:abc"},
|
|
{"filename":"NoMagnet","source":"tpb","size":"1 GB","seeders":2,"leechers":0,"magnet":null}]
|
|
"""#
|
|
let results = try JSONDecoder().decode([TorrentResult].self, from: Data(json.utf8))
|
|
XCTAssertEqual(results.count, 2)
|
|
XCTAssertTrue(results[0].addable)
|
|
XCTAssertEqual(results[0].seeders, 45)
|
|
XCTAssertFalse(results[1].addable) // null magnet → not addable
|
|
XCTAssertNotEqual(results[0].id, results[1].id)
|
|
}
|
|
}
|