fix(#838): bound broker reply deadlines + fail-close empty-read; de-flake acceptance harness #839

Merged
jason.woltje merged 7 commits from fix/838-broker-acceptance-flake into main 2026-07-18 07:15:51 +00:00
Showing only changes of commit 6f0f8eecdc - Show all commits

View File

@@ -24,8 +24,13 @@ async function scriptedBroker(
connections += 1;
socket.once('end', () => {
if (chunks === 'hang') return;
for (const chunk of chunks) socket.write(chunk);
socket.end();
void (async () => {
for (const chunk of chunks) {
socket.write(chunk);
await new Promise<void>((resolve) => setTimeout(resolve, 5));
}
socket.end();
})();
});
socket.resume();
});
@@ -105,6 +110,40 @@ describe('newline-framed broker test client', () => {
expect(broker.connections()).toBe(1);
});
test('rejects trailing bytes delivered after a complete frame in a later data event', async () => {
const broker = await scriptedBroker([[Buffer.from('{"ok":true}\n'), Buffer.from('extra')]]);
const failure = await requestBrokerReply(broker.socketPath, { action: 'probe' }).catch(
(error: unknown) => error,
);
expect(failure).toBeInstanceOf(BrokerTransportError);
expect(failure).toMatchObject({ kind: 'malformed-reply', responseLength: 17 });
expect(failure).toHaveProperty(
'message',
expect.stringMatching(/bytes after the newline terminator/i),
);
});
test('redacts security tokens from typed transport diagnostics', async () => {
const token = 'a'.repeat(64);
const reply = Buffer.from(`{"ok":true,"promotion_token":"${token}`);
const broker = await scriptedBroker([[reply]]);
const failure = await requestBrokerReply(broker.socketPath, { action: 'probe' }).catch(
(error: unknown) => error,
);
expect(failure).toBeInstanceOf(BrokerTransportError);
expect(failure).toMatchObject({
kind: 'early-close',
responseLength: reply.length,
responsePreview: '<redacted-sensitive-reply>',
});
expect(String((failure as Error).message)).not.toContain(token);
expect(JSON.stringify(failure)).not.toContain(token);
});
test.each([
['trailing bytes', Buffer.from('{"ok":true}\nextra'), /bytes after the newline/i],
['non-object JSON', Buffer.from('[]\n'), /JSON value is not an object/i],