Skip to content

test(contract): classify the last 21 endpoints and cover the new reads - #188

Merged
Adron merged 1 commit into
mainfrom
fix-manifest-final-endpoints
Sep 24, 2026
Merged

Adron merged 1 commit into
mainfrom
fix-manifest-final-endpoints

Conversation

@Adron

@Adron Adron commented Sep 24, 2026

Copy link
Copy Markdown
Member

Final pass over the contract suite now that all 47 parity PRs are on main.

The inventory test had been failing since the merge pass began — it re-derives the endpoint set from the client on every run, and the client kept growing. That's the feature working as designed; this makes it green.

1. Two more files left the portable subset

The glob introduced in #185 picks up new Services/ files automatically, which is exactly what makes it safe. It correctly failed on two that are not portable:

File Dependency
AiAvailabilityService.cs CommunityToolkit.Mvvm
GitHubListIndex.cs uses AppServices and AppLog

Both excluded, reasons recorded alongside the others. Worth noting this is the glob paying off — under the old enumeration these would have been silently missing rather than loudly failing.

2. One more call shape

ReadTagsAsync — a private read helper whose name doesn't start with Get. GetHelper now matches (Get|Read)[A-Za-z]*Async.

Extending the convention rather than renaming production code to suit the scanner: both prefixes unambiguously denote a GET, and HttpMethod still matches first so this can't override an explicit verb.

3. The last 21 endpoints classified

Blog, list folders, list/document invites, tags, link metadata, weather/location, and the GitHub-backed list refresh. Six read, fifteen skip.

The skips are where the judgement is — every one would touch something real:

  • POST /api/blog/subscribe and the two invite POSTs send actual emails to actual addresses
  • PUT /api/folders/{id} moves a folder, and the server does not reject a cycle (feat(lists): list folders (/api/folders) with a client-side cycle guard #180), so a careless move corrupts the tree
  • DELETE /api/folders/{id} cascades to subfolders
  • /api/weather and /api/location need coordinates, and would assert against a real user's stored profile location
  • The invite GETs are owner-only against a list the suite doesn't own

The six new probes

Two are deliberately narrow:

  • link-metadata unfurls example.com, so it asserts the unfurler answers rather than that some third-party site is up
  • tags/autocomplete queries a single letter — an empty q is a 400, and anything longer assumes the account has particular content

GET /api/documents/{documentId} is the one worth having. The tree payload omits content with no opt-in (#139), so this is the only bulk source of a document body — the editor breaks silently if it stops carrying one.

Verification

live run:   Failed: 0, Passed: 95          (was 71 before the merge pass)
gated off:  Failed: 0, Passed: 4, Skipped: 91, exit 0
app:        Debug clean, Release clean
tray:       builds clean
Sync.Core:  38/38

🤖 Generated with Claude Code

Final pass over the contract suite now that all 47 parity PRs are on main. The
inventory test had been failing since the merge pass began, because it
re-derives the endpoint set from the client on every run and the client kept
growing. That is the feature working; this makes it green.

Three fixes:

1. Two more files had to leave the portable subset. The glob introduced in #185
   picks up new Services/ files automatically, which is what makes it safe — and
   it correctly failed on two that are NOT portable:
     AiAvailabilityService.cs -> CommunityToolkit.Mvvm
     GitHubListIndex.cs       -> uses AppServices and AppLog
   Both now excluded, with the reason recorded next to the others.

2. The scanner could not resolve one more call shape: ReadTagsAsync, a private
   read helper whose name does not start with "Get". GetHelper now matches
   (Get|Read)[A-Za-z]*Async. Extending the convention rather than renaming
   production code to suit the scanner — both prefixes unambiguously denote a
   GET, and HttpMethod still wins when present.

3. Classified the remaining 21 endpoints: blog, list folders, list/document
   invites, tags, link metadata, weather/location, and the GitHub-backed list
   refresh. Six read, fifteen skip.

   The skips are where the judgement is. Every one of them would touch something
   real: POST /api/blog/subscribe and the two invite POSTs send actual emails to
   actual addresses; PUT /api/folders/{id} moves a folder and the server does
   NOT reject a cycle, so a careless move corrupts the tree; DELETE
   /api/folders/{id} cascades to subfolders; /api/weather and /api/location need
   coordinates and would assert against a real user's stored profile location;
   and the invite GETs are owner-only against a list the suite does not own.

Added the six read probes the inventory then demanded. Two are deliberately
narrow: link-metadata unfurls example.com so it asserts the unfurler answers
rather than that some third-party site is up, and tags/autocomplete queries a
single letter because an empty q is a 400 and anything longer assumes content.

GET api/documents/{documentId} is worth having: the tree payload omits `content`
with no opt-in, so this is the only bulk source of a document body and the
editor breaks silently if it stops carrying one.

Verified:
  live run:   95/95 passed (was 71 before the merge pass)
  gated off:  4 passed, 91 skipped, exit 0
  app:        Debug and Release both clean
  tray:       builds clean
  Sync.Core:  38/38

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Adron
Adron merged commit b12ac86 into main Sep 24, 2026
1 check 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