Conversation
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
b8a505d to
0f2384d
Compare
| const doTest = async () => { | ||
| await servingReady; | ||
| try { | ||
| execSync("cucumber-js", { | ||
| cwd: projectRoot, | ||
| stdio: "inherit", | ||
| }); | ||
| // will throw on failure | ||
| } finally { | ||
| if (serve.pid) process.kill(-serve.pid); | ||
| } | ||
| }; |
There was a problem hiding this comment.
🟡 Detached serve process never killed when servingReady rejects (timeout or early close)
In serve_and_test.ts, await servingReady at line 59 is outside the inner try/finally block (lines 60-68). If the servingReady promise rejects — most notably when the 30-second timeout fires at line 34-36, but the serve process is still running — the finally block containing process.kill(-serve.pid) is never reached. The rejection propagates directly to the .catch() handler at line 76 which calls process.exit(1) without killing the detached child. Because the child was spawned with detached: true (line 21), it survives the parent's exit and continues running as an orphaned process. In local development this leaves a lingering supabase functions serve process that holds the port.
| const doTest = async () => { | |
| await servingReady; | |
| try { | |
| execSync("cucumber-js", { | |
| cwd: projectRoot, | |
| stdio: "inherit", | |
| }); | |
| // will throw on failure | |
| } finally { | |
| if (serve.pid) process.kill(-serve.pid); | |
| } | |
| }; | |
| const doTest = async () => { | |
| try { | |
| await servingReady; | |
| execSync("cucumber-js", { | |
| cwd: projectRoot, | |
| stdio: "inherit", | |
| }); | |
| // will throw on failure | |
| } finally { | |
| if (serve.pid) process.kill(-serve.pid); | |
| } | |
| }; |
Was this helpful? React with 👍 or 👎 to provide feedback.
| }); | ||
| // will throw on failure | ||
| } finally { | ||
| if (serve.pid) process.kill(-serve.pid); |
There was a problem hiding this comment.
The process.kill(-serve.pid) call can throw an error if the process has already exited, which will mask any error from the test execution since this is in a finally block. This should be wrapped in a try-catch:
finally {
if (serve.pid) {
try {
process.kill(-serve.pid);
} catch (e) {
// Process already exited, ignore
}
}
}| if (serve.pid) process.kill(-serve.pid); | |
| if (serve.pid) { | |
| try { | |
| process.kill(-serve.pid); | |
| } catch (e) { | |
| // Process already exited, ignore | |
| } | |
| } |
Spotted by Graphite
Is this helpful? React 👍 or 👎 to let us know.
| const serve = spawn("supabase", ["functions", "serve"], { | ||
| cwd: projectRoot, | ||
| detached: true, | ||
| }); |
There was a problem hiding this comment.
🔴 Unconsumed stderr pipe can deadlock the supabase serve child process
The serve child process is spawned with the default stdio: 'pipe' for all streams (serve_and_test.ts:19-22). While stdout has a 'data' listener that drains it, stderr has no listener at all. If supabase functions serve writes more than the OS pipe buffer size (~64 KB on Linux) to stderr before writing "Serving functions" to stdout, the child process will block on its stderr write, never produce the expected stdout output, and the script will hit the 30-second timeout. This is a well-known Node.js child-process pitfall. The fix is to either add serve.stderr.resume() to drain stderr, pipe stderr to 'inherit', or set it to 'ignore'.
| const serve = spawn("supabase", ["functions", "serve"], { | |
| cwd: projectRoot, | |
| detached: true, | |
| }); | |
| const serve = spawn("supabase", ["functions", "serve"], { | |
| cwd: projectRoot, | |
| detached: true, | |
| stdio: ["ignore", "pipe", "inherit"], | |
| }); |
Was this helpful? React with 👍 or 👎 to provide feedback.
| }); | ||
| // will throw on failure | ||
| } finally { | ||
| if (serve.pid) process.kill(-serve.pid); |
There was a problem hiding this comment.
🔴 process.kill(-serve.pid) in finally block throws if process group already exited, masking test results
If the supabase functions serve process exits before process.kill(-serve.pid) is called at line 67, the call throws an ESRCH error. Because this is in a finally block, the thrown error replaces whatever result doTest() would have returned. Critically, if all tests pass but the serve process happens to have exited already, the ESRCH error propagates to the .catch() handler at line 76-79, which calls process.exit(1) — causing CI to report failure despite passing tests.
| if (serve.pid) process.kill(-serve.pid); | |
| try { if (serve.pid) process.kill(-serve.pid); } catch { /* already exited */ } |
Was this helpful? React with 👍 or 👎 to provide feedback.
ccb043f to
a28e59d
Compare
https://linear.app/discourse-graphs/issue/ENG-681/automate-database-testing