diff --git a/plugins/multipart/test/dynamic-option.test.ts b/plugins/multipart/test/dynamic-option.test.ts index 770e8e15a5..edfb8b109a 100644 --- a/plugins/multipart/test/dynamic-option.test.ts +++ b/plugins/multipart/test/dynamic-option.test.ts @@ -21,7 +21,7 @@ describe('test/dynamic-option.test.ts', () => { }); afterAll(async () => { - await fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true }); + await fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true, maxRetries: 3 }); }); afterAll(() => app.close()); afterAll(() => server.close()); diff --git a/plugins/multipart/test/enable-pathToRegexpModule.test.ts b/plugins/multipart/test/enable-pathToRegexpModule.test.ts index 88b2ffe543..fb5fe2fe18 100644 --- a/plugins/multipart/test/enable-pathToRegexpModule.test.ts +++ b/plugins/multipart/test/enable-pathToRegexpModule.test.ts @@ -23,7 +23,7 @@ describe.skip('test/enable-pathToRegexpModule.test.ts', () => { host = 'http://127.0.0.1:' + server.address().port; }); afterAll(async () => { - await fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true }); + await fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true, maxRetries: 3 }); }); afterAll(() => app.close()); afterAll(() => server.close()); diff --git a/plugins/multipart/test/file-mode-limit-filesize-per-request.test.ts b/plugins/multipart/test/file-mode-limit-filesize-per-request.test.ts index 9f82f78f4a..0f38283c06 100644 --- a/plugins/multipart/test/file-mode-limit-filesize-per-request.test.ts +++ b/plugins/multipart/test/file-mode-limit-filesize-per-request.test.ts @@ -22,7 +22,7 @@ describe('test/file-mode-limit-filesize-per-request.test.ts', () => { host = 'http://127.0.0.1:' + server.address().port; }); afterAll(async () => { - await fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true }); + await fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true, maxRetries: 3 }); }); afterAll(() => app.close()); afterAll(() => server.close()); diff --git a/plugins/multipart/test/file-mode.test.ts b/plugins/multipart/test/file-mode.test.ts index f419482052..7d5952afad 100644 --- a/plugins/multipart/test/file-mode.test.ts +++ b/plugins/multipart/test/file-mode.test.ts @@ -28,7 +28,7 @@ describe('test/file-mode.test.ts', () => { host = 'http://127.0.0.1:' + server.address().port; }); afterAll(async () => { - await fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true }); + await fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true, maxRetries: 3 }); }); afterAll(() => app.close()); afterAll(() => server.close()); diff --git a/plugins/multipart/test/stream-mode-with-filematch-glob.test.ts b/plugins/multipart/test/stream-mode-with-filematch-glob.test.ts index cae967574f..cf8da45de9 100644 --- a/plugins/multipart/test/stream-mode-with-filematch-glob.test.ts +++ b/plugins/multipart/test/stream-mode-with-filematch-glob.test.ts @@ -26,7 +26,7 @@ describe('test/stream-mode-with-filematch-glob.test.ts', () => { }); afterAll(async () => { try { - await fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true }); + await fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true, maxRetries: 3 }); } catch (err) { console.error(err); } diff --git a/plugins/multipart/test/stream-mode-with-filematch.test.ts b/plugins/multipart/test/stream-mode-with-filematch.test.ts index 7dd35e67e3..da7134659c 100644 --- a/plugins/multipart/test/stream-mode-with-filematch.test.ts +++ b/plugins/multipart/test/stream-mode-with-filematch.test.ts @@ -26,7 +26,7 @@ describe('test/stream-mode-with-filematch.test.ts', () => { }); afterAll(async () => { try { - await fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true }); + await fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true, maxRetries: 3 }); } catch (err) { console.error(err); } diff --git a/plugins/multipart/test/ts.test.ts b/plugins/multipart/test/ts.test.ts index 487db2366e..5308441027 100644 --- a/plugins/multipart/test/ts.test.ts +++ b/plugins/multipart/test/ts.test.ts @@ -25,7 +25,7 @@ describe('test/ts.test.ts', () => { host = 'http://127.0.0.1:' + server.address().port; }); afterAll(() => { - return fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true }); + return fs.rm(app.config.multipart.tmpdir, { force: true, recursive: true, maxRetries: 3 }); }); afterAll(() => app.close()); afterAll(() => server.close()); diff --git a/tools/scripts/test/stop.test.ts b/tools/scripts/test/stop.test.ts index 9f30b74622..eb9c62d2ff 100644 --- a/tools/scripts/test/stop.test.ts +++ b/tools/scripts/test/stop.test.ts @@ -8,7 +8,7 @@ import { mm, restore } from 'mm'; import { describe, it, beforeAll, afterAll, beforeEach, afterEach, expect } from 'vitest'; import { isWindows } from '../src/helper.ts'; -import { cleanup, replaceWeakRefMessage, type Coffee } from './utils.ts'; +import { cleanup, replaceWeakRefMessage, waitFor, type Coffee } from './utils.ts'; const __dirname = import.meta.dirname; @@ -45,7 +45,9 @@ describe('test/stop.test.ts', () => { ]) as Coffee; app.debug(); app.expect('code', 0); - await scheduler.wait(waitTime); + // Poll until the app reports it has started instead of a fixed delay: a 2-worker + // egg app can take well over a second to boot on a loaded CI runner. + await waitFor(() => app.stdout, /custom-framework started on http:\/\/127\.0\.0\.1:\d+/); expect(replaceWeakRefMessage(app.stderr)).toBe(''); expect(app.stdout).toMatch(/custom-framework started on http:\/\/127\.0\.0\.1:\d+/); @@ -155,7 +157,8 @@ describe('test/stop.test.ts', () => { ]) as Coffee; // app.debug(); app.expect('code', 0); - await scheduler.wait(waitTime); + // Poll until the app reports it has started (see note above). + await waitFor(() => app.stdout, /custom-framework started on http:\/\/127\.0\.0\.1:\d+/); expect(replaceWeakRefMessage(app.stderr)).toBe(''); expect(app.stdout).toMatch(/custom-framework started on http:\/\/127\.0\.0\.1:\d+/); @@ -314,7 +317,8 @@ describe('test/stop.test.ts', () => { app.debug(); app.expect('code', 0); - await scheduler.wait(waitTime); + // Poll until the app reports it has started (see note above). + await waitFor(() => app.stdout, /http:\/\/127\.0\.0\.1:\d+/); // assert.equal(replaceWeakRefMessage(app.stderr), ''); expect(app.stdout).toMatch(/http:\/\/127\.0\.0\.1:\d+/); @@ -332,7 +336,13 @@ describe('test/stop.test.ts', () => { killer.debug(); killer.expect('code', 0); await killer.end(); - await scheduler.wait(waitTime); + // Poll until the app logs its shutdown instead of a fixed delay. On Windows the + // SIGTERM signal is not handled (no shutdown log), so keep a fixed settle there. + if (isWindows) { + await scheduler.wait(waitTime); + } else { + await waitFor(() => app.stdout, /\[master] master is killed by signal SIGTERM, closing/); + } // make sure is kill not auto exist expect(app.stdout).not.toMatch(/exist by env/); @@ -356,7 +366,13 @@ describe('test/stop.test.ts', () => { killer.expect('code', 0); // await killer.end(); - await scheduler.wait(waitTime); + // Poll until the app has fully exited (master exits last, after workers/agent), + // instead of a fixed delay. On Windows the signal is not handled, so settle fixed. + if (isWindows) { + await scheduler.wait(waitTime); + } else { + await waitFor(() => app.stdout, /\[master] exit with code:0/); + } // make sure is kill not auto exist expect(app.stdout).not.toMatch(/exist by env/); diff --git a/tools/scripts/test/utils.ts b/tools/scripts/test/utils.ts index 79ffaf6867..4d491d3242 100644 --- a/tools/scripts/test/utils.ts +++ b/tools/scripts/test/utils.ts @@ -55,3 +55,19 @@ export function replaceWeakRefMessage(stderr: string) { } return stderr; } + +/** + * Poll until `getText()` matches `pattern`, up to `timeout` ms (default 10s), + * checking every 100ms. Returns as soon as it matches, and resolves anyway on + * timeout so the caller's own `expect(...).toMatch(...)` still produces a useful + * diff. Use this instead of a fixed `scheduler.wait(n)` before asserting on a + * forked process's stdout: a loaded CI runner may not have finished booting (or + * shutting down) within `n`, which is the classic source of these flaky timeouts. + */ +export async function waitFor(getText: () => string, pattern: RegExp, timeout = 10000): Promise { + const deadline = Date.now() + timeout; + while (!pattern.test(getText())) { + if (Date.now() >= deadline) return; + await scheduler.wait(100); + } +}