import { describe, it, expect } from "vitest"; import { playerKey, canonicalPlayerName, buildResolvedMatches, } from "../tennis-draw-mapping"; import type { FetchedDraw, FetchedPlayer } from "../types"; const sinner: FetchedPlayer = { name: "J Sinner", externalId: "Jannik Sinner", seed: 1 }; const djokovic: FetchedPlayer = { name: "N Djokovic", externalId: "Novak Djokovic", seed: 6 }; const noLink: FetchedPlayer = { name: "Some Qualifier", externalId: null, seed: null }; describe("playerKey", () => { it("prefers the external id", () => { expect(playerKey(sinner)).toBe("id:Jannik Sinner"); }); it("falls back to a normalized name when there is no external id", () => { expect(playerKey(noLink)).toBe("name:some qualifier"); }); }); describe("canonicalPlayerName", () => { it("uses the full name from the external id, not the abbreviated display", () => { expect(canonicalPlayerName(sinner)).toBe("Jannik Sinner"); }); it("uses the display name when there is no external id", () => { expect(canonicalPlayerName(noLink)).toBe("Some Qualifier"); }); it("strips the trailing Wikipedia disambiguator", () => { expect( canonicalPlayerName({ name: "Arthur Fils", externalId: "Arthur Fils (tennis)", seed: null }), ).toBe("Arthur Fils"); }); }); describe("buildResolvedMatches", () => { const draw: FetchedDraw = { templateId: "tennis_128", rounds: [ { roundName: "Round of 128", matches: [ { externalMatchId: "m-r128-1", round: "Round of 128", matchNumber: 1, player1: sinner, player2: noLink, winner: 1, }, ], }, { roundName: "Final", matches: [ { externalMatchId: "m-final", round: "Final", matchNumber: 1, player1: djokovic, player2: sinner, winner: 2, }, ], }, ], }; const idByKey = new Map([ [playerKey(sinner), "sp-sinner"], [playerKey(djokovic), "sp-djokovic"], [playerKey(noLink), "sp-qual"], ]); const roundIsScoring = new Map([ ["Round of 128", false], ["Final", true], ]); const resolved = buildResolvedMatches(draw, idByKey, roundIsScoring); it("maps winner slot 1 to winnerId/loserId", () => { const r128 = resolved.find((m) => m.externalMatchId === "m-r128-1"); expect(r128?.winnerId).toBe("sp-sinner"); expect(r128?.loserId).toBe("sp-qual"); expect(r128?.isScoring).toBe(false); }); it("maps winner slot 2 to the second player", () => { const final = resolved.find((m) => m.externalMatchId === "m-final"); expect(final?.participant1Id).toBe("sp-djokovic"); expect(final?.participant2Id).toBe("sp-sinner"); expect(final?.winnerId).toBe("sp-sinner"); expect(final?.loserId).toBe("sp-djokovic"); expect(final?.isScoring).toBe(true); }); it("leaves winner/loser null when the source reported no winner", () => { const noWinnerDraw: FetchedDraw = { templateId: "tennis_128", rounds: [ { roundName: "Round of 128", matches: [ { externalMatchId: "m-x", round: "Round of 128", matchNumber: 1, player1: sinner, player2: djokovic, winner: null, }, ], }, ], }; const [m] = buildResolvedMatches(noWinnerDraw, idByKey, roundIsScoring); expect(m.winnerId).toBeNull(); expect(m.loserId).toBeNull(); }); it("defaults isScoring to false for an unknown round", () => { const [m] = buildResolvedMatches( { templateId: "tennis_128", rounds: [ { roundName: "Mystery", matches: [ { externalMatchId: "m-y", round: "Mystery", matchNumber: 1, player1: sinner, player2: djokovic, winner: 1, }, ], }, ], }, idByKey, new Map(), ); expect(m.isScoring).toBe(false); }); });