Skip to content

ci(api): backend response-shape fixtures — pinned sync + decode contract tests - #317

Merged
saadqbal merged 1 commit into
developfrom
feat/291-backend-fixtures-sync
Jul 14, 2026
Merged

ci(api): backend response-shape fixtures — pinned sync + decode contract tests#317
saadqbal merged 1 commit into
developfrom
feat/291-backend-fixtures-sync

Conversation

@LukasWodka

@LukasWodkaLukasWodka commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

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, adopted never 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 from sync-schema.sh's shape (--check mode, staged temp files, JSON validation, ref-shape guard). Vendors the 18 fixtures from tracebloc/backendmetaApi/tests/contracts/cli/ into internal/api/testdata/. Backend is private, so it fetches via the authenticated GitHub contents API (BACKEND_CONTRACTS_TOKEN / GH_TOKEN / gh auth token fallback) 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 endpoints internal/api/client.go calls (device/code, device/token, userinfo, auth/revoke, edge-device create/adopt/patch/revoke/list/admins) plus the load-bearing error bodies (426 min_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 (ErrAuthorizationPending etc.), 200-vs-201 must drive the adopted flag, pagination must follow the real DRF next link, 426 must surface min_version, and the 409 bodies must carry the exact fields internal/cli's conflictMessage parses (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). Runs sync-backend-fixtures.sh --check on PRs + pushes to develop/main; skips with a warning until a BACKEND_CONTRACTS_TOKEN secret exists (read-only Contents access to tracebloc/backend — the repo-scoped default GITHUB_TOKEN cannot 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

  • CI / tooling
  • Tests

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 via gh auth token; re-ran in --check mode — 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 -s clean.
  • Workflow YAML parses; the missing-secret guard path was reviewed but can only fully execute in Actions (no BACKEND_CONTRACTS_TOKEN exists yet — see below).

Notes for review

  • Activation needed: the drift gate is inert (skips with a ::warning::) until someone with org admin adds BACKEND_CONTRACTS_TOKEN — a read-only (Contents: read) fine-grained token for tracebloc/backend — as a repo or org Actions secret. Until then the vendored fixtures + Go contract tests still run everywhere (they need no network).
  • Pin bump expected: scripts/.backend-ref currently 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).
  • All files are NEW (no shared-file edits) — no rebase pressure against sibling PRs.

🤖 Generated with Claude Code


Note

Low Risk
Changes are limited to tests, vendored fixtures, and CI; no runtime changes to internal/api/client.go or 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.sh and scripts/.backend-ref pull 18 fixtures from private tracebloc/backend (metaApi/tests/contracts/cli/) into internal/api/testdata/, with --check mode for drift detection (same deliberate SHA-bump model as schema sync).

internal/api/contracts_test.go replays those bodies through the real Client methods: non-zero load-bearing fields, RFC 8628 poll sentinels, 200 vs 201 adopted, DRF pagination next, 426 min_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.yml runs the sync check on PRs/pushes; it warns and skips until BACKEND_CONTRACTS_TOKEN is configured (default GITHUB_TOKEN cannot 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.

…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
LukasWodka requested a review from saadqbalJuly 14, 2026 12:41
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

👋 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):

  • averaging-service#181 — feat(weights): normalize-on-read — mixed cycles average instead of rejecting (SEC-03 §8 step 2) · author: @shujaatTracebloc · no reviewer assigned
  • averaging-service#182 — feat(weights): averaging writes SafeTensors (TF + PyTorch) — SEC-03 §8 step 4 · author: @shujaatTracebloc · no reviewer assigned
  • backend#1079 — feat(global_meta): edge dataset_meta exposure + attributes contract at ingest (#924 G4a) · author: @divyasinghds · no reviewer assigned
  • backend#1086 — docs(rfc): SafeTensors weight-format migration — SEC-03 Phase 1 (RFC 0004) · author: @shujaatTracebloc · no reviewer assigned
  • backend#1093 — chore(deps): bump django from 5.2.14 to 5.2.15 · author: @dependabot · no reviewer assigned
  • backend#1095 — feat(experiment): configurable preprocessing knobs incl. tabular scaler — RFC 0003 L1 + L1b (#1094) · author: @LukasWodka · no reviewer assigned
  • backend#1100 — fix(boot): pin SDK install to tracebloc==0.11.2, drop 404 dev line (#1098) · author: @LukasWodka · reviewer: @saqlainsyed007
  • backend#1105 — perf(api): query micro-fixes — notifications N+1, cached data_scientist, composite index, sampling (#975) · author: @aptracebloc · no reviewer assigned
  • backend#1108 — test(api): CLI response-shape contract fixtures for the 9 CLI endpoints (WS-D.2) · author: @LukasWodka · reviewer: @saadqbal
  • cli#278 — fix(deps): toolchain go1.26.5 + x/net v0.57.0 — clear 6 reachable vulns; govulncheck CI gate · author: @LukasWodka · reviewer: @saadqbal

Pull from review before opening new work. (This is a nudge from the kanban WIP check, not a block.)

@LukasWodkaLukasWodka self-assigned this Jul 14, 2026
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

@BugBot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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.

@saadqbalsaadqbal left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 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.

@saadqbal
saadqbal merged commit 754b87a into developJul 14, 2026
23 checks passed
@saadqbal
saadqbal deleted the feat/291-backend-fixtures-sync branch July 14, 2026 14:29
Sign up for freeto 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.

2 participants

@LukasWodka@saadqbal