148 lines
4.1 KiB
TypeScript
148 lines
4.1 KiB
TypeScript
|
|
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<string, string>([
|
||
|
|
[playerKey(sinner), "sp-sinner"],
|
||
|
|
[playerKey(djokovic), "sp-djokovic"],
|
||
|
|
[playerKey(noLink), "sp-qual"],
|
||
|
|
]);
|
||
|
|
const roundIsScoring = new Map<string, boolean>([
|
||
|
|
["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);
|
||
|
|
});
|
||
|
|
});
|