Skip to content

fix(manual-upload): await validation tasks before test teardown - #67

Closed
tnunamak wants to merge 2 commits into
mainfrom
fix/manual-upload-validation-race
Closed

fix(manual-upload): await validation tasks before test teardown#67
tnunamak wants to merge 2 commits into
mainfrom
fix/manual-upload-validation-race

Conversation

@tnunamak

@tnunamak tnunamak commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Manual-upload tests could remove their files and switch databases while upload validation was still running, allowing one test's writes to affect a later test.

The server now exposes each scheduled validation promise through an optional callback. The upload test harness retains these handles and awaits all of them before closing the server or deleting its directory, including when an assertion fails. Waiting for the promise includes filesystem cleanup that can continue after the database status becomes terminal. Teardown has no deadline that silently releases unfinished work. Unexpected validation exceptions reject the handle after cleanup and fail the test; rejected file content still produces a failed upload record that success assertions reject.

Status polling now throws on its wall-clock deadline. The roughly 200 MiB streamed-upload test allows four minutes for its status assertion. Its name and comments now describe the measured fixture size.

Verification: all 22 tests passed in each of twelve sequential full-file runs (264 passes, zero failures), including ten runs on the final source. A delayed-cleanup regression holds actual filesystem cleanup open after the upload record says failed and checks that teardown keeps the directory. An injected file-move error verifies that teardown reports the original validation error and still removes the directory. Static type checks for server and browser test code passed.

The callback changes server code, but lifetime ownership is enabled only by this test harness; default server shutdown does not drain validation tasks. These runs use the embedded SQLite database and a roughly 200 MiB fixture. They do not establish behavior with the PostgreSQL database backend, acceptance above 1 GiB, or a memory-growth bound.

Assisted-by: AI

The manual-upload route answers 202 and validates in a detached background
task (setImmediate(() => validateAndStageArtifact(...)), see
server/routes/ref-manual-upload-draft-connection.ts:1528) that nothing owns,
awaits, or cancels. Two failures on a loaded host followed from that.

waitForArtifact budgeted by attempt count (maxAttempts = 400), not wall
clock. Each attempt costs an HTTP round trip plus a 25 ms sleep, and both
stretch under CPU contention, so the budget shrank exactly when validation
needed longer. On exhaustion it returned the last response instead of
failing, so the large-upload test saw 'validating' where it asserted
'staged'.

The second failure followed from the first. On exhaustion the test's finally
closed the server and removed the temp dir while validation was still
running. Store calls inside that task re-resolve the module-scoped getDb()
handle at call time (server/db.ts:278), and starting the next server
re-points that variable, so the leaked task wrote its connector_instances row
into a later test's database. That is the off-by-one row count, and it is why
the victim moved between runs (tests 7, 14 and 19 observed locally; 8 and 15
in the referenced report).

Budget by wall clock and throw on exhaustion, so a timeout fails the test
that actually timed out. Drain artifacts to a terminal status before
teardown, so a slow validation cannot write into the next test's database.

Test-only; no production code changes. Under a concurrent full suite on a
24-core host at load average ~200: 6 of 7 runs failed before, 10 of 10 clean
after.

The underlying concern -- an unowned background task writing through a
mutable getDb() singleton that shutdown deliberately does not drain -- is
production-shaped and left for a separate change.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
@tnunamak tnunamak changed the title test(manual-upload): own the background validation task so tests stop leaking into each other test(manual-upload): own the background validation task in tests Sep 9, 2026
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
Assisted-by: AI
@tnunamak tnunamak changed the title test(manual-upload): own the background validation task in tests fix(manual-upload): await validation tasks before test teardown Sep 10, 2026
@tnunamak

tnunamak commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Already on main; closing as landed.

Assisted-by: AI

@tnunamak tnunamak closed this Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant