Skip to content

Commit 6801836

Browse files
authored
Merge pull request #294 from bbopen/fix/0.9-mixed-concurrency-flake
fix(test): replace flapping it.fails mixed-concurrency test with deterministic invariants
2 parents 3b3e04c + e2c4eb9 commit 6801836

1 file changed

Lines changed: 18 additions & 8 deletions

File tree

test/adversarial_playground.test.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,26 +144,36 @@ describeAdversarial('Adversarial playground', () => {
144144
testTimeoutMs
145145
);
146146

147-
it.fails(
148-
'handles mixed concurrency with a timeout and a fast success',
147+
it(
148+
'settles every call loudly when a timeout interleaves with a queued call',
149149
async () => {
150-
const bridge = await createBridge({ timeoutMs: 250 });
150+
// Whether the queued fast call starves into its own timeout or completes
151+
// once the worker frees up depends on scheduler timing under the serial
152+
// pool default — both are loud, and asserting one side made this test
153+
// flap between environments (it passed on fast CI runners while marked
154+
// it.fails from local behavior). The deterministic invariants: the slow
155+
// call times out, the fast call settles either way with a real
156+
// value/error, and the bridge stays usable afterwards.
157+
const bridge = await createBridge({ timeoutMs: 4_000 });
151158
if (!bridge) return;
152159

153160
try {
154-
const slow = callAdversarial(bridge, 'sleep_and_return', ['slow', 0.5]);
161+
const slow = callAdversarial(bridge, 'sleep_and_return', ['slow', 6.0]);
155162
const fast = callAdversarial(bridge, 'echo', ['fast']);
156163
const results = await Promise.allSettled([slow, fast]);
157164

158-
// Known limitation: the subprocess bridge is serial, so a slow call can
159-
// starve subsequent calls and time them out.
160165
expect(results[0].status).toBe('rejected');
161-
expect(results[1].status).toBe('fulfilled');
166+
if (results[0].status === 'rejected') {
167+
expect(String(results[0].reason)).toMatch(/timed out/i);
168+
}
162169
if (results[1].status === 'fulfilled') {
163170
expect(results[1].value).toBe('fast');
171+
} else {
172+
expect(String(results[1].reason)).toMatch(/timed out/i);
164173
}
165174

166-
await delay(600);
175+
// Why: let the slow worker drain (or restart) before probing recovery.
176+
await delay(6_000);
167177
const again = await callAdversarial(bridge, 'echo', ['after-timeout']);
168178
expect(again).toBe('after-timeout');
169179
} finally {

0 commit comments

Comments
 (0)