test(cli): cover restore.ts HTTP branches and the repair-path purge notice - #306
Conversation
|
@claude /review |
|
Claude finished @thecodedrift's task in 3m 47s —— View job Review complete
Review mode: incremental — read 0 prior review thread(s) before reviewing. Overall this is solid coverage work: the mutation-check table in the PR body is a real strength, and I traced each new assertion against One real issue found: [New] Missing root-skip guard on the two new permission-based purge-failure testsFile: Both new tests force // deliver.test.ts:543-546
// Skipped as root, which ignores the permission bits the failure is staged
// with. The same convention as `vale-verify.test.ts`.
const asUser = process.getuid?.() === 0 ? describe.skip : describe;// vale-verify.test.ts:151-152
// Root ignores the mode bits, so there is no unreadable directory to make.
const asUser = process.getuid?.() === 0 ? describe.skip : describe;The two new tests in this PR use the exact same This repo's GitHub Actions CI ( Minor / non-blocking
No other correctness, security, or test-adequacy issues found. The HTTP-contract tests for |
74db8ea to
730c56d
Compare
… notice restoreRule() (packages/cli/src/api/restore.ts) had five branches with no coverage: 401 -> unauthorized, a thrown fetch -> unavailable, a non-ok status -> unavailable, an unparsable body -> unavailable, and a `rules` field that is not an array -> unavailable. Each is now driven directly against a bare HTTP server, since restore-integration.test.ts only exercised the happy path via the built CLI. Also covers two branches in rules/runtime/plan.ts's planRuntime that the existing repair-integration suite never forced to fire: reconcile's own `unauthorized` outcome, and materializeRuntimeRules throwing (via a .taskless/.gitignore blocked as a directory). And it covers the PurgeIncompleteError wording plan.ts's repair path renders on a restore that wrote the rule but left a stale file behind -- singular and plural -- which deliver.test.ts could only prove in isolation on the raw error. Every added assertion was mutation-checked against a matching source change (flipped 401, dropped Array.isArray guard, removed try/catches, swapped plural/singular wording) and confirmed to fail before the source was reverted; see the PR description for the mutation table.
730c56d to
9d70ae5
Compare
|
Clean review, thanks. The HTTP fixture binds an ephemeral port on loopback ( — AI Coding Agent |
Stack (root → tip):
Summary
Closes part of #284: two named coverage gaps in the runtime-rule repair path.
1.
packages/cli/src/api/restore.ts:96-118(five HTTP branches, zero coverage).Added a
restoreRule's HTTP contract" describe block inrepair-integration.test.tsthat drivesrestoreRuledirectly against a barehttpserver (bypassing the built-CLI subprocess, since these are unit-shaped questions about one function's mapping from HTTP outcome toRestoreOutcome):unauthorizedunavailable/HTTP 500response.json()throwing on an unparsable body →unavailable/invalid response bodyrulesfield →unavailable/response carried no `rules`fetchitself rejecting (network error) →unavailable/ message containingnetwork errorAlso added, in the existing CLI-integration describe block:
planRuntime'sunauthorizedbranch (plan.ts) — reconcile itself returning 401, distinct from restore's 401 — asserted via the--jsonenvelope'sskippedfield.planRuntime's materialize-failure catch — forced by replacing.taskless/.gitignorewith a directory after fixture migration, somaterializeRuntimeRules→addToGitignore→writeFilethrowsEISDIR. Asserts the run still exits 0 and reports the rule as skipped rather than failing.2.
PurgeIncompleteError's repair-path message (plan.ts'srepairWithheldRules).deliver.test.tsalready proved the rawPurgeIncompleteError(message +.failures) in isolation. Added two integration tests that force a restore to write the blessed rule but leave a stale, unremovable file behind (achmod 0o500directory), and assert on the rendered notice plan.ts builds around the error — singular ("1 stale entry ... reads it") and plural ("2 stale entries ... reads them") — which the existing suite never exercised.Mutation-check table
Every assertion was verified to fail against the corresponding regression, then the source was reverted (
git diffwas empty againstmainfor all non-test files at the end — onlypackages/cli/test/repair-integration.test.tschanged).unauthorizedresponse.status === 401→=== 402unavailable/HTTP 401unavailable!response.ok→falseunavailable/response carried no \rules`` (fell through)response.json()cannot parse tounavailabletry/catcharoundresponse.json()SyntaxErrorinstead of resolvingrulestounavailableArray.isArray(rules)guard{status: "ok", rules: undefined}unavailabletry/catcharoundfetch(...)TypeError: fetch failed) instead of resolvingoutcome.status === "unauthorized"→=== "nope-not-it"output.skippedwasundefined(run crashed toSCAN_FAILEDinstead)try/catcharoundmaterializeRuntimeRules(...)exitCodewas1instead of0length === 1 ? "entries" : "entry"etc.)Notes / findings
No production defect found; all mutations behaved as documented and production code is unchanged (only the test file is diffed from
main).The issue's line numbers for
plan.ts(129-135, 139-148, 170-181, 280-289) point a little differently in currentmain(the same branches now sit around 165-176, 182-192, and 329-341) — no discrepancy in behavior, just line drift since the issue was filed.Test plan
NODE_OPTIONS= pnpm test— 1334 tests pass (up from 1328)NODE_OPTIONS= pnpm typecheck— cleanNODE_OPTIONS= pnpm lint— cleanNo changeset: tests only, no user-visible behavior change.
Refs #284