import { describe, it, expect } from "vitest"; /** * Tests for proper tournament bracket seeding * Phase 2.7 Bug Fix: Ensure proper seeding (1v16, 8v9, etc.) instead of sequential (1v2, 3v4) */ /** * Helper to simulate the seeding algorithm from playoff-match.ts */ function generateStandardSeeding(teamCount: number): [number, number][] { if (![4, 8, 16, 32].includes(teamCount)) { const pairs: [number, number][] = []; for (let i = 0; i < teamCount; i += 2) { pairs.push([i, i + 1]); } return pairs; } const rounds = Math.log2(teamCount); let seeds = [0, 1]; for (let round = 1; round < rounds; round++) { const newSeeds: number[] = []; const maxSeed = Math.pow(2, round + 1) - 1; for (const seed of seeds) { newSeeds.push(seed); newSeeds.push(maxSeed - seed); } seeds = newSeeds; } const pairs: [number, number][] = []; for (let i = 0; i < seeds.length; i += 2) { pairs.push([seeds[i], seeds[i + 1]]); } return pairs; } describe("Bracket Seeding", () => { describe("4-Team Bracket", () => { it("generates correct seeding pairs", () => { const pairs = generateStandardSeeding(4); // Should be: 1v4, 2v3 (0-indexed: 0v3, 1v2) expect(pairs).toEqual([ [0, 3], // Seed 1 vs Seed 4 [1, 2], // Seed 2 vs Seed 3 ]); }); it("balances the bracket", () => { const pairs = generateStandardSeeding(4); // Top half: seeds 1,4 (sum = 5) // Bottom half: seeds 2,3 (sum = 5) const topSum = pairs[0][0] + pairs[0][1] + 2; // +2 for 1-indexing const bottomSum = pairs[1][0] + pairs[1][1] + 2; expect(topSum).toBe(bottomSum); }); }); describe("8-Team Bracket", () => { it("generates correct seeding pairs", () => { const pairs = generateStandardSeeding(8); // Standard 8-team seeding: 1v8, 4v5, 2v7, 3v6 expect(pairs).toEqual([ [0, 7], // Seed 1 vs Seed 8 [3, 4], // Seed 4 vs Seed 5 [1, 6], // Seed 2 vs Seed 7 [2, 5], // Seed 3 vs Seed 6 ]); }); it("ensures top seeds are separated", () => { const pairs = generateStandardSeeding(8); // Seeds 1 and 2 should be in different halves const match1Seeds = [pairs[0][0] + 1, pairs[0][1] + 1]; const match2Seeds = [pairs[1][0] + 1, pairs[1][1] + 1]; const topHalf = [...match1Seeds, ...match2Seeds]; const match3Seeds = [pairs[2][0] + 1, pairs[2][1] + 1]; const match4Seeds = [pairs[3][0] + 1, pairs[3][1] + 1]; const bottomHalf = [...match3Seeds, ...match4Seeds]; // Seed 1 should be in top half, seed 2 in bottom half (or vice versa) expect(topHalf.includes(1) && bottomHalf.includes(2)).toBe(true); }); }); describe("16-Team Bracket", () => { it("generates 8 matches", () => { const pairs = generateStandardSeeding(16); expect(pairs).toHaveLength(8); }); it("uses all 16 seeds exactly once", () => { const pairs = generateStandardSeeding(16); const allSeeds = pairs.flat().map(s => s + 1).toSorted((a, b) => a - b); expect(allSeeds).toEqual(Array.from({ length: 16 }, (_, i) => i + 1)); }); it("ensures seed 1 plays seed 16", () => { const pairs = generateStandardSeeding(16); const firstMatch = pairs[0]; // Convert to 1-indexed seeds const seed1 = firstMatch[0] + 1; const seed2 = firstMatch[1] + 1; expect([seed1, seed2].toSorted()).toEqual([1, 16]); }); it("has proper matchup sums for balance", () => { const pairs = generateStandardSeeding(16); // Each matchup should have a sum that indicates proper seeding // 1v16 (sum=17), 2v15 (sum=17), etc. const sums = pairs.map(([a, b]) => (a + 1) + (b + 1)); // All matchups should sum to 17 (n + 1 for 16 teams) expect(sums.every(sum => sum === 17)).toBe(true); }); it("balances each quarterfinal pod", () => { const pairs = generateStandardSeeding(16); // Each pod of 4 teams should have balanced seeds // Pod 1 (matches 0,1): 1,16,8,9 sum = 34 // Pod 2 (matches 2,3): 5,12,4,13 sum = 34 // Pod 3 (matches 4,5): 6,11,3,14 sum = 34 // Pod 4 (matches 6,7): 7,10,2,15 sum = 34 const pod1 = pairs[0].concat(pairs[1]).map(s => s + 1); const pod2 = pairs[2].concat(pairs[3]).map(s => s + 1); const pod3 = pairs[4].concat(pairs[5]).map(s => s + 1); const pod4 = pairs[6].concat(pairs[7]).map(s => s + 1); const sum1 = pod1.reduce((a, b) => a + b, 0); const sum2 = pod2.reduce((a, b) => a + b, 0); const sum3 = pod3.reduce((a, b) => a + b, 0); const sum4 = pod4.reduce((a, b) => a + b, 0); expect(sum1).toBe(sum2); expect(sum2).toBe(sum3); expect(sum3).toBe(sum4); expect(sum1).toBe(34); // (1+16+8+9) }); }); describe("32-Team Bracket", () => { it("generates 16 matches", () => { const pairs = generateStandardSeeding(32); expect(pairs).toHaveLength(16); }); it("ensures seed 1 plays seed 32", () => { const pairs = generateStandardSeeding(32); const firstMatch = pairs[0]; const seed1 = firstMatch[0] + 1; const seed2 = firstMatch[1] + 1; expect([seed1, seed2].toSorted()).toEqual([1, 32]); }); it("uses all 32 seeds exactly once", () => { const pairs = generateStandardSeeding(32); const allSeeds = pairs.flat().map(s => s + 1).toSorted((a, b) => a - b); expect(allSeeds).toEqual(Array.from({ length: 32 }, (_, i) => i + 1)); }); }); describe("Non-Standard Sizes", () => { it("returns sequential pairs for non-power-of-2 sizes", () => { const pairs = generateStandardSeeding(68); // For NCAA 68, should return sequential until custom logic is implemented expect(pairs[0]).toEqual([0, 1]); expect(pairs[1]).toEqual([2, 3]); expect(pairs).toHaveLength(34); }); }); describe("Seed Info Display", () => { it("formats seed info correctly for 16-team bracket", () => { const pairs = generateStandardSeeding(16); // Verify seed info would be displayed correctly (1-indexed for users) const match1SeedInfo = `${pairs[0][0] + 1} vs ${pairs[0][1] + 1}`; expect(match1SeedInfo).toBe("1 vs 16"); // Check that all seed info strings are formatted correctly const allSeedInfo = pairs.map(([a, b]) => `${a + 1} vs ${b + 1}`); expect(allSeedInfo).toHaveLength(8); // Each should be "N vs M" format where N + M = 17 allSeedInfo.forEach(info => { expect(info).toMatch(/^\d+ vs \d+$/); }); }); }); });