Skip to content

[Hackathon] test delete-batch --all --status "" permanently deletes every test in the project (and test rerun --all --status "" reruns every test) #498

Open

Description

@cipoklean

Verified against (commit SHA or released version)

988c035 (main, v0.14.0), Linux, Node v22.23.3

Discord Username / User ID

949225590181994556

What does this improvement do?

What's broken

--status <list> is the only thing that narrows test delete-batch --all (permanent delete, no restore window) and test rerun --all (dispatches and bills a run per test). When the value is the empty string, both commands treat it as "no filter" and sweep the whole project:

  • src/commands/test.ts, runDeleteBatch: validateStatusFilter('') returns early ("Empty / undefined → no filter"), then the filter is skipped by if (opts.statusFilter !== undefined && opts.statusFilter !== ''). Every test returned by GET /tests?projectId=… is deleted.
  • The --all branch of runTestRerunCore has the same two lines, so every test is rerun.

An empty --status is easy to produce, and it is never what the caller meant:

  • --status "$STATUSES" in a cleanup or CI script when the variable is empty (set -u doesn't catch a variable set to an empty string);
  • --status= (a typo, or a value cut off by a templating step);
  • a generated command line where the status list came back empty.

The behaviour is also inconsistent: --status , and --status " " select nothing (delete zero tests; rerun exits 5). Only the literal "" flips to "everything".

Repro (loopback mock, no real key, nothing deleted for real)

A stub serves a project with three tests (failed, passed, draft) and logs every DELETE /tests/:id and POST /tests/batch/rerun.

On main @ 988c035:

command exit server saw
test delete-batch --all --project p1 --status failed --confirm 0 1 DELETE (t_failed)
test delete-batch --all --project p1 --status "" --confirm 0 3 DELETEs: every test in the project
test delete-batch --all --project p1 --status , --confirm 0 0 DELETEs
test delete-batch --all --project p1 --status " " --confirm 0 0 DELETEs
test rerun --all --project p1 --status failed 0 testIds=["t_failed"]
test rerun --all --project p1 --status "" 0 testIds=["t_failed","t_passed","t_draft"]
test rerun --all --project p1 --status , 5 no dispatch

Text output for the empty case gives no hint that the filter was dropped:

Resolving tests in project p1…
Resolved 3 tests to delete.
Deleted 3, skipped 0, failed 0.

Expected

A --status that was given but names no status should be refused before any request, with exit 5 and a message saying why. It should never widen a destructive or billing sweep to the whole project. Leaving --status out still means "every test", so nobody loses a way to do that on purpose.

Fix (PR to follow), all in src/commands/test.ts:

Tests: src/commands/test.quickwins.spec.ts (delete-batch --all with --status "", " " or " , ," → VALIDATION_ERROR on status, zero requests) and src/commands/test.rerun.spec.ts (same three values for rerun --all, plus --dry-run --all --status ""). All 7 new tests fail on main and pass with the fix; existing --status failed / failed,blocked tests still pass.

Checked for duplicates (all issues and PRs, open and closed, as of 17 Oct): #160/#163 (--status without --all ignored), #151/#152/#210/#211/#222 (test list --status validation order), #475 (rerun --all --env status source), #376 (flags ignored without their mode), #395/#468/#382/#386 (empty or whitespace ids). None covers an empty --status widening --all to every test.

Details / implementation notes

  • Add parseStatusSelection(raw): runs the existing validateStatusFilter token check, then requires at least one non-empty token, otherwise throws localValidationError('status', …) (VALIDATION_ERROR, exit 5) naming the accepted values. Returns the Set of statuses to keep.
  • runDeleteBatch and the --all branch of runTestRerunCore use it instead of validateStatusFilter plus the !== '' guard, refusing before the GET /tests listing, so nothing is listed, deleted or dispatched.
  • validateRerunOptions also calls it, so test rerun --all --dry-run --status "" refuses the same input. Today the rerun dry-run doesn't validate --status at all.
  • test list --status "" is left alone (read-only).
  • DOCUMENTATION.md: one sentence under test delete / test delete-batch and one --status bullet under test rerun.

Confirmations

  • I have searched existing issues and this is not a duplicate.
  • I have provided my Discord identity above for reward coordination.

Activity

  1. cipoklean commented on Oct 17, 2026

    @cipoklean
    Author

    I have a fix ready for this on a local branch: an explicitly empty --status is now rejected with a validation error (exit 5) before any request, for both test delete-batch and test rerun, while an omitted --status keeps today's behaviour. 7 tests (fail on main, pass with the fix); typecheck, format, lint and coverage green. I'll comment /assign and open the PR as soon as this is triaged.

  2. zeshi-du commented on Oct 20, 2026

    @zeshi-du
    Contributor

    Thanks @cipoklean. Accepted. Confirmed in v0.15.0: an explicitly empty --status means "no filter" in delete-batch --all and rerun --all, so one empty shell variable can delete, or re-run and bill, a whole project. Please /assign and open your PR.

    • A given --status with no status names (empty, whitespace, commas only) is refused with the existing validation error, exit 5, before any request.
    • Omitting --status keeps meaning every test.
    • Covers delete-batch --all and rerun --all (also with --dry-run); leave test list --status alone.
    • One shared helper on the existing token validation; no other exit-code changes.
    • Tests that fail on main and assert no requests are sent; short DOCUMENTATION.md note.

    rerun is being folded into run and removed in 1.0, so keep the diff small.

  3. added
    acceptedTriaged and accepted — a PR for this will be reviewed
    and removed
    needs-triageNew issue/PR awaiting first triage
    on Oct 20, 2026
  4. cipoklean commented on Oct 20, 2026

    @cipoklean
    Author

    /assign

  5. testsprite-hob commented on Oct 20, 2026

    @testsprite-hob

    Assigned to @cipoklean. Thanks for taking this on. (2 more slots available.)

    Your open assigned issues (1/3):
    (none)

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

Metadata

Metadata

Assignees

Labels

acceptedTriaged and accepted — a PR for this will be reviewedhackathonCLI hackathon submissionsin-progressAssigned and actively being worked on

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions