Skip to content

test: cover the coverage gate's signal-killed exit path - #1048

Open
hivecommons-hive[bot] wants to merge 1 commit into
mainfrom
quality/test-coverage-gate-signal-kill
Open

hivecommons-hive[bot] wants to merge 1 commit into
mainfrom
quality/test-coverage-gate-signal-kill

Conversation

@hivecommons-hive

Copy link
Copy Markdown
Contributor

Test Improvement

Adds one case to tests/coverage-report-cli.test.mjs covering the exit path
tests/tools/coverage-report.mjs:680 takes when the spawned unit run did not
exit normally.

spawnSync reports a child terminated by a signal as status: null, not as a
number, and process.exit(null) exits 0. Without the ?? 1 fallback the
reporter prints "Tests failed; coverage above is reported for context." and
then reports success — so a run cut short by the OOM killer or a job timeout
would satisfy npm run test:unit:coverage:check on no evidence, leaving only a
stderr line nobody reads on a green job.

The existing sibling case covers a suite that fails and exits 1. That status is
numeric and never reaches the fallback, so deleting ?? 1 leaves the suite
green today.

The new case drives the reporter over a generated suite that SIGKILLs
node --test itself. Three details make it deterministic:

  • process.ppid inside a test file is the runner the reporter spawned, one
    level below the reporter, so the suite kills the runner without touching the
    process under test.
  • SIGKILL rather than SIGTERM: the runner handles SIGTERM and exits 1, which is
    the numeric path already covered.
  • The killed runner flushes no profile and its orphan dies with it, so the
    suite calls v8.takeCoverage() before the kill. Otherwise the reporter stops
    at No coverage data was recorded. and never reaches the path under test —
    which also exits 1, so the assertion excludes it explicitly.

Verification

  • node --test tests/coverage-report-cli.test.mjs — 4/4 pass.
  • Mutation check: replacing result.status ?? 1 with result.status fails the
    new case and only that case; restoring it passes. Run both ways locally on
    node v26.10.0.
  • npm run test:unit:coverage moves tests/tools/coverage-report.mjs from
    99.63% regions (sole uncovered region: line 680) to 100.00% lines /
    100.00% regions.
  • npm run test:unit:coverage:check exits 0.
  • npx prettier --check tests/coverage-report-cli.test.mjs clean.

Test-only: no file outside tests/ is touched.

Related Issue

Closes #1047

Overlap

Touches tests/coverage-report-cli.test.mjs only. Disjoint from the open
hold-gated PRs: #1040 (tests/tools/e2e-coverage-report.mjs,
tests/tools/e2e-coverage-run.mjs, tests/tools/e2e-coverage-scripts.mjs,
tests/e2e-coverage-report.test.mjs, tests/e2e-coverage-run.test.mjs),
#1034 (tests/e2e-data-fixtures.test.mjs, tests/e2e/data-variants.spec.js),
#1042 (tests/svg-active-content.test.mjs), #1039 and #1044.

— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88

spawnSync reports a child terminated by a signal as status: null, and
process.exit(null) exits 0, so without the ?? 1 fallback at
tests/tools/coverage-report.mjs:680 the reporter prints "Tests failed"
and then reports success. A unit run cut short by the OOM killer or a
job timeout would satisfy npm run test:unit:coverage:check on no
evidence.

The existing sibling case covers a suite that fails and exits 1, which
is numeric and never reaches the fallback. This drives the reporter over
a suite that SIGKILLs node --test itself -- process.ppid inside a test
file is the runner, one level below the reporter -- and asserts the
reporter still exits 1 through the "Tests failed" path.

SIGKILL rather than SIGTERM because the runner handles SIGTERM and exits
1. The killed runner flushes no profile and its orphan dies with it, so
the suite calls v8.takeCoverage() first; otherwise the reporter stops at
"No coverage data was recorded.", which also exits 1, so the assertion
excludes it.

Closes #1047

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: quality <quality@hive.kubestellar.io>
@hivecommons-hive hivecommons-hive Bot added the hold label Oct 4, 2026
@hivecommons-hive

Copy link
Copy Markdown
Contributor Author

Important

Held for human review by the hive's ACMM level gate.

This PR was opened by the "quality" agent while Hive policy required a human checkpoint for that agent. Non-outreach agents are held at ACMM L3–L5; the outreach agent is always held because it publishes project-facing communication.

Hive will automatically remove the hold label once current policy no longer requires a level hold for "quality". If this is an outreach PR, a human must review it and remove the label.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[quality] coverage gate exits 0 when the test run is killed by a signal (tests/tools/coverage-report.mjs:680 ?? 1 is untested)

0 participants