Uh oh!
There was an error while loading. Please reload this page.
ci(api): backend response-shape fixtures — pinned sync + decode contract tests - #317
Conversation
…act tests (#291) WS-D.2 of the CLI hardening epic (backend#1106): the unit tests feed internal/api hand-written JSON, which pins the CLI's expectations but drifts silently when the backend renames a field — the new body decodes green and callers get Go zero values (empty account, id 0, adopted never true). This closes that class end to end: - scripts/sync-backend-fixtures.sh (cloned from sync-schema.sh's shape) vendors the REAL serialized responses for all 9 endpoints the CLI calls — generated + shape-asserted in backend CI (metaApi/tests/test_cli_response_contracts.py, backend#1108) — from tracebloc/backend at scripts/.backend-ref into internal/api/testdata/. Backend is private, so it fetches via the authenticated contents API (BACKEND_CONTRACTS_TOKEN / GH_TOKEN / gh auth token). - NEW pinned scripts/.backend-ref (backend#1009 pattern): an unrelated backend commit can't red every open CLI PR; adopting upstream shapes is a deliberate SHA bump + re-sync. - internal/api/contracts_test.go replays every fixture through the client's real decode paths: load-bearing fields must decode non-zero, the RFC 8628 error enums must map to their sentinels, 200-adopt vs 201-mint must drive the adopted flag, 426 must carry min_version, and the 409 bodies must carry the fields conflictMessage parses. A manifest completeness check fails when a synced fixture has no assertions. - backend-fixtures-drift.yml runs the --check in CI; skips with a warning until a read-only BACKEND_CONTRACTS_TOKEN secret exists (backend is private; the default GITHUB_TOKEN can't read it). Fixtures live in backend CI as plain pytest output — no backend PR ever blocks on the CLI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LukasWodka
commented
Jul 14, 2026
👋 Heads-up — Code review queue is at 56 / 30 Above the WIP limit. The team convention is to review existing PRs before opening new work. Open PRs currently in Code review (oldest first):
Pull from review before opening new work. (This is a nudge from the kanban WIP check, not a block.) |
LukasWodka
commented
Jul 14, 2026
@BugBot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 36e8932. Configure here.
saadqbal
left a comment
There was a problem hiding this comment.
👍 Nice contract layer — decode tests pin the CLI's response structs against real backend fixtures, and the manifest-completeness meta-test (no untested fixture, no stale coverage) is a great guard against fixture rot. Correctly handles backend being private (skip-with-warning without BACKEND_CONTRACTS_TOKEN, pinned to .backend-ref, least-privilege). Drift check + Test green.
Summary
Closes the backend API drift hole beyond the 426 handshake (epic backend#1106, WS-D.2): a renamed backend response field currently ships green in both repos while the CLI silently decodes it to a Go zero value (empty account, id 0,
adoptednever true). This PR vendors REAL backend response shapes at a pinned ref and replays them through the CLI's actual decode paths.scripts/sync-backend-fixtures.sh— cloned fromsync-schema.sh's shape (--checkmode, staged temp files, JSON validation, ref-shape guard). Vendors the 18 fixtures fromtracebloc/backendmetaApi/tests/contracts/cli/intointernal/api/testdata/. Backend is private, so it fetches via the authenticated GitHub contents API (BACKEND_CONTRACTS_TOKEN/GH_TOKEN/gh auth tokenfallback) instead of raw.githubusercontent.scripts/.backend-ref— NEW pinned backend SHA (backend#1009 pattern, mirrors.data-ingestors-ref): an unrelated backend commit can't red every open CLI PR; adopting upstream shapes is a deliberate SHA bump + re-sync.internal/api/testdata/*.json— the vendored fixtures: real serialized responses for all 9 endpointsinternal/api/client.gocalls (device/code, device/token, userinfo, auth/revoke, edge-device create/adopt/patch/revoke/list/admins) plus the load-bearing error bodies (426min_version, the four RFC 8628 device-flow error enums, 403, both 409 conflict bodies). Generated + shape-asserted in backend CI by the companion PR tracebloc/backend#1108.internal/api/contracts_test.go— replays every fixture through the real client methods: load-bearing fields must decode non-zero, error enums must map to their sentinels (ErrAuthorizationPendingetc.), 200-vs-201 must drive theadoptedflag, pagination must follow the real DRFnextlink, 426 must surfacemin_version, and the 409 bodies must carry the exact fieldsinternal/cli'sconflictMessageparses (error/owner_email/holder_name/holder_client_id). A manifest completeness check fails when a synced fixture has no assertions..github/workflows/backend-fixtures-drift.yml— NEW dedicated workflow (not a build.yml job — it needs a secret the build jobs don't). Runssync-backend-fixtures.sh --checkon PRs + pushes to develop/main; skips with a warning until aBACKEND_CONTRACTS_TOKENsecret exists (read-only Contents access to tracebloc/backend — the repo-scoped defaultGITHUB_TOKENcannot read a private sibling repo). Same built-inert-until-secret activation model as the public PII gate.Fixtures live in backend CI as plain pytest output — no backend PR ever blocks on the CLI; the CLI adopts shape changes via its own deliberate pin bump.
Fixes#291. Companion backend PR: tracebloc/backend#1108. Part of tracebloc/backend#1106.
Type of change
Test plan (what actually ran locally)
scripts/sync-backend-fixtures.sh(write mode) against the real pinned ref — fetched all 18 fixtures from the private backend repo viagh auth token; re-ran in--checkmode — no drift, exit 0.go test -race -count=1 ./internal/api/— all pass, including the 16 new contract tests.go test ./...— full unit suite green;go build ./...clean;go vet+gofmt -sclean.BACKEND_CONTRACTS_TOKENexists yet — see below).Notes for review
::warning::) until someone with org admin addsBACKEND_CONTRACTS_TOKEN— a read-only (Contents: read) fine-grained token fortracebloc/backend— as a repo or org Actions secret. Until then the vendored fixtures + Go contract tests still run everywhere (they need no network).scripts/.backend-refcurrently pins the backend#1108 branch head (370e4d6) — the first commit carrying the fixtures (T16-style coupled release, same as cli#216 did for the schema pin). After backend#1108 merges, bump the pin to the develop merge commit and re-run the sync (fixture bytes will be identical; only the ref comment changes).🤖 Generated with Claude Code
Note
Low Risk
Changes are limited to tests, vendored fixtures, and CI; no runtime changes to
internal/api/client.goor CLI behavior.Overview
Adds pinned vendoring of real backend API response JSON and contract tests so renamed or reshaped fields cannot decode as silent Go zero values (login, provisioning, device flow, and error handling).
scripts/sync-backend-fixtures.shandscripts/.backend-refpull 18 fixtures from privatetracebloc/backend(metaApi/tests/contracts/cli/) intointernal/api/testdata/, with--checkmode for drift detection (same deliberate SHA-bump model as schema sync).internal/api/contracts_test.goreplays those bodies through the realClientmethods: non-zero load-bearing fields, RFC 8628 poll sentinels, 200 vs 201adopted, DRF paginationnext, 426min_version, and 409 fields used by CLI conflict messaging. A manifest test keeps synced files and assertions in lock-step..github/workflows/backend-fixtures-drift.ymlruns the sync check on PRs/pushes; it warns and skips untilBACKEND_CONTRACTS_TOKENis configured (defaultGITHUB_TOKENcannot read the private backend repo).Reviewed by Cursor Bugbot for commit 36e8932. Bugbot is set up for automated code reviews on this repo. Configure here.