Repository navigation
[Hackathon] fix(test): honor --dry-run in test scaffold #454
Description
Verified against (commit SHA or released version)
commit 988c035
Discord Username / User ID
hacker18283737
What does this improvement do?
Problem. testsprite test scaffold --dry-run --out <path> performs real filesystem writes. The global --dry-run contract promises no writes — DOCUMENTATION.md ("--dry-run is a global flag that emits a canned sample matching the API contract without writes or charges"; the Global flags table: "These apply to every command: --dry-run | Preview without writes or charges"), and the CHANGELOG's original definition ("every command runs end-to-end without touching the network, credentials, or the local filesystem"). runScaffold in src/commands/test.ts (the --out branch, ~L6146–6159 at 988c035) never reads opts.dryRun: it resolves the path, then opens the output sink, writes the body, and renames it onto the target — exactly as in normal mode — and prints Scaffold written to <path>.
Impact. Verified against a clean build of main at the SHA above, in a throwaway temp directory:
testsprite test scaffold --dry-run --out fresh.plan.json→ createsfresh.plan.json(exit 0) and reportsScaffold written to …— an unexpected side effect under a no-write flag.testsprite test scaffold --dry-run --force --out existing.json→ overwrites an existing file's contents (sentinel bytes replaced by the scaffold) — silent data loss under--dry-run.testsprite test scaffold --dry-run --out existing.json(no--force) → exits 5 (already exists: … Pass --force to overwrite) instead of previewing.- Control (sibling, same flag plumbing):
testsprite test code get test_xxx --dry-run --out x.pyprints[dry-run] would write code body (693 bytes) to x.pyand creates nothing.
Why this is a bug and not intended. The sibling commands that write user-content files all preview under --dry-run and write nothing: test code get --out (its code comment: "Dry-run: no fetch, no fs…"), test failure get --out (its comment on a "written to" message under dry-run: "would be a lie here"), test artifact get --out, and the pure-local ci init github ("no files changed"). test scaffold was added later (CHANGELOG 0.3.0) without the guard; no test covers scaffold × dry-run anywhere. One deliberate dry-run file write does exist as a documented carve-out — --report junit --report-file writes a canned diagnostic sidecar (CHANGELOG 0.3.0) — but that is a changelog-documented, CI-sidecar artifact, whereas --out is the user's authored-content destination whose normal-mode contract explicitly says "Never clobber silently." Scaffold's dry-run write is undocumented, unprotected, and untested, and its success message is false under the flag's contract.
Expected behavior. Under --dry-run, test scaffold --out should preview (stderr line like [dry-run] would write scaffold (<n> bytes) to <path>, stdout mirroring the no---out mode) and create/modify nothing.
Details / implementation notes
Scope. runScaffold in src/commands/test.ts — specifically the --out branch only (no --out dry-run is already side-effect-free). Distinct from:
- [Hackathon] fix(ci):
ci connect --dry-runreally runsvercel link/vercel git connect(andci doctor --dry-runcallsgh api) #372 (ci connect / ci doctor): same flag-ignored pattern, different file/functions (ci.tsrunCiConnect/runCiDoctor), different side-effect class (subprocess spawn vs filesystem write), disjoint acceptance criteria and tests. - [Hackathon] fix(test): test scaffold uses a blank/whitespace TESTSPRITE_PROJECT_ID instead of the placeholder #383 / PR fix(test): normalize TESTSPRITE_PROJECT_ID in test scaffold #384 (TESTSPRITE_PROJECT_ID placeholder in scaffold): same command, different defect — that PR's scope is the env-var normalization ~L6096–6102, ~44 lines above this branch; worth a maintainer glance for overlap (also feat(test): add "test export" / "test import" to round-trip test DEFINITIONS for version control #265/feat(test): add export and import for backend definitions #417/fix(project): guard credential/auto-auth secret file reads (#282) #283 on the watch-list).
Suggested acceptance criteria (one concern; the guard belongs before the write, mirroring test code get):
--dry-run --out <nonexistent>creates no file (red today).--dry-run --force --out <existing>leaves the existing file's bytes untouched (red today).- Dry-run reports a truthful preview (e.g.
[dry-run] would write scaffold (<n> bytes) to <path>), neverScaffold written to …; stdout mirrors the no---outmode (text: scaffold body;--output json: the same JSON payload as without--out). - Design choice to confirm: with an existing target and no
--force, dry-run currently exits 5 (already exists… Pass --force). Recommended: preview with exit 0, matchingtest code get's documented rationale ("in dry-run the point is 'show me the shape, no side effects'") andci init's guard placement; the alternative (keep the validation error under dry-run, astest failure getdoes for its output dir) is defensible too — flagging so the fix deliberately picks one. Same question for the invalid-parent case (currently exit 5 under dry-run). - Normal mode unchanged: file created; overwrite without
--forcestill exits 5;--forcestill overwrites (existing tests must stay green). --outpath validation in normal mode unchanged.- Regression tests added (unit, plus optionally a
--dry-runsmoke entry for scaffold in the subprocess P6 block); no runtime dependencies; optional one-line DOCUMENTATION.md note may ride the fix PR.
Reproduction (from a clean build of 988c035; any temp dir):
$ testsprite test scaffold --dry-run --out fresh.plan.json ; echo "exit=$?"; ls fresh.plan.json
Scaffold written to …/fresh.plan.json # stderr
exit=0
…/fresh.plan.json # file EXISTS — should notConfirmations
- I have searched existing issues and this is not a duplicate. (Full-text search of open issues and PRs for "scaffold" and "dry-run", plus closed-issue searches, re-verified immediately before filing; happy to be pointed at anything missed.)
- I have provided my Discord identity above for reward coordination.
Activity