Skip to content

Commit 0898f04

Browse files
bloveclaude
andcommitted
test(cockpit): assert every ag-ui topic's agent endpoint in production smoke
Production smoke asserted page reachability for two hardcoded topics. That cannot catch a dead runtime: a misconfigured agent URL, a missing proxy route, or an image that crashed on boot all still serve a healthy 200 index.html, and /ok stays green because Railway keeps serving the last good image. /agent/subagents was 404 in production for two and a half months with every check passing. Assert the endpoint each app actually talks to, and drive the topic list off the capability registry so a new topic is covered without editing the spec. An empty POST is a token-free canary — the request model rejects it before any graph or LLM call, so 422 means mounted, 404 means missing, 401 means internal-token mismatch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 3bf9de7 commit 0898f04

1 file changed

Lines changed: 55 additions & 8 deletions

File tree

apps/cockpit/e2e/production-smoke.spec.ts

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { expect, test } from '@playwright/test';
2+
import { capabilities } from '../scripts/capability-registry';
23

34
/**
45
* Production smoke test: verifies the deployed cockpit shell and deployed
@@ -70,6 +71,20 @@ const RENDER_CAPABILITIES = [
7071
'render/computed-functions',
7172
] as const;
7273

74+
/**
75+
* Driven off the capability registry so a newly added ag-ui topic is covered
76+
* automatically instead of silently going unasserted — the previous two
77+
* hardcoded checks covered interrupts and streaming only.
78+
*
79+
* Scoped to the ag-ui product: `runtimes` capabilities share the Railway
80+
* runtime but are not yet in the examples route table, so they have no
81+
* /ag-ui/<topic>/ URL to assert against.
82+
*/
83+
const AG_UI_TOPICS = capabilities
84+
.filter((c) => c.product === 'ag-ui' && c.pythonDir)
85+
.map((c) => c.topic)
86+
.sort();
87+
7388
const SEND_RECEIVE_TIMEOUT_MS = 30_000;
7489
test.describe('Production: Angular chat example apps load', () => {
7590
for (const cap of CHAT_CAPABILITIES) {
@@ -197,15 +212,47 @@ test.describe('ag-ui Railway runtime', () => {
197212
expect(await res.json()).toMatchObject({ ok: true });
198213
});
199214

200-
test('examples.threadplane.ai/ag-ui/interrupts is reachable', async ({ page }) => {
201-
const res = await page.goto(`${EXAMPLES_URL}/ag-ui/interrupts/`);
202-
expect(res?.status()).toBeLessThan(400);
203-
});
215+
for (const topic of AG_UI_TOPICS) {
216+
test(`ag-ui/${topic} page is reachable`, async ({ page }) => {
217+
const res = await page.goto(`${EXAMPLES_URL}/ag-ui/${topic}/`, {
218+
timeout: 15_000,
219+
});
220+
expect(res?.status()).toBeLessThan(400);
221+
});
204222

205-
test('examples.threadplane.ai/ag-ui/streaming is reachable', async ({ page }) => {
206-
const res = await page.goto(`${EXAMPLES_URL}/ag-ui/streaming/`);
207-
expect(res?.status()).toBeLessThan(400);
208-
});
223+
/**
224+
* Page reachability is not enough, and /ok is not either. A misconfigured
225+
* agent URL, a missing proxy route, or a Railway image that crashed on
226+
* boot all leave a healthy 200 index.html and a healthy /ok while the
227+
* runtime endpoint is dead — Railway keeps serving the last good image
228+
* when a new one fails to boot. That combination hid a broken
229+
* /agent/subagents for two and a half months.
230+
*
231+
* POSTing an empty body is a deliberate, token-free canary: the FastAPI
232+
* request model rejects it before any graph or LLM call runs.
233+
* 422 → healthy (proxy routed, internal token accepted, topic mounted)
234+
* 404 → topic missing from the deployed image, or no proxy route
235+
* 401 → AG_UI_INTERNAL_TOKEN mismatch between the proxy and Railway
236+
*/
237+
test(`ag-ui/${topic} agent endpoint is routed and mounted`, async ({
238+
request,
239+
}) => {
240+
const res = await request.post(`${EXAMPLES_URL}/ag-ui/${topic}/agent`, {
241+
headers: { Origin: EXAMPLES_URL, 'content-type': 'application/json' },
242+
data: {},
243+
});
244+
const status = res.status();
245+
expect(
246+
status,
247+
`POST /ag-ui/${topic}/agent returned ${status}; 404 means the deployed image has no /agent/${topic} route (check the Railway build/boot log) or the Vercel proxy route is missing`
248+
).not.toBe(404);
249+
expect(
250+
status,
251+
`POST /ag-ui/${topic}/agent returned 401 — AG_UI_INTERNAL_TOKEN mismatch between the Vercel proxy and Railway`
252+
).not.toBe(401);
253+
expect(status).toBeLessThan(500);
254+
});
255+
}
209256
});
210257

211258
/**

0 commit comments

Comments
 (0)