Files
devin-ai-integration[bot] 006c4cadc9 test: nightly parser fuzz lane — parser input fails as typed AppErrors, never hangs (#1414) (#1438)
* test: nightly parser fuzz lane with typed-AppError invariant (#1414)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(fuzz): run envelope, artifact promotion, and harness self-check tests (#1414)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(fuzz): shared scheduled-lane envelope on every terminal path, watchdog after ready (#1414)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(fuzz): envelope for malformed options; add scheduled-lane health consumer (#1414, #1430)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(lanes): actions:read scope, terminal error envelope, first-due grace (#1414, #1430)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(lanes): anchor first-run grace to schedule registration, use exec helper in tests (#1414, #1430)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(lanes): portable POSIX pickaxe pattern for schedule registration (#1414, #1430)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* refactor(fuzz): fast-check generators over the shared hazard list, drop the bundled lane-health work (#1414)

- Strip scripts/scheduled-lane/* and scheduled-lane-health.yml: that watcher is #1430's own
  deliverable and collides with PR #1439's implementation of the same lane. What this lane owes
  (a per-run envelope) moves into scripts/fuzz/envelope.ts.
- Rebase onto #1437 and rebuild the generator layer on fast-check: cases come from arbitraries
  sharing SELECTOR_VALUE_HAZARDS with the property suite, and counterexamples are shrunk, so a
  failure names a minimal input plus fast-check's seed/path instead of a 20k-char random string.
- Route harness.test.ts into the serialized subprocess-stub project.
- Drop the AGENT_DEVICE_FUZZ_STARTUP_DELAY_MS test seam: the ready handshake is now proven by a
  case budget far below real worker startup.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(fuzz): replay the regression corpus through the worker watchdog (#1414)

A promoted hang case used to wedge the unit job until the CI timeout, because corpus replay called
checkCase in-process. It now goes through the same worker-backed watchdog the nightly lane uses, so
such a case fails against a 5s per-case budget; the file moves to the serialized subprocess-stub
project with the rest of the worker-driven fuzz tests.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(fuzz): let the watchdog outlive vitest's default case timeout (#1414)

A wedged parser was surfacing as a bare 'Test timed out in 5000ms' instead of the named hang:
failure that says which input wedged, because the file's vitest timeout was shorter than the
watchdog budget times the number of replayed cases.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(fuzz): complete drift provenance in the lane envelope (#1414)

configHash now covers every input that decides what a seed generates (generate.ts and the shared property arbitraries, not just the arbitraries/targets/invariant), and tool records fast-check's installed version. A generation-loop edit or a fast-check upgrade previously changed the case set while the envelope looked unchanged. A test recomputes the hash with each input omitted so a future omission fails.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: Michał Pierzchała <thymikee@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-28 11:29:25 +02:00

63 lines
2.3 KiB
TypeScript

// `pnpm fuzz:parsers --self-check` — proves the harness can still fail (#1414).
//
// Runs each broken-on-purpose target through the same worker/watchdog path a real case takes
// and asserts the expected failure kind comes back. Inverted expectations are the point: this
// mode fails when the harness reports nothing.
import type { FuzzTargetRun } from './envelope.ts';
import { runCases } from './execute.ts';
import { describeFailure } from './invariant.ts';
import type { FuzzOptions } from './options.ts';
import { SELF_CHECK_EXPECTATIONS, SELF_CHECK_TARGETS } from './self-check-targets.ts';
import type { SelfCheckTargetName } from './target-types.ts';
type SelfCheckResult = {
target: string;
expected: string;
actual: string;
ok: boolean;
durationMs: number;
};
/** Runs every self-check target once; `caseTimeoutMs` also bounds the hang target. */
async function selfCheckResults(caseTimeoutMs: number): Promise<SelfCheckResult[]> {
const results: SelfCheckResult[] = [];
for (const target of SELF_CHECK_TARGETS) {
const expected = SELF_CHECK_EXPECTATIONS[target.name as SelfCheckTargetName];
const started = Date.now();
const failures = await runCases(target, [...target.seeds], caseTimeoutMs);
const actual = failures[0] ? failures[0].kind : 'none';
results.push({
target: target.name,
expected,
actual,
ok: actual === expected,
durationMs: Date.now() - started,
});
if (failures[0]) process.stdout.write(` ${describeFailure(failures[0])}\n`);
}
return results;
}
/** Runs the self-check and reports per-target rows for the run envelope. */
export async function runSelfCheck(
options: FuzzOptions,
): Promise<{ ok: boolean; targetRuns: FuzzTargetRun[] }> {
process.stdout.write('Self-check: the harness must catch each seeded violation.\n');
const results = await selfCheckResults(options.caseTimeoutMs);
for (const result of results) {
process.stdout.write(
`${result.ok ? 'ok ' : 'FAIL'} ${result.target}: expected ${result.expected}, got ${result.actual}\n`,
);
}
return {
ok: results.every((result) => result.ok),
targetRuns: results.map((result) => ({
target: result.target,
cases: 1,
failures: result.ok ? 1 : 0,
durationMs: result.durationMs,
})),
};
}