Fix participantsRemaining not counting non-scoring-round eliminations (#161)

Participants eliminated in a non-scoring bracket round (e.g. Round of 16
in a 16-team bracket) receive finalPosition=0 and isPartialScore=false.
The previous calculateTeamScore loop only entered participantsCompleted++
when finalPosition > 0, so these participants were silently treated as
still "remaining" in the standings table — causing e.g. a team with one
eliminated participant to show "25 remaining" instead of "24 remaining".

Fix: add an else-if branch that increments participantsCompleted for any
finalized result (isPartialScore=false) regardless of finalPosition value.
The condition is intentionally broader than `=== 0` to also cover the
theoretical case of finalPosition=null with isPartialScore=false.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Chris Parsons 2026-03-17 15:02:19 -07:00 committed by GitHub
parent 7236c8aefb
commit dfaf1f0589
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 17 additions and 0 deletions

View file

@ -269,6 +269,20 @@ describe("calculateTeamScore", () => {
expect(result.placementCounts[5]).toBe(1); expect(result.placementCounts[5]).toBe(1);
}); });
it("eliminated in non-scoring round (finalPosition=0) counts as completed", async () => {
// Round of 16 loser: 0 pts, fully eliminated (isPartialScore=false)
const db = makeDb([
makePick("p1", "ss1", "playoff_bracket", { finalPosition: 0, isPartialScore: false }),
]);
const result = await calculateTeamScore("team1", "season1", db);
expect(result.totalPoints).toBe(0); // no points for pre-bracket elimination
expect(result.participantsCompleted).toBe(1); // done — should not count as remaining
expect(result.participantsTotal).toBe(1);
expect(result.placementCounts[1]).toBe(0); // no placement badge
});
it("mixed team: partial counts in totalPoints only; finalized counts everywhere", async () => { it("mixed team: partial counts in totalPoints only; finalized counts everywhere", async () => {
// p1: finalized 1st → totalPoints +100, placementCounts[1]++, completed++ // p1: finalized 1st → totalPoints +100, placementCounts[1]++, completed++
// p2: partial pos 5 (floor 20) → totalPoints +20, no placement, not completed // p2: partial pos 5 (floor 20) → totalPoints +20, no placement, not completed

View file

@ -890,6 +890,9 @@ export async function calculateTeamScore(
placementCounts[result.finalPosition]++; placementCounts[result.finalPosition]++;
} }
} }
} else if (result && !result.isPartialScore) {
// Finalized with no scoring position (e.g. eliminated in a non-scoring round) — still counts as done
participantsCompleted++;
} }
} }