Aggiunge app iOS nativa con parità multi-sport e test unitari.

Porting SwiftUI da Android (auth, hub, wizard, broadcast RTMP, overlay, scoring board-aware), reload hub al ritorno da diretta, decodifica score tollerante e documentazione allineata.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Emiliano Frascaro
2026-06-14 21:37:32 +02:00
parent b798e0ea06
commit 585332e32e
101 changed files with 7910 additions and 5 deletions

View File

@@ -0,0 +1,140 @@
import XCTest
@testable import MatchLiveTv
final class MatchScoringRulesTests: XCTestCase {
private func volleyMatch(setsToWin: Int = 3, rules: ScoringRules? = nil) -> Match {
Match(
id: "match-1",
teamId: "team-1",
teamName: "Tigers Volley",
opponentName: "Avversario",
location: nil,
scheduledAt: nil,
sportKey: "pallavolo",
sportLabel: "Pallavolo",
boardType: "volley",
overlayKind: "volley",
effectiveOverlayKind: "volley",
setsToWin: setsToWin,
category: nil,
scoringRules: rules,
activeSessionId: nil,
activeSessionStatus: nil,
streamCompleted: false,
coachHubVisible: nil,
homePrimaryColor: "#FF2D2D",
homeSecondaryColor: nil,
homeLogoUrl: nil,
opponentPrimaryColor: "#1E3A8A",
opponentLogoUrl: nil
)
}
func testSetWinnerAt26_12() {
let rules = MatchScoringContext(match: volleyMatch())
XCTAssertEqual(rules.setWinnerFromPoints(homePoints: 26, awayPoints: 12, currentSet: 1), .home)
}
func testSetWinnerRequiresMinLead() {
let rules = MatchScoringContext(match: volleyMatch())
XCTAssertNil(rules.setWinnerFromPoints(homePoints: 25, awayPoints: 24, currentSet: 1))
XCTAssertEqual(rules.setWinnerFromPoints(homePoints: 25, awayPoints: 23, currentSet: 1), .home)
}
func testDecidingSetUses15Points() {
let rules = MatchScoringContext(match: volleyMatch(setsToWin: 3))
XCTAssertEqual(rules.maxSets, 5)
XCTAssertEqual(rules.pointsTarget(currentSet: 5), 15)
XCTAssertEqual(rules.setWinnerFromPoints(homePoints: 15, awayPoints: 13, currentSet: 5), .home)
}
func testMatchWinnerAfterThreeSets() {
let rules = MatchScoringContext(match: volleyMatch(setsToWin: 3))
XCTAssertEqual(rules.matchWinner(homeSets: 3, awaySets: 1), .home)
XCTAssertNil(rules.matchWinner(homeSets: 2, awaySets: 1))
}
func testCustomScoringRulesFromMatch() {
let custom = ScoringRules(pointsPerSet: 21, pointsDecidingSet: 15, minPointLead: 2)
let rules = MatchScoringContext(match: volleyMatch(setsToWin: 2, rules: custom))
XCTAssertEqual(rules.pointsTarget(currentSet: 1), 21)
XCTAssertEqual(rules.setWinnerFromPoints(homePoints: 21, awayPoints: 19, currentSet: 1), .home)
}
func testUsesSetLogicOnlyForVolleyAndRacket() {
XCTAssertTrue(MatchScoringContext(match: volleyMatch()).usesSetLogic)
var basket = volleyMatch()
basket = Match(
id: basket.id,
teamId: basket.teamId,
teamName: basket.teamName,
opponentName: basket.opponentName,
location: basket.location,
scheduledAt: basket.scheduledAt,
sportKey: "basket",
sportLabel: "Basket",
boardType: "basket",
overlayKind: "basket",
effectiveOverlayKind: "basket",
setsToWin: 0,
category: nil,
scoringRules: nil,
activeSessionId: nil,
activeSessionStatus: nil,
streamCompleted: false,
coachHubVisible: nil,
homePrimaryColor: nil,
homeSecondaryColor: nil,
homeLogoUrl: nil,
opponentPrimaryColor: nil,
opponentLogoUrl: nil
)
XCTAssertFalse(MatchScoringContext(match: basket).usesSetLogic)
}
func testDecodesFlexibleScorePayload() throws {
let json = """
{
"board_type": "basket",
"home_points": 42,
"away_points": 38,
"current_set": 1,
"period": "Q3",
"clock_secs": 312,
"clock_running": 1,
"data": { "home_score": 42, "away_score": 38, "period": 3, "overtime": false }
}
""".data(using: .utf8)!
let dto = try JSONDecoder().decode(ScoreStateDto.self, from: json)
let score = dto.toDomain()
XCTAssertEqual(score.boardType, "basket")
XCTAssertEqual(score.homePoints, 42)
XCTAssertEqual(score.periodLabel, "Q3")
XCTAssertEqual(score.clockSecs, 312)
XCTAssertTrue(score.clockRunning)
}
}
final class ScoreboardOverlayTests: XCTestCase {
func testScoreboardColumnsAfterSetClosed() {
let score = ScoreState(
homeSets: 1,
awaySets: 0,
homePoints: 0,
awayPoints: 0,
currentSet: 2,
setPartials: [SetPartial(set: 1, home: 26, away: 12)],
boardType: "volley"
)
let board = score.toScoreboardState(homeTeamName: "Casa", awayTeamName: "Ospite")
XCTAssertEqual(board.columns.count, 2)
XCTAssertEqual(board.columns[0].setNumber, 1)
XCTAssertEqual(board.columns[0].homePoints, 26)
XCTAssertEqual(board.columns[0].awayPoints, 12)
XCTAssertFalse(board.columns[0].isCurrent)
XCTAssertEqual(board.columns[1].setNumber, 2)
XCTAssertTrue(board.columns[1].isCurrent)
XCTAssertEqual(board.columns[1].homePoints, 0)
XCTAssertEqual(board.columns[1].awayPoints, 0)
}
}