Skip to content

test(api): decode-fidelity, not blanket non-zero, for the client/admin list contracts - #324

Merged
LukasWodka merged 1 commit into
developfrom
fix/contract-list-decode-fidelity
Jul 14, 2026
Merged

test(api): decode-fidelity, not blanket non-zero, for the client/admin list contracts#324
LukasWodka merged 1 commit into
developfrom
fix/contract-list-decode-fidelity

Conversation

@LukasWodka

@LukasWodkaLukasWodka commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What

Fixes the Cursor Bugbot "contract test rejects valid values" (Medium) finding on #323 — plus one same-class sibling the verification sweep surfaced.

assertProvisionedClient asserts every ProvisionedClient field decodes non-zero (a valid silent-drift check). That's right for the three hand-pinned single fixtures (create/adopt/patch), but TestContractListClientsPaginated applied it to every row of a real synced list (edge_device_list.json, reseeded by scripts/sync-backend-fixtures.sh). Per client.go, status==0 is STATUS_OFFLINE and cluster_id is "Empty on legacy / not-yet-backfilled clients" — both legitimate. So the next re-sync of a list containing an offline or unanchored row would fail CI even though decoding is correct.

Fix

New assertDecodesFrom, used per-row by the list test, catches both silent-drift directions while tolerating legitimate zeros:

drift directionhow it's caught
struct-tag drift (a Go json: tag renamed)wire still carries the old key with a value, struct decodes zero → value mismatch
backend rename/drop on re-sync (wire field renamed, fixture reseeded)old key vanishes → a pure value compare reads zero on both sides, so a presence assertion on the always-present keys (id/first_name/username/namespace/status) catches it

cluster_id is presence-optional (legitimately empty); its rename stays covered by the strict single-fixture tests. status==0 is tolerated (key present, value 0). The three pinned single fixtures keep the strict non-zero helper.

Sibling (same-class, from the sweep):TestContractListClientAdmins asserted name != "" for every admin row — but name is a display name that can be legitimately blank; email is the domain-guaranteed identifier. Hardened to assert email present + wire-match and name wire-fidelity only.

Verification (mutation-proven)

  • struct-tag rename of cluster_id → list test fails
  • backend key-rename namespacek8s_namespace on the fixture → list test fails via presence ✓
  • fixture row with status:0 & cluster_id:"" → list test passes
  • blank admin name → admins test passes
  • admin email key-rename → admins test fails
  • go vet, gofmt -s, goimports, file-budget clean

Once merged to develop, #323 (develop → main promotion) inherits it and Bugbot re-clears.

The presence-check design was caught by a 3-lens adversarial review of an earlier draft that value-compared all six fields — that draft silently missed the backend-rename-on-resync direction, the primary class these contract tests exist to catch.


Note

Low Risk
Test-only changes in contract tests; no production API or CLI behavior is modified.

Overview
List contract tests no longer require every ProvisionedClient row to decode non-zero. assertDecodesFrom compares each decoded list row to the raw fixture JSON: required keys must still be present on the wire (catches backend rename/drop on re-sync), and values must match (catches Go json tag drift). status == 0 and empty cluster_id are allowed on real synced list rows.

TestContractListClientsPaginated uses assertDecodesFrom per row (mapped to page1.Results) instead of assertProvisionedClient. Create/adopt/patch single-fixture tests still use the strict non-zero helper.

TestContractListClientAdmins now asserts email is non-empty and matches the wire, and name matches the wire only (blank display names are valid).

Reviewed by Cursor Bugbot for commit b8fb9c4. Bugbot is set up for automated code reviews on this repo. Configure here.

…n list contracts
assertProvisionedClient asserts every ProvisionedClient field decodes
non-zero — the silent-drift class. That is right for the three hand-pinned
single fixtures (create/adopt/patch), but TestContractListClientsPaginated
applied it to every row of a real synced list (edge_device_list.json).
status==0 is STATUS_OFFLINE and cluster_id is "Empty on legacy /
not-yet-backfilled clients" (client.go), so the next re-sync of a list that
includes an offline or unanchored row would fail CI even though decoding is
correct. This is the Cursor Bugbot "contract test rejects valid values"
finding on #323.
Add assertDecodesFrom, used per-row by the list test. It catches BOTH silent
drift directions without rejecting legitimate zeros:
- struct-tag drift (a Go json tag renamed): the wire still carries the old
key with a value while the struct decodes zero -> value mismatch -> caught.
- backend rename/drop on re-sync (the wire field renamed, fixture reseeded):
the old key vanishes, so a pure value compare reads zero on both sides. A
presence assertion on the always-present keys (id/first_name/username/
namespace/status) catches this. cluster_id is presence-optional (legit
empty), its rename stays covered by the strict single-fixture tests.
status==0 is tolerated (key present, value 0). The single pinned fixtures
keep the strict non-zero helper.
Also harden TestContractListClientAdmins (surfaced by the same-class sweep):
email is a domain-guaranteed identifier -> assert present + wire-match (a
renamed email tag decodes "" -> caught); name is a display name that can be
legitimately blank -> assert wire-fidelity only, not non-empty.
Proven by mutation: struct-tag rename and backend key-rename both fail the
list test; an offline/unanchored fixture row passes; a blank admin name
passes; an admin email-key rename fails.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodkaLukasWodka self-assigned this Jul 14, 2026
@LukasWodka
LukasWodka requested a review from saadqbalJuly 14, 2026 17:40
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

@BugBot run

@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

👋 Heads-up — Code review queue is at 47 / 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.)

@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 b8fb9c4. Configure here.

@LukasWodka
LukasWodka merged commit 692eccb into developJul 14, 2026
24 checks passed
@LukasWodka
LukasWodka deleted the fix/contract-list-decode-fidelity branch July 14, 2026 17:44
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