Files
suaveplan/openspec/changes/enterprise-testing-integration/specs/testing/spec.md
2026-05-27 23:29:53 +01:00

5.1 KiB

MODIFIED Requirements

Requirement: Coverage and docs gates

Original: "Istanbul statement + branch coverage across packages/universal/testing/src/ (excluding *.bench.ts and *.schemas.ts) MUST be ≥ 95%. […]"

Istanbul statement, branch, function, AND line coverage across packages/testing/testing/src/ (excluding *.bench.ts, *.schemas.ts, barrel index.ts files, and inner-dir *.types.ts) MUST each be exactly 100 %. There is no foundation carve-out, no degraded-situation floor, and no exception for any genesis package — the gate applies to @suaveplan/testing and to every other @suaveplan/* package equally. The pre-publish gate (bunx turbo test:coverage --filter=@suaveplan/testing) MUST hard-fail when any of statements, branches, functions, or lines is below 100 %.

Every implementation file (*.ts other than *.test.ts, *.bench.ts, *.schemas.ts, inner-dir *.types.ts, barrel index.ts) MUST have a sibling .md file of ≥ 200 words (excluding code fences) with a simple and an advanced usage example. Mutation testing, contract testing, property testing, and fuzz coverage layer on top of 100 % line coverage; they do not substitute for it, and 100 % line coverage does not substitute for them.

Scenario: Coverage gate at 100 %

  • WHEN bunx turbo test:coverage --filter=@suaveplan/testing runs
  • THEN reported statement, branch, function, and line coverage are each 100 % AND the gate fails the build if any dimension drops below 100 %

Scenario: Docs coverage enforced

  • WHEN a script enumerates implementation files under src/ (excluding the stated exclusions) and checks for sibling .md files
  • THEN every implementation file has a sibling .md AND every .md is ≥ 200 non-code words

Requirement: Property-based coverage for pure helpers

Original: "Pure functions and deterministic state machines in the package […] SHALL have fast-check property tests covering round-trip, idempotency, associativity, and boundary invariants […]"

Pure functions and deterministic state machines in @suaveplan/testing SHALL have fast-check property tests covering round-trip, idempotency, associativity, and boundary invariants as applicable. The same requirement extends to every other pure-function @suaveplan/* package — every package whose public surface includes deterministic, side-effect-free helpers MUST ship fast-check property tests that exercise round-trip, idempotency, associativity, or boundary invariants where each applies. Load-bearing examples include @suaveplan/crypto-utils (encoding round-trips), @suaveplan/diff (apply ∘ invert, structural equality), @suaveplan/pagination (cursor encode/decode round-trip, slice associativity), and @suaveplan/validation (parse / safeParse equivalence on valid inputs).

Scenario: Builder composition is associative

  • WHEN fast-check runs over arbitrary RequestBuilder presets a, b, c applied in different groupings
  • THEN (a then b) then c produces the same final state as a then (b then c)

Scenario: Pure-function package ships round-trip property

  • WHEN any genesis package whose public surface is pure (e.g. @suaveplan/crypto-utils hexEncode/hexDecode) is audited for property tests
  • THEN at least one fast-check test exists asserting decode(encode(x)) === x for arbitrary Uint8Array inputs

Requirement: Deterministic clock and RNG via dependency injection

Original: "Every public function that reads time, randomness, or iterates SHALL accept an optional { clock?: Clock; rng?: Rng; signal?: AbortSignal } options parameter. […]"

Every public function in @suaveplan/testing that reads time, randomness, or iterates SHALL accept an optional { clock?: Clock; rng?: Rng; signal?: AbortSignal } options parameter. Zero-config callers MUST get behaviorally stable defaults: defaultClock (backed by Date.now / performance.now), cryptoRng (backed by crypto.getRandomValues), and no abort.

This contract additionally extends to integration test fixtures via TestEnvironmentOptions — @suaveplan/testing-integration's TestEnvironment MUST accept { clock?, rng?, signal? } and propagate them to fixtures whose configs declare acceptsContext: true, so that integration tests can run against the same deterministic time and randomness sources as unit tests.

Scenario: Injected clock controls observed time

  • WHEN waitFor(predicate, { clock: fakeClock }) runs and fakeClock.advance(1000) is called
  • THEN the helper observes 1000 ms elapsed AND does NOT read Date.now() or performance.now() directly

Scenario: Injected RNG produces deterministic factory output

  • WHEN Factory.build({ rng: seededRng(42) }) is called twice with the same factory definition
  • THEN both calls return byte-identical output (for factories whose definition is otherwise deterministic)

Scenario: TestEnvironment forwards clock to opt-in fixtures

  • WHEN a TestEnvironment is constructed with { clock: fakeClock } and a fixture's config declares acceptsContext: true
  • THEN that fixture's config.clock === fakeClock at the time start() is invoked