import XCTest @testable import MatchLiveTv final class ScoreActionDecodeTests: XCTestCase { func testDecodesScoreActionResponse() throws { let json = """ { "id": "821ddb81-9dc2-466a-9ad5-2555e78f4653", "match_id": "e887ce1d-10ee-4680-8323-79bb8f11e0aa", "status": "live", "platform": "matchlivetv", "score": { "type": "score_update", "board_type": "volley", "home_sets": 0, "away_sets": 0, "home_points": 1, "away_points": 0, "current_set": 1, "set_partials": [], "timeout_home": false, "timeout_away": false, "period": null, "data": {} }, "set_won": false, "match_won": false, "winner": null } """.data(using: .utf8)! let dto = try ApiInstant.decoder.decode(StreamSessionDto.self, from: json) let score = try XCTUnwrap(dto.score?.toDomain()) XCTAssertEqual(score.homePoints, 1) XCTAssertEqual(score.boardType, "volley") } func testDecodesCloseSetResponseWithPartials() throws { let json = """ { "id": "821ddb81-9dc2-466a-9ad5-2555e78f4653", "match_id": "e887ce1d-10ee-4680-8323-79bb8f11e0aa", "status": "live", "platform": "matchlivetv", "score": { "type": "score_update", "board_type": "volley", "home_sets": 1, "away_sets": 0, "home_points": 0, "away_points": 0, "current_set": 2, "set_partials": [{ "set": 1, "home": 25, "away": 18 }], "timeout_home": false, "timeout_away": false }, "set_won": true, "match_won": false, "winner": "home" } """.data(using: .utf8)! let dto = try ApiInstant.decoder.decode(StreamSessionDto.self, from: json) let score = try XCTUnwrap(dto.score?.toDomain()) XCTAssertEqual(score.currentSet, 2) XCTAssertEqual(score.setPartials.count, 1) XCTAssertEqual(score.setPartials[0].home, 25) XCTAssertEqual(score.setPartials[0].away, 18) } func testDecodesBasketAdvancePeriodResponse() throws { let json = """ { "id": "821ddb81-9dc2-466a-9ad5-2555e78f4653", "match_id": "e887ce1d-10ee-4680-8323-79bb8f11e0aa", "status": "live", "platform": "matchlivetv", "score": { "type": "score_update", "board_type": "basket", "home_points": 12, "away_points": 10, "current_set": 1, "set_partials": [], "timeout_home": false, "timeout_away": false, "period": "Q2", "clock_secs": 600, "clock_running": false, "data": { "home_score": 12, "away_score": 10, "period": 2, "clock_secs": 600, "clock_running": false, "overtime": false } }, "set_won": false, "match_won": false, "winner": null } """.data(using: .utf8)! let dto = try ApiInstant.decoder.decode(StreamSessionDto.self, from: json) let score = try XCTUnwrap(dto.score?.toDomain()) XCTAssertEqual(score.periodLabel, "Q2") XCTAssertEqual(score.period, 2) XCTAssertEqual(score.clockSecs, 600) XCTAssertFalse(score.clockRunning) } func testDecodesResumeSessionDetailWithBasketScore() throws { let json = """ { "id": "420fd5b9-b2ad-45af-9c1d-569c654866f8", "match_id": "e887ce1d-10ee-4680-8323-79bb8f11e0aa", "status": "paused", "platform": "matchlivetv", "rtmp_ingest_url": "rtmp://localhost:1935/live/key", "hls_playback_url": "http://localhost:8888/live/key/index.m3u8", "watch_page_url": "http://localhost:3000/live/abc", "share_url": "http://localhost:3000/live/abc", "youtube_watch_url": null, "youtube_broadcast_id": null, "youtube_ready": false, "privacy_status": "unlisted", "quality_preset": "720p_30_2.5mbps", "target_bitrate": 2500000, "started_at": "2026-06-19T10:30:00.000Z", "disconnection_count": 0, "score": { "type": "score_update", "board_type": "basket", "home_sets": 0, "away_sets": 0, "home_points": 42, "away_points": 38, "current_set": 1, "set_partials": [], "timeout_home": false, "timeout_away": false, "period": "Q3", "clock_secs": 312, "clock_running": true, "data": { "home_score": 42, "away_score": 38, "period": 3, "clock_secs": 312, "clock_running": true, "overtime": false } }, "devices": [ { "type": "device_state", "battery": 85, "network": "wifi", "bitrate": 2100, "fps": 30, "status": "paused" } ] } """.data(using: .utf8)! let dto = try ApiInstant.decoder.decode(StreamSessionDto.self, from: json) let score = try XCTUnwrap(dto.score?.toDomain()) XCTAssertEqual(score.homePoints, 42) XCTAssertEqual(score.periodLabel, "Q3") XCTAssertEqual(dto.status, "paused") } func testDecodesBasketScoreWithNumericTimeouts() throws { let json = """ { "id": "a", "match_id": "b", "status": "paused", "platform": "matchlivetv", "score": { "board_type": "basket", "home_points": 10, "away_points": 8, "period": "Q2", "clock_secs": 600, "clock_running": false, "timeout_home": 0, "timeout_away": 0, "data": { "home_score": 10, "away_score": 8, "period": 2, "overtime": false } } } """.data(using: .utf8)! let dto = try ApiInstant.decoder.decode(StreamSessionDto.self, from: json) XCTAssertEqual(dto.score?.toDomain().homePoints, 10) } func testDecodesBasketScoreWithStringPeriodInData() throws { let json = """ { "id": "a", "match_id": "b", "status": "paused", "platform": "matchlivetv", "score": { "board_type": "basket", "home_points": 10, "away_points": 8, "period": "Q2", "clock_secs": 600, "clock_running": false, "data": { "period": "2", "home_score": 10, "away_score": 8, "overtime": false } } } """.data(using: .utf8)! let dto = try ApiInstant.decoder.decode(StreamSessionDto.self, from: json) XCTAssertEqual(dto.score?.toDomain().period, 2) } func testDecodesCableAdvancePeriodPayload() throws { let json = """ { "type": "score_update", "board_type": "basket", "home_points": 0, "away_points": 4, "period": "Q2", "clock_secs": 600, "clock_running": false, "data": { "period": 2, "home_score": 0, "away_score": 4, "clock_secs": 600, "clock_running": false, "overtime": false } } """.data(using: .utf8)! let score = ScoreState.fromCablePayload(try JSONSerialization.jsonObject(with: json) as! [String: Any]) XCTAssertEqual(score.period, 2) XCTAssertEqual(score.periodLabel, "Q2") XCTAssertEqual(score.homePoints, 0) XCTAssertEqual(score.awayPoints, 4) XCTAssertGreaterThan(score.progressKey(), ScoreState(boardType: "basket", period: 1, periodLabel: "Q1").progressKey()) } }