Skip to content

feat(coverage): complete, deterministic coverage from OpenAPI (staged) - #12

Merged
testomut merged 8 commits into
mainfrom
feat/coverage-completeness
Nov 9, 2026
Merged

testomut merged 8 commits into
mainfrom
feat/coverage-completeness

Conversation

@testomut

@testomut testomut commented Nov 9, 2026 •

Copy link
Copy Markdown
Owner

Coverage completeness, in stages. docs/SUPPORT_MATRIX.md lists every OpenAPI keyword / check with its state (supported, partial, missing, not generated on purpose) and the stage that changed it. One commit per stage; each verified on the demo API in TypeScript and Python before the next.

Stage Commit What
1 audit ca2c522 the support matrix (baseline)
2 one ID per test ca2c522 query enum variants: one test + accepted-value ID each (vocabulary v2), Python renders them too; tombstones for cases no longer generated; case IDs checked on the real run (Playwright report, pytest collection)
3 request bodies 93b4d14 recursive negative cases at any depth (request.body#/items/*/price), all listed keywords, exactly one broken rule each (ajv-verified); --max-cases with dropped cases reported
4 parameters 9142b4b query / header / cookie / path cases, array styles, required header / cookie in every call, path safety (UNSAFE_LIVE_CHECK)
5 responses ea9aa19 headers, no-body statuses, empty body, text schemas, writeOnly; what is not checked is said; identical rules in validator.ts and contract.py (one table of 24 cases through both)
6 errors c0234b4 401 invalid credentials, 405 (GET only), 409 (deps conflictOn), 412, 415; 403 / 429 / others as gaps
7 functional + data 7f59010 boundary-valid query values, deps echoes, sampler fixes (multipleOf, exclusive bounds, uniqueItems), uncovered oneOf / anyOf branches reported
8 scenarios 91b5455 reviewed business scenarios (--scenarios, schema, strict scenarios validate), repeated delete
9–10 parity, reporting ca0195c coverage-gaps.json with machine codes and summary, unchecked statuses / media types / security alternatives; expected gaps; README "What is covered, and what is not"

Verification

  • npm run typecheck clean; npm test 192 pass, 2 skipped (real-oasdiff tests).
  • Demo API (CI, TypeScript and Python, with demo-api/scenarios.json): 64 physical tests per language, each with its own registered case ID; only stats/get/contract (the deliberate defect) fails; nothing left behind; registry and manifest valid; the same active cases in both languages; coverage gaps exactly as expected.
  • Synthetic fixture tests/fixtures/coverage/kitchen-sink.yaml (CI): generated in both languages, compiles (tsc, py_compile), valid registry, 112 identical cases in TypeScript and Python.

Honest limits (reported as gaps, listed in the matrix)

oneOf / anyOf / not / discriminator negatives; array-item constraints in parameters; 403 without a reviewed role setup; 429; expired tokens; 405 where only unsafe methods are undocumented; list / filter / pagination without a reviewed scenario; eventual consistency polling (no polling step in the scenario DSL yet); non-JSON / non-text bodies; optional-field variants of create payloads; multiple examples as variants.

Stage 1 + 2 of the coverage plan.

- docs/SUPPORT_MATRIX.md: OpenAPI keyword / check → supported, partial,
  missing, with the stage that changes it.
- Query enum variants: each value is its own test with its own case ID
  (vocabulary v2, functional/positive accepted-value,
  request.query#/<name>/<value>); Python renders them too (it counted them
  but rendered none). Values equal as strings are sent once.
- Cases the generator no longer produces become tombstones in cases.json.
- generate --run checks the physical tests: Playwright JSON annotations,
  pytest collection via API_AGENT_CASES_OUT (conftest hook); report
  generated.caseIdCheck {tests, missing, duplicates, unregistered}.
- CI: case-ID check, registry validate --spec, manifest validate for
  TypeScript and Python, same active cases in both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 99c05c44-92da-4ba9-9991-bf0917c82bea
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

testomut and others added 7 commits November 9, 2026 20:16
- src/generate/body-cases.ts: from a valid payload, one broken rule per
  case at any depth (request.body#/customer/address/city,
  request.body#/items/*/price): required, type, nullable, enum (string,
  number, integer, boolean), const, min/maxLength, pattern, format,
  minimum/maximum, exclusive min/max (3.1 numeric and 3.0 boolean),
  multipleOf, min/maxItems, uniqueItems, items, min/maxProperties,
  additionalProperties: false; allOf merged.
- Rules that cannot be broken alone, oneOf / anyOf / not / discriminator,
  and missing samples become coverage gaps with a code and the exact
  location (CANNOT_ISOLATE_RULE, UNSUPPORTED_SCHEMA_KEYWORD,
  MISSING_VALID_SAMPLE). Fixes minProperties sending {} (it also broke
  required).
- Limit: --max-cases (default 60); potential / generated / dropped per
  operation in coverage-gaps.json, dropped ones as CASE_LIMIT_REACHED.
- tests/fixtures/coverage/kitchen-sink.yaml: synthetic spec with a wide
  keyword set. Every generated case is checked with ajv: exactly one
  error, the expected keyword, the expected instance path (68 cases).

Demo API: 58 tests in TypeScript and in Python, only stats/get/contract
fails; every test has its own registered case ID.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- src/generate/param-cases.ts: a valid request (every required query /
  header / cookie parameter with its documented value) in which exactly one
  parameter breaks exactly one rule; rules from the body engine (required,
  type for number / integer / boolean, enum, const, pattern, format, bounds,
  multipleOf, lengths, min/maxItems, uniqueItems).
- Arrays serialized as documented: form explode true / false,
  spaceDelimited, pipeDelimited; header simple; cookies in the Cookie
  header.
- Path parameters: invalid type / format / pattern / enum / length; numeric
  bounds on write operations are UNSAFE_LIVE_CHECK gaps (an out-of-range id
  could address a real record).
- Required header / cookie parameters are sent in every generated call
  (TypeScript optionsWith, Python pyRequired); one without a documented
  value blocks the operation.
- One data-driven loop over whole requests in TypeScript and Python;
  catalog keeps style / explode. The old query-only generator is removed.
- Tests: every case leaves the other parameters valid and breaks its own
  rule; serialization; path safety; gaps; TS/Python parity on the fixture.

Demo API: 58 tests in TypeScript and Python, only stats/get/contract fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on (stage 5)

validator.ts and contract.py:
- response headers: required ones present, documented ones match their
  schema (numbers / booleans coerced, arrays comma-separated);
- no body expected for 204 / 205 / 304, HEAD and responses without
  content: a body that arrives is "unexpected-body";
- an empty body where a schema is documented is "empty-body" (an
  undocumented Content-Type is reported first; JSON null is a body);
- text media types with a schema are validated as strings;
- writeOnly properties must not appear in a response and are never
  required in one;
- what is not checked (no schema, unsupported media type) is a note:
  Playwright annotation contract-note, pytest warning.

coverage-gaps.json lists MISSING_RESPONSE_SCHEMA and UNSUPPORTED_MEDIA_TYPE
per response. tests/unit/validator.test.ts runs one table of 24 cases
through both validators and requires identical results.

Demo API: no new failures; only stats/get/contract fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/generate/error-cases.ts plans, per operation, the documented errors
that can be triggered safely; the rest are gaps with codes.

- 401 invalid credentials per security scheme (bearer / OAuth2 / OIDC,
  basic, API key in header / query / cookie), sent without real ones.
- 405: a GET to a path that defines no GET, one case per path; a path with
  GET is UNSAFE_LIVE_CHECK.
- 409: create twice with the same payload, only with a reviewed deps
  "conflictOn"; business scenario, both records removed in finally;
  otherwise UNCONFIRMED_BUSINESS_RULE.
- 412: non-matching If-Match / If-Unmodified-Since on the test's own record.
- 415: the documented body as text/plain, with confirmed cleanup.
- 403 / 429 / other 4xx: gaps (UNCONFIRMED_BUSINESS_RULE,
  UNSAFE_LIVE_CHECK, UNREACHABLE_RESPONSE_STATUS).
- Vocabulary v2: invalid-credentials, method-not-allowed,
  precondition-failed. TypeScript and Python render the same cases.
- CI: the synthetic fixture (now with security, 405, 412, 415, 409, 403,
  429, 418) is generated in both languages and must compile (tsc,
  py_compile) with a valid registry.

Demo API: 59 tests per language (401 with an invalid token on GET /me
passes live); only stats/get/contract fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s (stage 7)

- Boundary-valid query values (minimum / maximum inside exclusive bounds
  and on multipleOf, shortest / longest string without pattern / format):
  one test and one accepted-value case ID each, TypeScript and Python;
  registry validation accepts these values (shared boundaries.ts).
- deps "echoes": the create test asserts that the named fields come back as
  sent (only with this reviewed mapping; the spec does not state it).
- Sampler: smallest multipleOf inside the bounds; 3.0 boolean and 3.1
  numeric exclusive bounds combined with minimum; distinct elements for
  uniqueItems (or blocked with the reason).
- oneOf / anyOf in a sent payload: the branches not exercised are
  UNCOVERED_VARIANT gaps at their location.

Demo API: 61 tests per language (limit=1 and limit=100 pass live); only
stats/get/contract fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e 8)

- src/scenarios/dsl.ts + schemas/scenarios.schema.json: a reviewed
  scenarios file (--scenarios): steps with phases, path / query params and
  bodies with {var} values saved by earlier steps (JSON pointers), the
  documented status each step must get, cleanup steps.
- Strict validation (also `scenarios validate --spec`): operations and
  parameters exist, statuses documented, variables saved before use, a
  writing scenario has cleanup, the primary is an action, unique IDs.
- Generated in the primary operation's scenarios file, TypeScript and
  Python: steps in order, cleanup in finally with failures reported. The
  scenarioUid comes from the external id (never the title); registry
  definition with steps and externalId.
- CRUD: deleting the same resource twice gets the documented 404.
- demo-api/scenarios.json (positive and negative) runs in CI in both
  languages.

Demo API: 64 tests per language, both scenarios pass live, nothing left
behind; only stats/get/contract fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ecks in CI (stages 9-10)

- src/generate/coverage.ts: stable gap codes; positive-blocked reasons as
  codes; per operation the generated count and missing code of each suite,
  unchecked statuses (with the precise reason when an error gap names the
  status), unchecked request media types and security alternatives.
- coverage-gaps.json gets a summary (operations, generated tests per suite,
  gaps by code); generate logs the totals.
- Expected gaps: the synthetic fixture (unit test) and the demo API (CI,
  from the real run) must match exactly: a new gap or a silently vanished
  one fails.
- CI: TypeScript / Python parity on the synthetic fixture too.
- README: "What is covered, and what is not" (spec-derived, needs
  confirmation, never generated); docs/SUPPORT_MATRIX.md completed;
  CHANGELOG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@testomut
testomut marked this pull request as ready for review November 9, 2026 20:14
@testomut
testomut merged commit 175b8d0 into main Nov 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant