The project’s pool.test.js is part of the course, not decoration. It verifies bounded starts and ordered results using explicit gates, first-failure behavior while a peer is held, cancellation before and during work, invalid limits, source errors, cleanup and undefined causes.
Run npm test in the extracted folder on Node.js 22+. Node’s built-in test(name, callback) groups each case; assert.deepEqual compares complete values and assert.rejects checks awaited failures. These are the same contract ideas practised in the console helpers, with a standard reporting tool.
Mutate only one behavior at a time. Replace indexed assignment with push, skip iterator.return, or remove the no-new-work guard. The appropriate test should fail. Restore the source before continuing. A test that fails for a syntax error does not prove it detects the behavioral defect; the mutation should remain a runnable program.
Predict, run, and explain
Write the output and trace the important state changes before checking it. Then run the program. Each editor is independent. Async examples end with main() so this editor waits for their work.
async function main() {
let release;
const gate = new Promise(resolve => {
release = resolve;
});
let settled = false;
const work = gate.then(() => {
settled = true;
});
console.log(settled);
release();
await work;
console.log(settled);
}
main();Check your prediction
false
true Match a test to the guarantee it distinguishes
A result-order mutation needs reverse completion; a cleanup mutation needs an unfinished iterator; a start-guard mutation needs more work available after failure. Ordinary completion cannot distinguish all three. Read fixtures as controlled collaborators that expose public behavior, not as snapshots of private implementation details.
| Boundary or case | What to inspect |
|---|---|
| Bounded starts | Observe peak work and refill before a held peer completes |
| First failure | Observe source closure and no new pulls while peers settle |
| External signal | Preserve caller reason and release its listener |
Predict the failed assertion before changing the maintained implementation. If your change causes a syntax error or hangs, that is not evidence of the intended regression test. Restore source, confirm the baseline, and only then try another mutation.
Your turn
Tests & Failure Lab: practise
Write a runtime assertion showing a generator closes on early break. Its finally sets closed=true. After breaking after one value, assert closed and print cleanup verified.
// Keep the assertion outside the iterator cleanup itself.Hint 1: the idea
The public observation is that cleanup happened before the consuming loop finished.
Hint 2: a first step
Initialize closed=false outside the generator and set it in finally.
Show one solution
async function main() {
let closed = false;
function* values() {
try {
yield 1;
yield 2;
} finally {
closed = true;
}
}
for (const value of values()) {
if (value === 1) {
break;
}
}
if (!closed) {
throw new Error("Cleanup missing");
}
console.log("cleanup verified");
}
main();cleanup verifiedExplain why the implementation meets the stated contract. Which part is enforced by the language or checker, and which part needs behavioral evidence?
Change it and test it
Run all downloaded tests before and after each mutation. Identify the exact test that catches missing cleanup and the one that catches completion-order results. Add a separate caller-cancellation test if you change the signal contract.
Completion is saved in this browser only. It does not measure mastery.