Files
zeekay 40d120dbf7 refactor(api): say /v1/ in the Python callers
Every first-party Python caller and test now names the mount the API actually
answers on. Mechanical: URL literals only, and only where the /api/ was ours --
a preceding word character means it was a module path like insights/api/ or a
third-party host like slack.com/api/, and those are untouched.

Left saying /api/, each for a reason rather than for safety:
- products/warehouse_sources/.../sources/** is a catalogue of vendor endpoints
- services/llm-gateway deploys separately and is under a code freeze, so moving
  its two calls would break it during the skew
- the mcp_store and Slack/Teams redirect_uris are registered with third parties
  and must byte-match what was registered
- the Wizard and Raycast client-metadata paths are OAuth client_ids
- /api/surveys and /api/feature_flag/local_evaluation are SDK aliases the
  ingress routes to the Rust fleets, held by released SDK versions

Test URLconfs moved with the requests that call them, and the Datastore
user-selection fixtures now carry the leading slash that request.path has.
2026-08-08 18:38:44 -07:00

62 lines
2.6 KiB
TypeScript

import { expect, test } from '@playwright/test'
/**
* The API surface a signed-in user's session actually depends on.
*
* smoke.spec.ts opens scenes and catches anything that breaks while a scene
* loads. That misses endpoints no scene calls on mount -- `POST /v1/projects/:id/fn/`
* answered 500 in production for every request because of an unbound name, and
* no amount of scene-mounting would have found it.
*
* Read-only, deliberately. This runs against a real tenant with real data, so a
* write here would leave objects behind on every release; worse, a write that
* only passes because it fails is a test that starts creating junk the day the
* bug is fixed. Endpoints that only break under a write are covered by unit
* tests, not here.
*
* The session arrives as storageState from auth.setup.ts, so `request` is already
* signed in.
*/
// What this build carries. A 5xx is a fault anywhere; these must also not be 404,
// because a missing route here means a product surface lost its backend.
const CARRIED = [
'/v1/users/@me/',
'/v1/organizations/@current/',
'/v1/projects/1/',
'/v1/projects/1/event_definitions/?limit=5',
'/v1/projects/1/property_definitions/?limit=5',
'/v1/projects/1/insights/?limit=5',
'/v1/projects/1/dashboards/?limit=5',
'/v1/projects/1/feature_flags/?limit=5',
'/v1/projects/1/cohorts/?limit=5',
'/v1/projects/1/actions/?limit=5',
'/v1/projects/1/annotations/?limit=5',
'/v1/projects/1/surveys/?limit=5',
'/v1/projects/1/notebooks/?limit=5',
'/v1/projects/1/session_recordings/?limit=3',
]
// Implemented in upstream's separately-licensed ee/ tree, which this fork does not
// ship, so they have no viewset at all. Pinned as 404 rather than left unlisted:
// if one of these starts answering 500 it means something half-wired it back, and
// if one starts answering 200 the honest-state UI in capabilities.ts is now lying.
const NOT_CARRIED = ['/v1/projects/1/experiments/', '/v1/projects/1/groups_types/']
test.describe('the API a session depends on', () => {
for (const path of CARRIED) {
test(`GET ${path.split('?')[0]} works`, async ({ request }) => {
const r = await request.get(path)
expect(r.status(), `${path} -> ${r.status()}: ${(await r.text()).slice(0, 200)}`).toBeLessThan(400)
})
}
for (const path of NOT_CARRIED) {
test(`GET ${path} is absent, not broken`, async ({ request }) => {
// 404 is the honest answer for a feature this build does not carry.
// A 5xx would mean it is half-present and throwing.
expect(await request.get(path).then((r) => r.status())).toBe(404)
})
}
})