From e00959dfb63c765bbbb2f9bdbf59664f3f51eb47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Magyar?= Date: Thu, 23 Apr 2026 20:11:54 +0100 Subject: [PATCH] test(gitnexus): stabilize rel-csv-split stream teardown on Windows (expect.poll) (#1052) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test(lbug): stabilize rel-csv-split Windows CI with expect.poll Fixed sleeps assumed readline had already created the first mock stream within 20ms; windows-latest can lag, causing streams.length===0 and ENOTEMPTY tempdir cleanup. Poll up to 10s instead (Vitest 4). Refs #1051 Made-with: Cursor * test(lbug): use exact toBe assertions in rel-csv-split (DoD §2.7) - Poll for streams.length === 2 after unblock (two pair keys only) - disk-full test: streams.length === 1 for single Function|Class row Made-with: Cursor * test(lbug): replace rel-csv-split setTimeout waits with expect.poll Shared pollOpts; drain-listener and disk-full tests now wait on streams.length instead of fixed 50ms sleeps (DoD §2.7 deterministic tests). Made-with: Cursor --- gitnexus/test/unit/rel-csv-split.test.ts | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/gitnexus/test/unit/rel-csv-split.test.ts b/gitnexus/test/unit/rel-csv-split.test.ts index 5b0a81199..dedc94602 100644 --- a/gitnexus/test/unit/rel-csv-split.test.ts +++ b/gitnexus/test/unit/rel-csv-split.test.ts @@ -106,6 +106,8 @@ function writeCsv(lines: string[]): string { // --------------------------------------------------------------------------- describe('splitRelCsvByLabelPair', () => { const validTables = new Set(['Function', 'Class', 'File', 'Method']); + /** Bounded poll for readline + split loop to reach an observable milestone (DoD §2.7). */ + const pollOpts = { interval: 10, timeout: 10_000 } as const; it('splits lines into per-pair files with correct row counts', async () => { const csvPath = writeCsv([ @@ -197,8 +199,8 @@ describe('splitRelCsvByLabelPair', () => { mockFactory(streams, { blocked: true }), ); - // Give readline time to buffer and fire lines - await new Promise((r) => setTimeout(r, 50)); + // All rows share Function|Class — one stream, blocked on header or row drain + await expect.poll(() => streams.length, pollOpts).toBe(1); // Unblock all streams so the Promise can resolve for (const ws of streams) ws.unblock(); @@ -222,9 +224,7 @@ describe('splitRelCsvByLabelPair', () => { mockFactory(streams, { blocked: true }), ); - // Wait for readline to process, then error while paused on drain - await new Promise((r) => setTimeout(r, 50)); - expect(streams.length).toBeGreaterThan(0); + await expect.poll(() => streams.length, pollOpts).toBe(1); streams[0].triggerError(new Error('disk full')); await expect(promise).rejects.toThrow('disk full'); @@ -247,14 +247,12 @@ describe('splitRelCsvByLabelPair', () => { mockFactory(streams, { blocked: true }), ); - // The first pair stream is created immediately and blocks on its header - // write. Unblock it once so the loop advances and creates the second - // pair stream (also blocked). Now both streams exist — trigger the error. - await new Promise((r) => setTimeout(r, 20)); - expect(streams.length).toBe(1); + // First pair stream once readline delivered a row; poll avoids Windows CI races. + await expect.poll(() => streams.length, pollOpts).toBe(1); streams[0].unblock(); - await new Promise((r) => setTimeout(r, 20)); - expect(streams.length).toBeGreaterThanOrEqual(2); + // Exactly two pair keys before the third CSV row: Function|Class then + // File|Method; the loop is blocked on the second stream's header drain. + await expect.poll(() => streams.length, pollOpts).toBe(2); streams[0].triggerError(new Error('EMFILE')); await expect(promise).rejects.toThrow('EMFILE');