Skip to content

Commit 8c84eb8

Browse files
committed
fix(flaky): cap --runs at 10 per maintainer scope (#115)
Rescope the flaky detector's --runs bound from 1-100 to 1-10 as requested in the #115 triage: uncapped FE replays amplify free executions. Updates the MAX_FLAKY_RUNS constant (which drives the validation, error message, and --runs help text), docs, changelog, and the runs-bound tests. Regenerates the help snapshot, which also adds the previously-missing 'test flaky' entry.
1 parent 808badf commit 8c84eb8

5 files changed

Lines changed: 71 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ All notable changes to `@testsprite/testsprite-cli` are documented here. The for
66

77
### Added
88

9+
<<<<<<< HEAD
910
- **JUnit XML report export for batch `--wait` runs.** `test run --all` and batch `test rerun` (`--all` or multiple test ids) accept `--report junit --report-file <path>` to write a CI-friendly XML sidecar after polling completes. `--output json` is unchanged; the report is written even when the batch exits non-zero. `--dry-run` writes a canned sample without network calls.
1011
- **`testsprite test flaky <test-id>`** — repeat-run flaky-test detector. Replays a test N times (`--runs <n>`, default 5), aggregates the outcomes, and reports a stability verdict (`stable` / `flaky` / `failing`) plus the `runId` and `failureKind` of every attempt that did not pass. Replays run with auto-heal OFF (strict verbatim) so a healed drift can't mask a nondeterministic pass/fail. Exit code is 0 only when every attempt passed, so CI can gate a merge on flakiness (`testsprite test flaky <id> --runs 5 || exit 1`). Flags: `--runs <n>` (1–100), `--until-fail` (stop at the first non-passing attempt), `--timeout <s>` (per-attempt), and `--output json` for a machine-readable stability report. Frontend replays are free verbatim script replays; a one-line advisory is printed for backend tests, whose closure reruns may cost credits.
12+
=======
13+
- **`testsprite test flaky <test-id>`** — repeat-run flaky-test detector. Replays a test N times (`--runs <n>`, default 5), aggregates the outcomes, and reports a stability verdict (`stable` / `flaky` / `failing`) plus the `runId` and `failureKind` of every attempt that did not pass. Replays run with auto-heal OFF (strict verbatim) so a healed drift can't mask a nondeterministic pass/fail. Exit code is 0 only when every attempt passed, so CI can gate a merge on flakiness (`testsprite test flaky <id> --runs 5 || exit 1`). Flags: `--runs <n>` (1–10), `--until-fail` (stop at the first non-passing attempt), `--timeout <s>` (per-attempt), and `--output json` for a machine-readable stability report. Frontend replays are free verbatim script replays; a one-line advisory is printed for backend tests, whose closure reruns may cost credits.
14+
>>>>>>> d6a959a (fix(flaky): cap --runs at 10 per maintainer scope (#115))
1115
1216
## [0.2.0] - 2026-06-29
1317

DOCUMENTATION.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,15 +411,15 @@ Detect a **flaky** test by replaying it several times and reporting how often it
411411
testsprite test flaky test_xxxxxxxx --runs 10
412412

413413
# Fast "is it flaky at all?" — stop at the first non-passing attempt
414-
testsprite test flaky test_xxxxxxxx --runs 20 --until-fail
414+
testsprite test flaky test_xxxxxxxx --runs 10 --until-fail
415415

416416
# Machine-readable stability report for CI
417417
testsprite test flaky test_xxxxxxxx --runs 10 --output json
418418
```
419419

420420
Flags:
421421

422-
- `--runs <n>` — number of replays (1–100, default 5).
422+
- `--runs <n>` — number of replays (1–10, default 5).
423423
- `--until-fail` — stop at the first attempt that does not pass.
424424
- `--timeout <s>` — per-attempt polling deadline (same semantics as `test wait`).
425425

src/commands/test.flaky.spec.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ describe('runFlaky', () => {
345345
expect((err as ApiError).code).toBe('NOT_FOUND');
346346
});
347347

348-
it('rejects --runs outside 1..100 with a validation error (exit 5)', async () => {
348+
it('rejects --runs below the range (0) with a validation error (exit 5)', async () => {
349349
const { fetchImpl } = makeFlakyFetch({ statuses: [] });
350350
const { deps } = makeDeps(fetchImpl);
351351
const err = await runFlaky(
@@ -365,4 +365,25 @@ describe('runFlaky', () => {
365365
expect(err).toBeInstanceOf(ApiError);
366366
expect((err as ApiError).exitCode).toBe(5);
367367
});
368+
369+
it('rejects --runs above the cap (11) with a validation error (exit 5)', async () => {
370+
const { fetchImpl } = makeFlakyFetch({ statuses: [] });
371+
const { deps } = makeDeps(fetchImpl);
372+
const err = await runFlaky(
373+
{
374+
profile: 'default',
375+
output: 'text',
376+
dryRun: false,
377+
debug: false,
378+
verbose: false,
379+
testId: 'test_x',
380+
runs: 11,
381+
untilFail: false,
382+
timeoutSeconds: 600,
383+
},
384+
deps,
385+
).catch((e: unknown) => e);
386+
expect(err).toBeInstanceOf(ApiError);
387+
expect((err as ApiError).exitCode).toBe(5);
388+
});
368389
});

src/commands/test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8195,8 +8195,8 @@ export function createTestCommand(deps: TestDeps = {}): Command {
81958195
// `test flaky` — repeat-run flaky-test detector
81968196
// ---------------------------------------------------------------------------
81978197

8198-
/** Upper bound on `--runs` so a typo can't spawn thousands of replays. */
8199-
const MAX_FLAKY_RUNS = 100;
8198+
/** Upper bound on `--runs` so a repeat-runner can't amplify free FE replays. */
8199+
const MAX_FLAKY_RUNS = 10;
82008200
/** Default replay count when `--runs` is omitted. */
82018201
const DEFAULT_FLAKY_RUNS = 5;
82028202

test/__snapshots__/help.snapshot.test.ts.snap

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,15 @@ Commands:
270270
11 rate limited — honor Retry-After
271271
272272
On failure/blocked/cancelled, run: testsprite test artifact get <run-id>
273+
flaky [options] <test-id> Repeatedly replay a test to measure stability and surface flakiness.
274+
Replays run with auto-heal OFF (strict verbatim) so healed drift cannot mask nondeterministic pass/fail.
275+
276+
Exit codes:
277+
0 stable (every attempt passed)
278+
1 flaky or failing (at least one attempt did not pass)
279+
3 auth error
280+
4 test not found (no replayable run — trigger \`testsprite test run <id>\` first)
281+
5 validation error
273282
code Inspect and edit generated test code
274283
plan Manage FE test plan-steps (FE-only)
275284
failure Export the latest-failure agent bundle
@@ -345,6 +354,38 @@ Global options (--dry-run, --output, --profile, --endpoint-url, --request-timeou
345354
"
346355
`;
347356
357+
exports[`--help snapshots > test flaky 1`] = `
358+
"Usage: testsprite test flaky [options] <test-id>
359+
360+
Repeatedly replay a test to measure stability and surface flakiness.
361+
Replays run with auto-heal OFF (strict verbatim) so healed drift cannot mask nondeterministic pass/fail.
362+
363+
Exit codes:
364+
0 stable (every attempt passed)
365+
1 flaky or failing (at least one attempt did not pass)
366+
3 auth error
367+
4 test not found (no replayable run — trigger \`testsprite test run <id>\` first)
368+
5 validation error
369+
370+
Options:
371+
--runs <n> number of replays to run (1-10, default 5)
372+
--until-fail stop at the first non-passing attempt (fast "is it flaky at
373+
all?" check) (default: false)
374+
--timeout <s> per-attempt max seconds to wait (1-3600, default 600)
375+
-h, --help display help for command
376+
377+
Notes:
378+
• Frontend replays are free verbatim script replays (no credit); backend replays
379+
re-run the dependency closure and may cost credits — a one-line advisory is printed.
380+
• Replays use auto-heal OFF so a flaky test is not silently "healed" into a pass;
381+
this measures replay stability of the saved script against the configured URL.
382+
• \`--output json\` emits a machine-readable stability report for CI gating.
383+
384+
Global options (--dry-run, --output, --profile, --endpoint-url, --verbose, --debug):
385+
testsprite --help
386+
"
387+
`;
388+
348389
exports[`--help snapshots > test get 1`] = `
349390
"Usage: testsprite test get [options] <test-id>
350391

0 commit comments

Comments
 (0)