checks
This commit is contained in:
@@ -14,6 +14,7 @@ export const TLA_TO_ISO2: Record<string, string> = {
|
||||
COD: "cd",
|
||||
COL: "co",
|
||||
CPV: "cv",
|
||||
CUW: "cw",
|
||||
CZE: "cz",
|
||||
ECU: "ec",
|
||||
EGY: "eg",
|
||||
@@ -48,5 +49,4 @@ export const TLA_TO_ISO2: Record<string, string> = {
|
||||
URU: "uy",
|
||||
USA: "us",
|
||||
UZB: "uz",
|
||||
CUW: "cw",
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ export const TEAM_LOCALIZATION: Record<string, { de: string; en: string }> = {
|
||||
COD: { de: "DR Kongo", en: "Congo DR" },
|
||||
COL: { de: "Kolumbien", en: "Colombia" },
|
||||
CPV: { de: "Kap Verde", en: "Cape Verde" },
|
||||
CUW: { de: "Curaçao", en: "Curaçao" },
|
||||
CZE: { de: "Tschechien", en: "Czech Republic" },
|
||||
ECU: { de: "Ecuador", en: "Ecuador" },
|
||||
EGY: { de: "Ägypten", en: "Egypt" },
|
||||
|
||||
164
scripts/verify-migration.ts
Normal file
164
scripts/verify-migration.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
// verify-migration.ts — Automatisierte FIFA-Migrations-Checks
|
||||
// Ausführung: npx tsx scripts/verify-migration.ts
|
||||
|
||||
import { TEAM_LOCALIZATION } from "../lib/team-mappings";
|
||||
import { TLA_TO_ISO2 } from "../lib/flags";
|
||||
import { FIFA_GROUP_MAP } from "../lib/fifa-constants";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
const FIFA_BASE = "https://api.fifa.com/api/v3";
|
||||
const FIFA_SEASON = "285023";
|
||||
const FLAGS_DIR = path.join(__dirname, "..", "public", "flags");
|
||||
|
||||
interface RawMatch {
|
||||
MatchNumber: number;
|
||||
IdGroup: string | null;
|
||||
IdStage: string;
|
||||
Home: { IdTeam: string; Abbreviation: string; TeamName?: Array<{ Locale: string; Description: string }> } | null;
|
||||
Away: { IdTeam: string; Abbreviation: string; TeamName?: Array<{ Locale: string; Description: string }> } | null;
|
||||
HomeTeamScore: number | null;
|
||||
AwayTeamScore: number | null;
|
||||
}
|
||||
|
||||
let failures = 0;
|
||||
function pass(msg: string) { console.log(" \x1b[32m✓\x1b[0m " + msg); }
|
||||
function fail(msg: string) { console.log(" \x1b[31m✗\x1b[0m " + msg); failures++; }
|
||||
function warn(msg: string) { console.log(" \x1b[33m!\x1b[0m " + msg); }
|
||||
|
||||
async function main() {
|
||||
console.log("\n═══ FIFA-Migration Verifikation ═══\n");
|
||||
|
||||
// ── 1. Flaggen-Vollständigkeit ──
|
||||
console.log("1. Flaggen-Vollständigkeit");
|
||||
const missingFlags: string[] = [];
|
||||
const emptyFlags: string[] = [];
|
||||
for (const code of Object.keys(TEAM_LOCALIZATION)) {
|
||||
const iso2 = TLA_TO_ISO2[code];
|
||||
if (!iso2) { missingFlags.push(`${code} (kein ISO-2)`); continue; }
|
||||
const filePath = path.join(FLAGS_DIR, `${iso2}.svg`);
|
||||
if (!fs.existsSync(filePath)) { missingFlags.push(`${code} → ${iso2}.svg`); continue; }
|
||||
const content = fs.readFileSync(filePath, "utf8");
|
||||
if (content.trim().length === 0) { emptyFlags.push(`${code} → ${iso2}.svg (leer)`); continue; }
|
||||
if (!content.includes("<svg")) { emptyFlags.push(`${code} → ${iso2}.svg (kein <svg>, ${content.length} bytes)`); continue; }
|
||||
}
|
||||
if (missingFlags.length === 0 && emptyFlags.length === 0) {
|
||||
pass(`${Object.keys(TEAM_LOCALIZATION).length} Teams, alle Flaggen ok`);
|
||||
} else {
|
||||
for (const f of missingFlags) fail(`Fehlende Flagge: ${f}`);
|
||||
for (const f of emptyFlags) fail(`Leere/defekte Flagge: ${f}`);
|
||||
}
|
||||
|
||||
// ── 2. FIFA-Feed abrufen ──
|
||||
console.log("\n2. FIFA-Feed-Daten abrufen");
|
||||
let rawMatches: RawMatch[] = [];
|
||||
try {
|
||||
const url = `${FIFA_BASE}/calendar/matches?language=de&count=500&idSeason=${FIFA_SEASON}`;
|
||||
const res = await fetch(url, { headers: { "User-Agent": "wm2026-board/1.0" }, signal: AbortSignal.timeout(10000) });
|
||||
if (!res.ok) { fail(`HTTP ${res.status}`); } else {
|
||||
const data = (await res.json()) as { Results: RawMatch[] };
|
||||
rawMatches = data.Results ?? [];
|
||||
pass(`${rawMatches.length} Matches geladen`);
|
||||
}
|
||||
} catch (err) {
|
||||
fail(`Feed nicht erreichbar: ${err instanceof Error ? err.message : err}`);
|
||||
}
|
||||
|
||||
if (rawMatches.length === 0) {
|
||||
console.log(`\n\x1b[31m${failures} Fehler\x1b[0m (Feed-Fehler, Rest übersprungen)`);
|
||||
process.exit(failures > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
// ── 3. Gruppenzuordnung ──
|
||||
console.log("\n3. Gruppenzuordnung");
|
||||
const perGroup = new Map<string, RawMatch[]>();
|
||||
const teamsPerGroup = new Map<string, Set<string>>();
|
||||
for (const m of rawMatches) {
|
||||
const gid = m.IdGroup ? FIFA_GROUP_MAP[m.IdGroup] : null;
|
||||
if (gid) {
|
||||
if (!perGroup.has(gid)) perGroup.set(gid, []);
|
||||
perGroup.get(gid)!.push(m);
|
||||
if (!teamsPerGroup.has(gid)) teamsPerGroup.set(gid, new Set());
|
||||
if (m.Home?.IdTeam) teamsPerGroup.get(gid)!.add(m.Home.IdTeam);
|
||||
if (m.Away?.IdTeam) teamsPerGroup.get(gid)!.add(m.Away.IdTeam);
|
||||
}
|
||||
}
|
||||
const groupIds = ["A","B","C","D","E","F","G","H","I","J","K","L"];
|
||||
let groupOk = true;
|
||||
for (const g of groupIds) {
|
||||
const matches = perGroup.get(g)?.length ?? 0;
|
||||
const teams = teamsPerGroup.get(g)?.size ?? 0;
|
||||
if (matches !== 6) { warn(`Gruppe ${g}: ${matches} Spiele (erwartet 6)`); groupOk = false; }
|
||||
if (teams !== 4) { warn(`Gruppe ${g}: ${teams} Teams (erwartet 4)`); groupOk = false; }
|
||||
}
|
||||
if (groupOk) {
|
||||
const total = [...perGroup.values()].reduce((s, a) => s + a.length, 0);
|
||||
pass(`${total} Gruppenspiele, 12 Gruppen mit je 6 Spielen + 4 Teams`);
|
||||
}
|
||||
|
||||
// ── 4. MatchNumber-Vollständigkeit ──
|
||||
console.log("\n4. MatchNumber-Vollständigkeit (1–104)");
|
||||
const byNum = new Map<number, RawMatch[]>();
|
||||
for (const m of rawMatches) {
|
||||
if (!byNum.has(m.MatchNumber)) byNum.set(m.MatchNumber, []);
|
||||
byNum.get(m.MatchNumber)!.push(m);
|
||||
}
|
||||
const missing: number[] = [];
|
||||
const dupes: number[] = [];
|
||||
for (let n = 1; n <= 104; n++) {
|
||||
const entries = byNum.get(n);
|
||||
if (!entries || entries.length === 0) missing.push(n);
|
||||
else if (entries.length > 1) dupes.push(n);
|
||||
}
|
||||
if (missing.length === 0 && dupes.length === 0) {
|
||||
pass("Alle 104 MatchNumbers genau einmal vorhanden");
|
||||
} else {
|
||||
if (missing.length > 0) fail(`Fehlend: ${missing.join(", ")}`);
|
||||
if (dupes.length > 0) fail(`Duplikate: ${dupes.join(", ")}`);
|
||||
}
|
||||
|
||||
// ── 5. Team-Code-Auflösung ──
|
||||
console.log("\n5. Team-Code-Auflösung");
|
||||
const teamCodes = new Set<string>();
|
||||
for (const m of rawMatches) {
|
||||
for (const tb of [m.Home, m.Away]) {
|
||||
if (tb?.Abbreviation) teamCodes.add(tb.Abbreviation);
|
||||
}
|
||||
}
|
||||
const unresolved: string[] = [];
|
||||
for (const code of teamCodes) {
|
||||
const appCode = code; // Rohwert aus FIFA
|
||||
// FIFA_CODE_OVERRIDE: CRO→HRV, POR→PRT, SUI→CHE
|
||||
const overrides: Record<string, string> = { CRO: "HRV", POR: "PRT", SUI: "CHE" };
|
||||
const resolved = overrides[code] ?? code;
|
||||
if (!TEAM_LOCALIZATION[resolved]) unresolved.push(`${code}→${resolved}`);
|
||||
if (!TLA_TO_ISO2[resolved]) unresolved.push(`${code}→${resolved} (kein ISO-2)`);
|
||||
}
|
||||
if (unresolved.length === 0) {
|
||||
pass(`${teamCodes.size} eindeutige Team-Codes, alle in TEAM_LOCALIZATION + TLA_TO_ISO2`);
|
||||
} else {
|
||||
for (const u of unresolved) fail(`Nicht auflösbar: ${u}`);
|
||||
}
|
||||
|
||||
// ── 6. K.o.-Paarung 89/90 ──
|
||||
console.log("\n6. K.o.-Paarung 89/90");
|
||||
const m89 = rawMatches.find(m => m.MatchNumber === 89);
|
||||
const m90 = rawMatches.find(m => m.MatchNumber === 90);
|
||||
if (m89) {
|
||||
const h = m89.Home?.Abbreviation ?? "?";
|
||||
const a = m89.Away?.Abbreviation ?? "?";
|
||||
const ok = h === "PAR" && a === "FRA";
|
||||
(ok ? pass : fail)(`Spiel 89: ${h}/${a} ${ok ? "(korrekt PAR/FRA)" : "(erwartet PAR/FRA)"}`);
|
||||
} else fail("Spiel 89 nicht gefunden");
|
||||
if (m90) {
|
||||
const h = m90.Home?.Abbreviation ?? "?";
|
||||
const a = m90.Away?.Abbreviation ?? "?";
|
||||
const ok = h === "CAN" && a === "MAR";
|
||||
(ok ? pass : fail)(`Spiel 90: ${h}/${a} ${ok ? "(korrekt CAN/MAR)" : "(erwartet CAN/MAR)"}`);
|
||||
} else fail("Spiel 90 nicht gefunden");
|
||||
|
||||
console.log(`\n═══ Ergebnis: ${failures} Fehler ═══\n`);
|
||||
process.exit(failures > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
main().catch(err => { console.error(err); process.exit(1); });
|
||||
Reference in New Issue
Block a user