65 lines
2.5 KiB
TypeScript
65 lines
2.5 KiB
TypeScript
|
|
import { describe, expect, it, vi } from "vitest";
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The Expected Values admin page renders EV from the stored probability columns.
|
|||
|
|
*
|
|||
|
|
* It used to carry its own hardcoded scoring table (100/70/45/45/20/20/20/20), which
|
|||
|
|
* flattened positions 5–8 to 20 points each. For a standard single-elimination bracket
|
|||
|
|
* that was invisible — all four quarterfinal losers share one tier worth
|
|||
|
|
* avg(25,25,15,15) = 20 anyway — but for the templates that split 5–8 into two tiers
|
|||
|
|
* (llws_20, afl_10) it reported a team locked into 5th–6th and a team locked into
|
|||
|
|
* 7th–8th as the same 20 points. These pin it to the shared DEFAULT_SCORING_RULES.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
vi.mock("../admin.sports-seasons.$id.expected-values.server", () => ({
|
|||
|
|
loader: vi.fn(),
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
import { evFromProbs } from "../admin.sports-seasons.$id.expected-values";
|
|||
|
|
|
|||
|
|
const ZERO = {
|
|||
|
|
probFirst: "0", probSecond: "0", probThird: "0", probFourth: "0",
|
|||
|
|
probFifth: "0", probSixth: "0", probSeventh: "0", probEighth: "0",
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
describe("evFromProbs", () => {
|
|||
|
|
it("gives a team locked into the 5th–6th tier 25 points, not 20", () => {
|
|||
|
|
expect(evFromProbs({ ...ZERO, probFifth: "0.5", probSixth: "0.5" })).toBe(25);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("gives a team locked into the 7th–8th tier 15 points, not 20", () => {
|
|||
|
|
expect(evFromProbs({ ...ZERO, probSeventh: "0.5", probEighth: "0.5" })).toBe(15);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("still gives a single 5th–8th tier (4 QF losers) 20 points", () => {
|
|||
|
|
const ev = evFromProbs({
|
|||
|
|
...ZERO,
|
|||
|
|
probFifth: "0.25", probSixth: "0.25", probSeventh: "0.25", probEighth: "0.25",
|
|||
|
|
});
|
|||
|
|
expect(ev).toBe(20);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("keeps 3rd and 4th distinct rather than a flat 45 each", () => {
|
|||
|
|
expect(evFromProbs({ ...ZERO, probThird: "1" })).toBe(50);
|
|||
|
|
expect(evFromProbs({ ...ZERO, probFourth: "1" })).toBe(40);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("preserves the 340 total-EV invariant across a full set of unit columns", () => {
|
|||
|
|
const perPosition = [
|
|||
|
|
evFromProbs({ ...ZERO, probFirst: "1" }),
|
|||
|
|
evFromProbs({ ...ZERO, probSecond: "1" }),
|
|||
|
|
evFromProbs({ ...ZERO, probThird: "1" }),
|
|||
|
|
evFromProbs({ ...ZERO, probFourth: "1" }),
|
|||
|
|
evFromProbs({ ...ZERO, probFifth: "1" }),
|
|||
|
|
evFromProbs({ ...ZERO, probSixth: "1" }),
|
|||
|
|
evFromProbs({ ...ZERO, probSeventh: "1" }),
|
|||
|
|
evFromProbs({ ...ZERO, probEighth: "1" }),
|
|||
|
|
];
|
|||
|
|
expect(perPosition.reduce((sum, ev) => sum + ev, 0)).toBe(340);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("returns 0 for a participant with no probability mass", () => {
|
|||
|
|
expect(evFromProbs(ZERO)).toBe(0);
|
|||
|
|
});
|
|||
|
|
});
|