Spec Coverage Gate

HAProxy UI ships a script that cross-references the official Dataplane v3 OpenAPI specification against the Zod schemas in src/lib/api/types.ts. It answers one question continuously: does the app model the whole schema, or has it drifted?

Running it

node scripts/coverage.mjs # offline, uses the cached spec
node scripts/coverage.mjs --refresh # refetch the spec first
pnpm coverage # same as the first form

Current status: 287/287 spec fields modeled, 0 gaps.

What it checks

Six models are compared against the spec, because those are the six the app owns:

const MODELS = ['global', 'defaults', 'frontend', 'backend', 'bind', 'server'];

For each, it walks the spec’s definitions → <model> → properties block and compares each field against the Zod schema. Nested properties: blocks (inside array-of-objects such as cpu_maps) are skipped so JSON-Schema keywords like items, type and pattern are not miscounted as model fields.

The gate

The script exits 1 when any model has unmodeled spec fields, or when the modelled set drifts from the spec. It runs in CI on every build:

# .github/workflows/build.yml
- run: pnpm run coverage

This turns “did we forget a field?” from a question someone has to remember to ask into a build failure. When the Dataplane API adds a field, the build breaks and names it.

Spec source and caching

The spec is fetched from the haproxytech/dataplaneapi-specification repository and cached at .coverage/haproxy_spec.yaml. The offline mode is what makes the gate deterministic in CI; --refresh updates the cache when the upstream spec changes.

Why this matters more than a test count

Passing unit tests prove the code does what it was written to do. They say nothing about whether the code matches the API it targets. This gate is the only thing in the build that notices when the API changes underneath the app — and for a config editor, silently not modelling a field is a correctness bug, not a missing feature. An unmodelled field on a resource can be deleted by a PUT (see API Reference), so gaps here are data-loss risks, not gaps in convenience.

Full verification command set

All four must be green before a change ships:

pnpm lint # prettier --check . && eslint . (0 errors)
pnpm check # svelte-check (0 errors, 0 warnings)
pnpm test # vitest single-run (230 passed, 1 skipped)
pnpm coverage # spec cross-reference (287/287, exit 0)


Was this article helpful?