Withdrawn teams forfeit visibly through the bracket instead of becoming byes

A withdrawn team stays in any slot whose opponent is not known yet and forfeits
when they arrive; the board strikes the name through and tags the match
'Forfeit · Team'. Release 0.5.1.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MB7nCCAscYsb3zzkT6LHZi
This commit is contained in:
bkvargyasandClaude Fable 5.1 committed 2026-09-05 15:50:49 +00:00
1 parent a4bee22d96
commit 4ce7e48551
8 files changed
+58 -10

No files matched your search

+26
View File
@@ -216,3 +216,29 @@ test('manual bracket: hand-picked round-1 pairs land in the chosen matches', ()
assert.throws(() => t.generateBracketFromPairs([[a, a], [b, c], [d, e], [f, null]]), /placed twice/);
assert.throws(() => t.generateBracketFromPairs([[a, b], [c, d], [e, null], [null, null]]), /Not placed/);
});
test('withdrawal mid-bracket shows as forfeits (not byes) and play continues to a champion', () => {
const t = setup(8, { stages: ['bracket'] });
t.closeRegistration();
t.generateBracket(ids(t), { type: 'double' });
let clock = 0;
const eng = new CourtEngine(t, { now: () => clock });
eng.start();
// play two matches, then withdraw a team that is live on a court
for (let i = 0; i < 2; i++) { const c = eng.courts.find(c => c.matchId); clock += 10 * 60_000; eng.recordResult(c.matchId, [[21, 10]]); }
const liveMatch = t.match(eng.courts.find(c => c.matchId).matchId);
const quitter = liveMatch.teamA;
eng.withdrawTeam(quitter, { mode: 'forfeit' });
// the live match is now a forfeit by the quitter, and any later match they land in forfeits too
assert.equal(liveMatch.status, 'forfeit'); assert.equal(liveMatch.loser, quitter);
let guard = 0;
while (t.phase === 'live' && guard++ < 100) {
const c = eng.courts.find(c => c.matchId); assert.ok(c, 'stalled');
clock += 10 * 60_000; eng.recordResult(c.matchId, [[21, 10]]);
}
assert.equal(t.phase, 'final');
const theirs = t.matches.filter(m => [m.teamA, m.teamB].includes(quitter) && m.stage !== 'pool');
assert.ok(theirs.length >= 2, 'quitter appears in WB and LB');
assert.ok(theirs.every(m => m.status === 'forfeit' && m.loser === quitter), 'every match of the quitter is a forfeit against them');
assert.equal(t.matches.filter(m => m.status === 'bye' && m.round > 1).length, 0, 'no silent byes created by the withdrawal');
});