-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
test: fix flaky CI tests (multipart tmpdir teardown + egg-scripts start readiness) #6015
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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()); | ||||||||||||||||||||||||
|
Comment on lines
25
to
29
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Vitest, Reordering the hooks so that
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, but this rests on an incorrect premise about Vitest. Vitest's default |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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()); | ||||||||||||||||||||||||
|
Comment on lines
24
to
28
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Vitest, Reordering the hooks so that
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, but this rests on an incorrect premise about Vitest. Vitest's default |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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()); | ||||||||||||||||||||||||
|
Comment on lines
30
to
34
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Vitest, Reordering the hooks so that
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, but this rests on an incorrect premise about Vitest. Vitest's default |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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()); | ||||||||||||||||||||||||
|
Comment on lines
27
to
31
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Vitest, Reordering the hooks so that
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, but this rests on an incorrect premise about Vitest. Vitest's default |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<void> { | ||
| const deadline = Date.now() + timeout; | ||
| while (!pattern.test(getText())) { | ||
|
Comment on lines
+67
to
+69
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
rg -nP --type=ts 'waitFor\s*\([^,]+,\s*/(?:\\.|[^/])+/[a-z]*[gy][a-z]*' tools/scripts/testRepository: eggjs/egg Length of output: 147 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== utils.ts ==\n'
sed -n '1,180p' tools/scripts/test/utils.ts
printf '\n== waitFor call sites ==\n'
rg -n --type=ts 'waitFor\s*\(' tools/scripts/testRepository: eggjs/egg Length of output: 3423 Reset regex state before polling. 🧰 Tools🪛 ast-grep (0.44.0)[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec. (detect-child-process-typescript) 🤖 Prompt for AI Agents |
||
| if (Date.now() >= deadline) return; | ||
| await scheduler.wait(100); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Vitest,
afterAllhooks are executed sequentially in the order they are registered. Currently, the temporary directory removal (fs.rm) is registered beforeapp.close()andserver.close(). This means the test suite attempts to delete the temporary directory while the server and application are still active and potentially holding open file handles or writing files, which is the primary cause of the flakyENOTEMPTY/EBUSYerrors.Reordering the hooks so that
server.close()andapp.close()run first ensures that all network activity has ceased and all file handles are released beforefs.rmis executed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, but this rests on an incorrect premise about Vitest. Vitest's default
sequence.hooksis'stack', soafterAllhooks run in reverse registration order — verified empirically on v4.1.9 (3rd-registered runs first, 1st-registered last). The current order therefore already executesserver.close()→app.close()→fs.rm(...): the tmpdir is removed last, after the server and app are closed. Applying the suggested reorder would movefs.rmto run first, reintroducing exactly the race described. The residualENOTEMPTYis a transient FS-level race during the recursive removal (macOS APFS / async per-request file cleanup draining), whichmaxRetries: 3handles. Keeping the current order + maxRetries.