mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-12 11:49:28 +00:00
fix(career): bound the prepare request; validate the setlist (PR #971 review)
Both CodeRabbit findings were right. 1. A HUNG PREPARE COULD BLOCK THE GIG FOREVER. `await fetch(...)` only rejects on a network ERROR. A server that accepts the connection and then never answers hangs indefinitely — and the gig would never start. That makes this optimisation the exact thing the PR promises it can never be: the reason you cannot play. The request is now bounded by an AbortController (PREPARE_TIMEOUT_MS, generous because unpacking a setlist is real work — but a CEILING, not a wait). Past it we start the gig and let the first play extract lazily, as it always did. The Play button is restored in a `finally`, so a timeout cannot strand the poster on "Preparing set…" with Play disabled — which would have been the same bug wearing a different hat. 2. THE `songs` BODY WAS UNVALIDATED. A str is iterable: "abc" would have prepared three one-character "songs". And the endpoint unpacks zips, so an arbitrary caller could ask for unbounded work. Now list-only, string entries, blanks dropped, capped at MAX_GIG_SONGS. Tests: the fetch is abortable and the button is re-enabled on EVERY path including the abort; non-list bodies, non-string/blank entries, and an oversized setlist. 50 career tests, JS 5/5, eslint clean.
This commit is contained in:
@@ -75,3 +75,24 @@ test('the prepare route degrades instead of failing', () => {
|
||||
'a host without the library resolvers must degrade, not 500 — pre-extraction ' +
|
||||
'is an optimisation and can never be why a gig will not start');
|
||||
});
|
||||
|
||||
// ── the prepare must never be able to BLOCK the gig (CodeRabbit, #971) ──────
|
||||
//
|
||||
// A bare `await fetch(...)` only rejects on a network error. A server that
|
||||
// accepts the connection and then never answers hangs forever — and the gig
|
||||
// would never start. That would make this optimisation the exact thing it
|
||||
// promises never to be: the reason you cannot play.
|
||||
|
||||
test('the prepare fetch is bounded — a hung server cannot block the gig', () => {
|
||||
const fn = extractBlock(CAREER, 'async function prepareGigSongs(');
|
||||
assert.match(fn, /AbortController/, 'the request must be abortable');
|
||||
assert.match(fn, /setTimeout\([\s\S]{0,40}abort\s*\(\s*\)/,
|
||||
'a hung request must be aborted, not awaited forever');
|
||||
assert.match(fn, /signal:\s*ctrl\.signal/, 'the signal must actually be passed to fetch');
|
||||
assert.match(fn, /clearTimeout/, 'the timer must be cleared on the happy path');
|
||||
assert.match(CAREER, /const\s+PREPARE_TIMEOUT_MS\s*=\s*\d+/, 'the ceiling must be named');
|
||||
// The button must be restored however we leave — otherwise a timeout strands
|
||||
// the poster on "Preparing set…" with Play disabled: unplayable.
|
||||
assert.match(fn, /finally\s*\{[\s\S]{0,220}btn\.disabled\s*=\s*false/,
|
||||
'the Play button must be re-enabled on EVERY path, including the abort');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user