753 lines
26 KiB
TypeScript
753 lines
26 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { calculateDraftEligibility, getEligibilitySummary } from "~/lib/draft-eligibility";
|
|
|
|
// Helper to create test data
|
|
const createSport = (id: string, name: string) => ({ id, name });
|
|
|
|
const createTeam = (id: string) => ({ id });
|
|
|
|
const createParticipant = (id: string, sportId: string, sportName: string) => ({
|
|
id,
|
|
sport: { id: sportId, name: sportName },
|
|
});
|
|
|
|
const createPick = (
|
|
teamId: string,
|
|
participantId: string,
|
|
sportId: string,
|
|
sportName: string
|
|
) => ({
|
|
teamId,
|
|
participant: {
|
|
id: participantId,
|
|
sport: { id: sportId, name: sportName },
|
|
},
|
|
});
|
|
|
|
describe("calculateDraftEligibility", () => {
|
|
const NFL = createSport("nfl", "NFL");
|
|
const NBA = createSport("nba", "NBA");
|
|
const NHL = createSport("nhl", "NHL");
|
|
const sports = [NFL, NBA, NHL];
|
|
|
|
describe("Team Flex Pick Constraints", () => {
|
|
it("allows drafting from any sport when no picks made", () => {
|
|
const team1 = createTeam("team1");
|
|
const teams = [team1];
|
|
const participants = [
|
|
createParticipant("p1", "nfl", "NFL"),
|
|
createParticipant("p2", "nba", "NBA"),
|
|
createParticipant("p3", "nhl", "NHL"),
|
|
];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
[], // no picks yet
|
|
[],
|
|
participants,
|
|
sports,
|
|
5, // 5 rounds
|
|
teams
|
|
);
|
|
|
|
expect(eligibility.eligibleSportIds).toEqual(new Set(["nfl", "nba", "nhl"]));
|
|
expect(eligibility.flexPicksUsed).toBe(0);
|
|
expect(eligibility.flexPicksAvailable).toBe(2); // 5 rounds - 3 sports
|
|
});
|
|
|
|
it("tracks flex picks correctly when drafting duplicates", () => {
|
|
const team1 = createTeam("team1");
|
|
const teams = [team1];
|
|
const participants = [
|
|
createParticipant("p1", "nfl", "NFL"),
|
|
createParticipant("p2", "nfl", "NFL"),
|
|
createParticipant("p3", "nfl", "NFL"),
|
|
createParticipant("p4", "nba", "NBA"),
|
|
createParticipant("p5", "nhl", "NHL"),
|
|
];
|
|
|
|
const teamPicks = [
|
|
createPick("team1", "p1", "nfl", "NFL"), // First NFL
|
|
createPick("team1", "p2", "nfl", "NFL"), // Flex 1
|
|
createPick("team1", "p4", "nba", "NBA"), // First NBA
|
|
];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
teamPicks,
|
|
teamPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
expect(eligibility.flexPicksUsed).toBe(1); // One duplicate NFL
|
|
expect(eligibility.flexPicksAvailable).toBe(2);
|
|
expect(eligibility.picksBySport).toEqual({ nfl: 2, nba: 1 });
|
|
});
|
|
|
|
it("blocks all flex picks when limit reached", () => {
|
|
const team1 = createTeam("team1");
|
|
const teams = [team1];
|
|
const participants = [
|
|
...Array.from({ length: 5 }, (_, i) => createParticipant(`nfl${i}`, "nfl", "NFL")),
|
|
...Array.from({ length: 5 }, (_, i) => createParticipant(`nba${i}`, "nba", "NBA")),
|
|
...Array.from({ length: 5 }, (_, i) => createParticipant(`nhl${i}`, "nhl", "NHL")),
|
|
];
|
|
|
|
const teamPicks = [
|
|
createPick("team1", "nfl1", "nfl", "NFL"), // First NFL
|
|
createPick("team1", "nfl2", "nfl", "NFL"), // Flex 1
|
|
createPick("team1", "nfl3", "nfl", "NFL"), // Flex 2
|
|
createPick("team1", "nba1", "nba", "NBA"), // First NBA
|
|
];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
teamPicks,
|
|
teamPicks,
|
|
participants,
|
|
sports,
|
|
5, // 5 rounds, 3 sports = 2 flex picks available
|
|
teams
|
|
);
|
|
|
|
expect(eligibility.flexPicksUsed).toBe(2);
|
|
expect(eligibility.flexPicksAvailable).toBe(2);
|
|
// Can only draft from NHL (not yet drafted)
|
|
expect(eligibility.eligibleSportIds).toEqual(new Set(["nhl"]));
|
|
expect(eligibility.ineligibleReasons["nfl"]).toContain("All flex picks used");
|
|
expect(eligibility.ineligibleReasons["nba"]).toContain("All flex picks used");
|
|
});
|
|
});
|
|
|
|
describe("Global Sport Availability Constraints", () => {
|
|
it("allows first pick from sport when enough participants remain", () => {
|
|
const teams = [createTeam("team1"), createTeam("team2"), createTeam("team3")];
|
|
const participants = [
|
|
createParticipant("p1", "nfl", "NFL"),
|
|
createParticipant("p2", "nfl", "NFL"),
|
|
createParticipant("p3", "nfl", "NFL"),
|
|
createParticipant("p4", "nba", "NBA"),
|
|
createParticipant("p5", "nhl", "NHL"),
|
|
];
|
|
|
|
// Team 1 has drafted NFL, teams 2 and 3 haven't
|
|
const allPicks = [createPick("team1", "p1", "nfl", "NFL")];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team2", // Team that hasn't drafted NFL
|
|
[], // Team 2's picks (none)
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 2 NFL participants left, 2 teams still need NFL (team2, team3)
|
|
// 2 >= 2, so allowed
|
|
expect(eligibility.eligibleSportIds).toContain("nfl");
|
|
expect(eligibility.sportAvailability["nfl"].undraftedCount).toBe(2);
|
|
expect(eligibility.sportAvailability["nfl"].teamsStillNeeding).toBe(2);
|
|
expect(eligibility.sportAvailability["nfl"].canDraftAsFirst).toBe(true);
|
|
});
|
|
|
|
it("blocks flex pick when it would prevent other teams from getting sport", () => {
|
|
const teams = [createTeam("team1"), createTeam("team2"), createTeam("team3")];
|
|
const participants = [
|
|
createParticipant("p1", "nfl", "NFL"),
|
|
createParticipant("p2", "nfl", "NFL"),
|
|
createParticipant("p3", "nfl", "NFL"),
|
|
createParticipant("p4", "nba", "NBA"),
|
|
createParticipant("p5", "nhl", "NHL"),
|
|
];
|
|
|
|
// Team 1 has drafted NFL, teams 2 and 3 haven't
|
|
const allPicks = [createPick("team1", "p1", "nfl", "NFL")];
|
|
const team1Picks = [createPick("team1", "p1", "nfl", "NFL")];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1", // Team that already has NFL
|
|
team1Picks,
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 2 NFL participants left, 2 teams still need NFL
|
|
// For flex pick: 2 > 2 is FALSE, so blocked
|
|
expect(eligibility.eligibleSportIds).not.toContain("nfl");
|
|
expect(eligibility.ineligibleReasons["nfl"]).toContain(
|
|
"Would prevent other teams from getting NFL"
|
|
);
|
|
expect(eligibility.sportAvailability["nfl"].canDraftAsFlex).toBe(false);
|
|
});
|
|
|
|
it("allows flex pick when extra participants available", () => {
|
|
const teams = [createTeam("team1"), createTeam("team2"), createTeam("team3")];
|
|
const participants = [
|
|
createParticipant("p1", "nfl", "NFL"),
|
|
createParticipant("p2", "nfl", "NFL"),
|
|
createParticipant("p3", "nfl", "NFL"),
|
|
createParticipant("p4", "nfl", "NFL"), // Extra NFL participant
|
|
createParticipant("p5", "nba", "NBA"),
|
|
createParticipant("p6", "nhl", "NHL"),
|
|
];
|
|
|
|
const allPicks = [createPick("team1", "p1", "nfl", "NFL")];
|
|
const team1Picks = [createPick("team1", "p1", "nfl", "NFL")];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
team1Picks,
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 3 NFL participants left, 2 teams still need NFL
|
|
// 3 > 2, so flex pick allowed
|
|
expect(eligibility.eligibleSportIds).toContain("nfl");
|
|
expect(eligibility.sportAvailability["nfl"].canDraftAsFlex).toBe(true);
|
|
});
|
|
|
|
it("handles case where all teams have drafted from a sport", () => {
|
|
const teams = [createTeam("team1"), createTeam("team2")];
|
|
const participants = [
|
|
createParticipant("p1", "nfl", "NFL"),
|
|
createParticipant("p2", "nfl", "NFL"),
|
|
createParticipant("p3", "nfl", "NFL"),
|
|
];
|
|
|
|
const allPicks = [
|
|
createPick("team1", "p1", "nfl", "NFL"),
|
|
createPick("team2", "p2", "nfl", "NFL"),
|
|
];
|
|
const team1Picks = [createPick("team1", "p1", "nfl", "NFL")];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
team1Picks,
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 0 teams still need NFL, 1 participant left
|
|
// 1 > 0, so flex allowed
|
|
expect(eligibility.sportAvailability["nfl"].teamsStillNeeding).toBe(0);
|
|
expect(eligibility.sportAvailability["nfl"].canDraftAsFlex).toBe(true);
|
|
expect(eligibility.eligibleSportIds).toContain("nfl");
|
|
});
|
|
});
|
|
|
|
describe("Combined Constraints", () => {
|
|
it("requires both constraints to be satisfied", () => {
|
|
const teams = Array.from({ length: 5 }, (_, i) => createTeam(`team${i + 1}`));
|
|
const participants = [
|
|
...Array.from({ length: 6 }, (_, i) => createParticipant(`nfl${i}`, "nfl", "NFL")),
|
|
...Array.from({ length: 6 }, (_, i) => createParticipant(`nba${i}`, "nba", "NBA")),
|
|
...Array.from({ length: 6 }, (_, i) => createParticipant(`nhl${i}`, "nhl", "NHL")),
|
|
];
|
|
|
|
// Team 1 has used all flex picks (2) with 3 NFL picks
|
|
// 4 teams haven't drafted NFL yet, 3 NFL participants remain
|
|
const allPicks = [
|
|
createPick("team1", "nfl1", "nfl", "NFL"),
|
|
createPick("team1", "nfl2", "nfl", "NFL"),
|
|
createPick("team1", "nfl3", "nfl", "NFL"),
|
|
createPick("team1", "nba1", "nba", "NBA"),
|
|
createPick("team1", "nhl1", "nhl", "NHL"),
|
|
];
|
|
const team1Picks = allPicks;
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
team1Picks,
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
5, // 5 rounds, 3 sports = 2 flex picks
|
|
teams
|
|
);
|
|
|
|
// Flex picks exhausted
|
|
expect(eligibility.flexPicksUsed).toBe(2);
|
|
expect(eligibility.flexPicksAvailable).toBe(2);
|
|
|
|
// Even though NFL has 3 > 4 (global constraint would block flex),
|
|
// team can't draft NFL because flex picks are exhausted
|
|
expect(eligibility.eligibleSportIds).toEqual(new Set([]));
|
|
expect(eligibility.ineligibleReasons["nfl"]).toContain("All flex picks used");
|
|
expect(eligibility.ineligibleReasons["nba"]).toContain("All flex picks used");
|
|
expect(eligibility.ineligibleReasons["nhl"]).toContain("All flex picks used");
|
|
});
|
|
|
|
it("allows pick when both constraints satisfied", () => {
|
|
const teams = [createTeam("team1"), createTeam("team2")];
|
|
const participants = [
|
|
...Array.from({ length: 4 }, (_, i) => createParticipant(`nfl${i}`, "nfl", "NFL")),
|
|
...Array.from({ length: 4 }, (_, i) => createParticipant(`nba${i}`, "nba", "NBA")),
|
|
...Array.from({ length: 4 }, (_, i) => createParticipant(`nhl${i}`, "nhl", "NHL")),
|
|
];
|
|
|
|
const allPicks = [
|
|
createPick("team1", "nfl1", "nfl", "NFL"),
|
|
createPick("team1", "nba1", "nba", "NBA"),
|
|
];
|
|
const team1Picks = allPicks;
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
team1Picks,
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// Has flex picks remaining (0/2 used)
|
|
expect(eligibility.flexPicksUsed).toBe(0);
|
|
|
|
// 3 NFL left, 1 team needs (team2), 3 > 1 so flex allowed
|
|
expect(eligibility.sportAvailability["nfl"].undraftedCount).toBe(3);
|
|
expect(eligibility.sportAvailability["nfl"].teamsStillNeeding).toBe(1);
|
|
expect(eligibility.sportAvailability["nfl"].canDraftAsFlex).toBe(true);
|
|
|
|
// Both constraints satisfied for NFL
|
|
expect(eligibility.eligibleSportIds).toContain("nfl");
|
|
});
|
|
});
|
|
|
|
describe("Edge Cases", () => {
|
|
it("handles single sport league", () => {
|
|
const singleSport = [createSport("nfl", "NFL")];
|
|
const teams = [createTeam("team1"), createTeam("team2")];
|
|
const participants = Array.from({ length: 10 }, (_, i) =>
|
|
createParticipant(`p${i}`, "nfl", "NFL")
|
|
);
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
[],
|
|
[],
|
|
participants,
|
|
singleSport,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 5 rounds - 1 sport = 4 flex picks
|
|
expect(eligibility.flexPicksAvailable).toBe(4);
|
|
expect(eligibility.eligibleSportIds).toContain("nfl");
|
|
});
|
|
|
|
it("handles case with no participants left in sport", () => {
|
|
const teams = [createTeam("team1"), createTeam("team2")];
|
|
const participants = [
|
|
createParticipant("p1", "nfl", "NFL"),
|
|
createParticipant("p2", "nba", "NBA"),
|
|
createParticipant("p3", "nhl", "NHL"),
|
|
];
|
|
|
|
const allPicks = [
|
|
createPick("team1", "p1", "nfl", "NFL"),
|
|
];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
[createPick("team1", "p1", "nfl", "NFL")],
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// Only 1 NFL participant total, 1 drafted = 0 left
|
|
expect(eligibility.sportAvailability["nfl"].undraftedCount).toBe(0);
|
|
expect(eligibility.sportAvailability["nfl"].canDraftAsFlex).toBe(false);
|
|
});
|
|
|
|
it("blocks first pick when not enough participants for all teams", () => {
|
|
const teams = Array.from({ length: 5 }, (_, i) => createTeam(`team${i + 1}`));
|
|
const participants = [
|
|
createParticipant("p1", "nfl", "NFL"),
|
|
createParticipant("p2", "nfl", "NFL"),
|
|
createParticipant("p3", "nfl", "NFL"), // Only 3 NFL for 5 teams
|
|
createParticipant("p4", "nba", "NBA"),
|
|
createParticipant("p5", "nhl", "NHL"),
|
|
];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
[],
|
|
[],
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 3 participants, 5 teams need it, 3 < 5
|
|
expect(eligibility.sportAvailability["nfl"].canDraftAsFirst).toBe(false);
|
|
expect(eligibility.eligibleSportIds).not.toContain("nfl");
|
|
expect(eligibility.ineligibleReasons["nfl"]).toContain(
|
|
"Only 3 participants left for 5 teams"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("getEligibilitySummary", () => {
|
|
it("provides readable summary", () => {
|
|
const teams = [createTeam("team1")];
|
|
const participants = [
|
|
createParticipant("p1", "nfl", "NFL"),
|
|
createParticipant("p2", "nba", "NBA"),
|
|
];
|
|
|
|
const teamPicks = [
|
|
createPick("team1", "p1", "nfl", "NFL"),
|
|
createPick("team1", "p2", "nba", "NBA"),
|
|
];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
teamPicks,
|
|
teamPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
const summary = getEligibilitySummary(eligibility);
|
|
expect(summary).toContain("Flex picks: 0/2");
|
|
expect(summary).toContain("NFL: 1");
|
|
expect(summary).toContain("NBA: 1");
|
|
});
|
|
});
|
|
|
|
describe("Exact Boundary Conditions", () => {
|
|
it("allows first pick when undrafted exactly equals teams needing", () => {
|
|
const teams = Array.from({ length: 3 }, (_, i) => createTeam(`team${i + 1}`));
|
|
const participants = [
|
|
createParticipant("nfl1", "nfl", "NFL"),
|
|
createParticipant("nfl2", "nfl", "NFL"),
|
|
createParticipant("nfl3", "nfl", "NFL"), // Exactly 3 for 3 teams
|
|
createParticipant("nba1", "nba", "NBA"),
|
|
];
|
|
|
|
// No picks yet - all 3 teams need NFL
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
[],
|
|
[],
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 3 undrafted, 3 teams need it: 3 >= 3 is TRUE
|
|
expect(eligibility.sportAvailability["nfl"].undraftedCount).toBe(3);
|
|
expect(eligibility.sportAvailability["nfl"].teamsStillNeeding).toBe(3);
|
|
expect(eligibility.sportAvailability["nfl"].canDraftAsFirst).toBe(true);
|
|
expect(eligibility.eligibleSportIds).toContain("nfl");
|
|
});
|
|
|
|
it("blocks first pick when undrafted is less than teams needing", () => {
|
|
const teams = Array.from({ length: 4 }, (_, i) => createTeam(`team${i + 1}`));
|
|
const participants = [
|
|
createParticipant("nfl1", "nfl", "NFL"),
|
|
createParticipant("nfl2", "nfl", "NFL"),
|
|
createParticipant("nfl3", "nfl", "NFL"), // Only 3 for 4 teams
|
|
createParticipant("nba1", "nba", "NBA"),
|
|
];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
[],
|
|
[],
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 3 undrafted, 4 teams need it: 3 >= 4 is FALSE
|
|
expect(eligibility.sportAvailability["nfl"].undraftedCount).toBe(3);
|
|
expect(eligibility.sportAvailability["nfl"].teamsStillNeeding).toBe(4);
|
|
expect(eligibility.sportAvailability["nfl"].canDraftAsFirst).toBe(false);
|
|
expect(eligibility.eligibleSportIds).not.toContain("nfl");
|
|
});
|
|
|
|
it("allows flex pick when undrafted is exactly one more than teams needing", () => {
|
|
const teams = Array.from({ length: 3 }, (_, i) => createTeam(`team${i + 1}`));
|
|
const participants = [
|
|
...Array.from({ length: 4 }, (_, i) => createParticipant(`nfl${i}`, "nfl", "NFL")), // 4 NFL
|
|
createParticipant("nba1", "nba", "NBA"),
|
|
];
|
|
|
|
// Team1 has already picked NFL, teams 2 and 3 haven't
|
|
const allPicks = [createPick("team1", "nfl1", "nfl", "NFL")];
|
|
const team1Picks = [createPick("team1", "nfl1", "nfl", "NFL")];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
team1Picks,
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 3 undrafted, 2 teams need it: 3 > 2 is TRUE (exactly one extra)
|
|
expect(eligibility.sportAvailability["nfl"].undraftedCount).toBe(3);
|
|
expect(eligibility.sportAvailability["nfl"].teamsStillNeeding).toBe(2);
|
|
expect(eligibility.sportAvailability["nfl"].canDraftAsFlex).toBe(true);
|
|
expect(eligibility.eligibleSportIds).toContain("nfl");
|
|
});
|
|
|
|
it("blocks flex pick when undrafted equals teams needing", () => {
|
|
const teams = Array.from({ length: 3 }, (_, i) => createTeam(`team${i + 1}`));
|
|
const participants = [
|
|
...Array.from({ length: 3 }, (_, i) => createParticipant(`nfl${i}`, "nfl", "NFL")), // 3 NFL
|
|
createParticipant("nba1", "nba", "NBA"),
|
|
];
|
|
|
|
// Team1 has already picked NFL, teams 2 and 3 haven't
|
|
const allPicks = [createPick("team1", "nfl1", "nfl", "NFL")];
|
|
const team1Picks = [createPick("team1", "nfl1", "nfl", "NFL")];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
team1Picks,
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 2 undrafted, 2 teams need it: 2 > 2 is FALSE (exactly equal)
|
|
expect(eligibility.sportAvailability["nfl"].undraftedCount).toBe(2);
|
|
expect(eligibility.sportAvailability["nfl"].teamsStillNeeding).toBe(2);
|
|
expect(eligibility.sportAvailability["nfl"].canDraftAsFlex).toBe(false);
|
|
expect(eligibility.eligibleSportIds).not.toContain("nfl");
|
|
});
|
|
|
|
it("blocks all sports when flex picks exactly exhausted", () => {
|
|
const teams = [createTeam("team1")];
|
|
const participants = [
|
|
...Array.from({ length: 5 }, (_, i) => createParticipant(`nfl${i}`, "nfl", "NFL")),
|
|
...Array.from({ length: 5 }, (_, i) => createParticipant(`nba${i}`, "nba", "NBA")),
|
|
...Array.from({ length: 5 }, (_, i) => createParticipant(`nhl${i}`, "nhl", "NHL")),
|
|
];
|
|
|
|
// 5 rounds, 3 sports = 2 flex picks
|
|
// Use exactly 2 flex picks: 3 NFL, 1 NBA (2 NFL duplicates = 2 flexes)
|
|
const teamPicks = [
|
|
createPick("team1", "nfl1", "nfl", "NFL"),
|
|
createPick("team1", "nfl2", "nfl", "NFL"), // Flex 1
|
|
createPick("team1", "nfl3", "nfl", "NFL"), // Flex 2
|
|
createPick("team1", "nba1", "nba", "NBA"),
|
|
];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
teamPicks,
|
|
teamPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// Exactly at flex limit
|
|
expect(eligibility.flexPicksUsed).toBe(2);
|
|
expect(eligibility.flexPicksAvailable).toBe(2);
|
|
|
|
// Can only draft NHL (not yet picked)
|
|
expect(eligibility.eligibleSportIds).toEqual(new Set(["nhl"]));
|
|
expect(eligibility.ineligibleReasons["nfl"]).toContain("All flex picks used");
|
|
expect(eligibility.ineligibleReasons["nba"]).toContain("All flex picks used");
|
|
});
|
|
});
|
|
|
|
describe("Draft Progression Simulation", () => {
|
|
it("shows eligibility changing as teams make sequential picks", () => {
|
|
const teams = Array.from({ length: 3 }, (_, i) => createTeam(`team${i + 1}`));
|
|
const participants = [
|
|
...Array.from({ length: 4 }, (_, i) => createParticipant(`nfl${i}`, "nfl", "NFL")),
|
|
...Array.from({ length: 4 }, (_, i) => createParticipant(`nba${i}`, "nba", "NBA")),
|
|
...Array.from({ length: 4 }, (_, i) => createParticipant(`nhl${i}`, "nhl", "NHL")),
|
|
];
|
|
|
|
// Start: No picks yet
|
|
let allPicks: any[] = [];
|
|
|
|
// Team1 picks NFL
|
|
allPicks = [createPick("team1", "nfl1", "nfl", "NFL")];
|
|
let team2Eligibility = calculateDraftEligibility(
|
|
"team2",
|
|
[],
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 3 NFL left, 2 teams need it (team2, team3): 3 >= 2, should allow
|
|
expect(team2Eligibility.sportAvailability["nfl"].undraftedCount).toBe(3);
|
|
expect(team2Eligibility.sportAvailability["nfl"].teamsStillNeeding).toBe(2);
|
|
expect(team2Eligibility.eligibleSportIds).toContain("nfl");
|
|
|
|
// Team2 picks NFL
|
|
allPicks = [
|
|
createPick("team1", "nfl1", "nfl", "NFL"),
|
|
createPick("team2", "nfl2", "nfl", "NFL"),
|
|
];
|
|
let team3Eligibility = calculateDraftEligibility(
|
|
"team3",
|
|
[],
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 2 NFL left, 1 team needs it (team3): 2 >= 1, should allow
|
|
expect(team3Eligibility.sportAvailability["nfl"].undraftedCount).toBe(2);
|
|
expect(team3Eligibility.sportAvailability["nfl"].teamsStillNeeding).toBe(1);
|
|
expect(team3Eligibility.eligibleSportIds).toContain("nfl");
|
|
|
|
// Team3 picks NFL
|
|
allPicks = [
|
|
createPick("team1", "nfl1", "nfl", "NFL"),
|
|
createPick("team2", "nfl2", "nfl", "NFL"),
|
|
createPick("team3", "nfl3", "nfl", "NFL"),
|
|
];
|
|
|
|
// Now team1 wants a second NFL (flex pick)
|
|
let team1Eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
[createPick("team1", "nfl1", "nfl", "NFL")],
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// 1 NFL left, 0 teams need it: 1 > 0, should allow flex
|
|
expect(team1Eligibility.sportAvailability["nfl"].undraftedCount).toBe(1);
|
|
expect(team1Eligibility.sportAvailability["nfl"].teamsStillNeeding).toBe(0);
|
|
expect(team1Eligibility.sportAvailability["nfl"].canDraftAsFlex).toBe(true);
|
|
expect(team1Eligibility.eligibleSportIds).toContain("nfl");
|
|
});
|
|
});
|
|
|
|
describe("Late-Draft Forced Choices", () => {
|
|
it("forces team into specific sport when its the only eligible option", () => {
|
|
const teams = [createTeam("team1")];
|
|
const participants = [
|
|
...Array.from({ length: 3 }, (_, i) => createParticipant(`nfl${i}`, "nfl", "NFL")),
|
|
...Array.from({ length: 3 }, (_, i) => createParticipant(`nba${i}`, "nba", "NBA")),
|
|
...Array.from({ length: 3 }, (_, i) => createParticipant(`nhl${i}`, "nhl", "NHL")),
|
|
];
|
|
|
|
// 5 rounds, 3 sports, team has picked 4 times (used all flexes)
|
|
// Picked: 2 NFL, 2 NBA, need NHL
|
|
const teamPicks = [
|
|
createPick("team1", "nfl1", "nfl", "NFL"),
|
|
createPick("team1", "nfl2", "nfl", "NFL"),
|
|
createPick("team1", "nba1", "nba", "NBA"),
|
|
createPick("team1", "nba2", "nba", "NBA"),
|
|
];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team1",
|
|
teamPicks,
|
|
teamPicks,
|
|
participants,
|
|
sports,
|
|
5,
|
|
teams
|
|
);
|
|
|
|
// Only NHL is eligible (not yet drafted, and flex picks exhausted)
|
|
expect(eligibility.eligibleSportIds).toEqual(new Set(["nhl"]));
|
|
expect(eligibility.flexPicksUsed).toBe(2);
|
|
expect(eligibility.flexPicksAvailable).toBe(2);
|
|
expect(eligibility.picksBySport).toEqual({ nfl: 2, nba: 2 });
|
|
});
|
|
|
|
it("handles complex late-draft scenario with multiple teams", () => {
|
|
const teams = Array.from({ length: 4 }, (_, i) => createTeam(`team${i + 1}`));
|
|
const participants = [
|
|
...Array.from({ length: 5 }, (_, i) => createParticipant(`nfl${i}`, "nfl", "NFL")),
|
|
...Array.from({ length: 5 }, (_, i) => createParticipant(`nba${i}`, "nba", "NBA")),
|
|
...Array.from({ length: 5 }, (_, i) => createParticipant(`nhl${i}`, "nhl", "NHL")),
|
|
];
|
|
|
|
// Late draft: most participants drafted
|
|
const allPicks = [
|
|
// Team1: 2 NFL, 2 NBA
|
|
createPick("team1", "nfl1", "nfl", "NFL"),
|
|
createPick("team1", "nfl2", "nfl", "NFL"),
|
|
createPick("team1", "nba1", "nba", "NBA"),
|
|
createPick("team1", "nba0", "nba", "NBA"),
|
|
// Team2: 2 NBA, 1 NFL
|
|
createPick("team2", "nba2", "nba", "NBA"),
|
|
createPick("team2", "nba3", "nba", "NBA"),
|
|
createPick("team2", "nfl3", "nfl", "NFL"),
|
|
// Team3: 2 NHL, 1 NFL, 1 NBA
|
|
createPick("team3", "nhl1", "nhl", "NHL"),
|
|
createPick("team3", "nhl2", "nhl", "NHL"),
|
|
createPick("team3", "nfl4", "nfl", "NFL"),
|
|
createPick("team3", "nba4", "nba", "NBA"),
|
|
// Team4: 0 picks yet
|
|
];
|
|
|
|
const team4Picks: any[] = [];
|
|
|
|
const eligibility = calculateDraftEligibility(
|
|
"team4",
|
|
team4Picks,
|
|
allPicks,
|
|
participants,
|
|
sports,
|
|
4, // 4 rounds
|
|
teams
|
|
);
|
|
|
|
// Team4 hasn't drafted anything yet
|
|
// NFL: 1 left, 1 team needs (team4): 1 >= 1, eligible
|
|
// NBA: 0 left, 1 team needs: 0 >= 1 is FALSE, BLOCKED
|
|
// NHL: 3 left, 1 team needs: 3 >= 1, eligible
|
|
|
|
expect(eligibility.sportAvailability["nfl"].undraftedCount).toBe(1);
|
|
expect(eligibility.sportAvailability["nfl"].teamsStillNeeding).toBe(1);
|
|
expect(eligibility.sportAvailability["nba"].undraftedCount).toBe(0);
|
|
expect(eligibility.sportAvailability["nba"].teamsStillNeeding).toBe(1);
|
|
|
|
// Should be able to draft NFL or NHL, but NOT NBA
|
|
expect(eligibility.eligibleSportIds).toContain("nfl");
|
|
expect(eligibility.eligibleSportIds).toContain("nhl");
|
|
expect(eligibility.eligibleSportIds).not.toContain("nba");
|
|
});
|
|
});
|
|
});
|