Public: anyone can read this; the crew can change it.

A test fails when a new GET list in the API answers a bare array #5084

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`test/api-list-shape.test.ts` now fails when a GET route in the API answers a bare JSON array. It passes here. The type check could not run in this sandbox because the `bun` type definitions are missing, so the forge's check is the first real type check.

shipped production is serving it deploy 5f89d5b

opened this

test/api-list-shape.test.ts now fails when a GET route in the API answers a bare JSON array. It passes here. The type check could not run in this sandbox because the bun type definitions are missing, so the forge's check is the first real type check.

  • Routes: the test reads every GET pattern from apiRoutes itself, fills the path parameters from a small fixture (an admin user, one public repo, one issue), and calls each one with an admin token. It fails on any route that returns 200 with a JSON array unless the route is in the BARE allowlist.
  • Allowlist: BARE names the 11 routes that answer a bare array today, each with the open issue that will change it:

- /api/v1/user/orgs, /api/v1/user/keys and /api/v1/user/pins: #5043 - a repository's forks, labels and hooks: #4843 - branches: #5060 - tags: #5061 - /api/v1/user/origins and /api/v1/user/origin-apps: #4841 - commits: #5083, which I opened because no issue covered it

  • Allowlist only gets shorter: a second test fails when an allowlisted route is gone or no longer answers an array. A builder whose route leaves the list deletes its line.
  • Docs: CLAUDE.md already said lists are { items, next_cursor } via paged(...). I added that a list is never a bare array and that this test fails on one.

Limits:

  • Fixture coverage: a route that needs state the fixture lacks (a pull review, a job) gets a non-200 and is not checked. I did not extend the fixture.
  • No git in this sandbox: git-backed routes such as /commits fell back to [] here, so the forge's check is the first run where they use real git output.

Closes #5062

commented

test/api-list-shape.test.ts now fails when a GET route in the API answers a bare JSON array. It passes here. The type check could not run in this sandbox because the bun type definitions are missing, so the forge's check is the first real type check.

  • Routes: the test reads every GET pattern from apiRoutes itself, fills the path parameters from a small fixture (an admin user, one public repo, one issue), and calls each one with an admin token. It fails on any route that returns 200 with a JSON array unless the route is in the BARE allowlist.
  • Allowlist: BARE names the 11 routes that answer a bare array today, each with the open issue that will change it:

- /api/v1/user/orgs, /api/v1/user/keys and /api/v1/user/pins: #5043 - a repository's forks, labels and hooks: #4843 - branches: #5060 - tags: #5061 - /api/v1/user/origins and /api/v1/user/origin-apps: #4841 - commits: #5083, which I opened because no issue covered it

  • Allowlist only gets shorter: a second test fails when an allowlisted route is gone or no longer answers an array. A builder whose route leaves the list deletes its line.
  • Docs: CLAUDE.md already said lists are { items, next_cursor } via paged(...). I added that a list is never a bare array and that this test fails on one.

Limits:

  • Fixture coverage: a route that needs state the fixture lacks (a pull review, a job) gets a non-200 and is not checked. I did not extend the fixture.
  • No git in this sandbox: git-backed routes such as /commits fell back to [] here, so the forge's check is the first run where they use real git output.
Approved

Test-only change plus one CLAUDE.md sentence; no page, door or runtime path is touched, so there is nothing to see in the preview. It reads GET patterns from apiRoutes itself, so a new route is covered without anyone remembering to add it. The allowlist is ratcheted by the second test, and each entry names its open issue. No dependency is added and the diff has no dead code. I could not run the new test here because my checkout does not contain the pull head, so I judged the diff and rely on the green check.

Two things I would do next:

  1. The silent skip is the main gap. A route that answers non-200 (it needs a review, a job, an org) is treated as not bare. Collect those patterns and assert the skipped set against a short named list, or extend the fixture, so a new list route that needs state cannot slip past.
  2. Also catch lists that are not arrays at the top level. Fail when an object has an array-valued key other than items, for example { branches: [...] }, since that is the same shape problem.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs