2026-06-07 21:56:40 -07:00
import XCTest
2026-06-08 22:04:22 -07:00
@ testable import TVAnarchyCore
2026-06-07 21:56:40 -07:00
// / L o c k s t h e t o r r e n t w i r e f o r m a t s a g a i n s t r e a l ` c l i . t s ` o u t p u t .
final class TorrentDecoderTests : XCTestCase {
2026-06-07 23:35:39 -07:00
func testTransferRowDecodeRPC ( ) throws {
// R i c h J S O N - R P C s h a p e f r o m ` c l i . t s t x - l i s t ` ( t r a n s m i s s i o n L i s t R i c h ) .
2026-06-07 21:56:40 -07:00
let json = # " " "
2026-06-07 23:35:39 -07:00
[ { " 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 } ]
2026-06-07 21:56:40 -07:00
" " " #
let rows = try JSONDecoder ( ) . decode ( [ TorrentRow ] . self , from : Data ( json . utf8 ) )
2026-06-07 23:35:39 -07:00
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 ) // 1 0 2 4 B / s → f o r m a t t e d
let wip = rows [ 1 ]
XCTAssertFalse ( wip . isComplete )
XCTAssertTrue ( wip . isDownloading )
XCTAssertEqual ( wip . progress , 0.15 , accuracy : 0.001 )
XCTAssertNil ( wip . completedAt ) // d o n e D a t e 0 → n o t c o m p l e t e d
XCTAssertEqual ( wip . etaText , " 1h 2m " ) // 3 7 2 0 s
}
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 ] ) // n e w e s t - c o m p l e t e d f i r s t ; i n - p r o g r e s s l a s t
2026-06-07 21:56:40 -07:00
}
2026-06-09 05:50:01 -07:00
func testTransferRowDecodesHealthFields ( ) throws {
// T h e l i s t p o l l n o w a l s o c a r r i e s e r r o r / e r r o r S t r i n g / p e e r s C o n n e c t e d ( a d d e d t o
// R P C _ F I E L D S ) — t h e y ' r e o p t i o n a l s o a p r e - P a r t - E c l i s t i l l d e c o d e s , b u t w h e n
// p r e s e n t t h e y m u s t d r i v e t h e h e a l t h c l a s s i f i e r .
let json = # " " "
[ { " id " : 7 , " name " : " Stuck " , " percentDone " : 0.4 , " status " : 4 , " doneDate " : 0 , " addedDate " : 1749300000 ,
" haveValid " : 1 , " sizeWhenDone " : 10 , " rateDownload " : 0 , " rateUpload " : 0 , " uploadRatio " : 0 , " eta " : - 1 ,
" error " : 0 , " errorString " : " " , " peersConnected " : 0 } ]
" " " #
let row = try JSONDecoder ( ) . decode ( [ TorrentRow ] . self , from : Data ( json . utf8 ) ) [ 0 ]
XCTAssertEqual ( row . peersConnected , 0 )
// 0 p e e r s + 0 r a t e , s t u c k p a s t t h e d e a d t h r e s h o l d → d e a d .
XCTAssertEqual ( row . health ( secondsStuck : 400 ) , . dead )
// S a m e r o w , e r r o r e d t a k e s p r e c e d e n c e r e g a r d l e s s o f p e e r s / t i m e .
let errJSON = # " { " id " :8, " name " : " E " , " percentDone " :0.4, " status " :4, " doneDate " :0, " addedDate " :1, " haveValid " :1, " sizeWhenDone " :10, " rateDownload " :0, " rateUpload " :0, " uploadRatio " :0, " eta " :-1, " error " :3, " errorString " : " tracker gone " , " peersConnected " :5} " #
let errored = try JSONDecoder ( ) . decode ( TorrentRow . self , from : Data ( errJSON . utf8 ) )
XCTAssertEqual ( errored . health ( secondsStuck : 10 ) , . errored )
}
func testTorrentDetailDecode ( ) throws {
// W i r e s h a p e o f ` c l i . t s t x - d e t a i l < i d > ` ( t r a n s m i s s i o n D e t a i l ) .
let json = # " " "
{ " id " : 11 , " name " : " WIP.S01 " , " percentDone " : 0.15 , " status " : 4 , " error " : 0 , " errorString " : " " ,
" eta " : 3720 , " rateDownload " : 1048576 , " rateUpload " : 0 , " uploadRatio " : 0 ,
" peersConnected " : 3 , " peersSendingToUs " : 2 , " peersGettingFromUs " : 1 ,
" downloadDir " : " /bigdisk/_/media/tv " ,
" trackerStats " : [
{ " host " : " udp://tracker.one:1337 " , " announceState " : 1 , " lastAnnounceResult " : " Success " , " lastAnnounceSucceeded " : true , " seederCount " : 42 , " leecherCount " : 7 } ,
{ " host " : " udp://dead.tracker:80 " , " announceState " : 0 , " lastAnnounceResult " : " Could not connect to tracker " , " lastAnnounceSucceeded " : false , " seederCount " : - 1 , " leecherCount " : - 1 }
] }
" " " #
let d = try JSONDecoder ( ) . decode ( TorrentDetail . self , from : Data ( json . utf8 ) )
XCTAssertEqual ( d . id , 11 )
XCTAssertFalse ( d . isComplete )
XCTAssertFalse ( d . hasError )
XCTAssertEqual ( d . peersText , " 3 connected · ↓2 ↑1 " )
XCTAssertEqual ( d . trackerStats . count , 2 )
XCTAssertTrue ( d . trackerStats [ 0 ] . lastAnnounceSucceeded )
XCTAssertEqual ( d . trackerStats [ 0 ] . swarmText , " 42 seed · 7 leech " )
XCTAssertEqual ( d . trackerStats [ 1 ] . swarmText , " — seed · — leech " ) // - 1 → u n k n o w n
XCTAssertEqual ( d . health ( ) , . downloading )
}
2026-06-07 21:56:40 -07:00
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 ) // n u l l m a g n e t → n o t a d d a b l e
XCTAssertNotEqual ( results [ 0 ] . id , results [ 1 ] . id )
}
}