/** * Integration test — exercises the Altair plugin's full surface against * a real `Router` instance, end to end. * * Stage-7 spec notes the desired Playwright + Yoga integration; that * arrives once `@suaveplan/api-graphql-yoga` lands. This file pins the * structural integration the engine package will then layer Playwright * on top of — HTML response, asset response, OTel observation, and * collision detection — using only the dependencies actually present * in the genesis tree today. */ import type { Counter, Meter, MeterProvider } from "@opentelemetry/api"; import { Router } from "@suaveplan/api-router"; import { describe, expect, it } from "@suaveplan/testing/runner"; import { createAltairInstruments, createAltairUi, GRAPHQL_UI_ALTAIR_CODES, GraphqlUiAltairError, } from "../../src/index.js"; interface FakeCounter { readonly name: string; addCalls: Array<{ value: number; attrs?: Readonly>; }>; } function buildFakeMeterProvider(): { provider: MeterProvider; counters: FakeCounter[]; } { const counters: FakeCounter[] = []; const provider: MeterProvider = { getMeter(): Meter { const meter: Meter = { createCounter(n: string) { const c: FakeCounter = { name: n, addCalls: [] }; counters.push(c); return { add(value, attrs) { c.addCalls.push({ value, attrs }); }, } as Counter; }, } as unknown as Meter; return meter; }, } as MeterProvider; return { provider, counters }; } describe("integration — altair end-to-end", () => { it("GET /graphql/ui → 200 HTML with embedded config", { timeout: 30000, }, () => { const { provider, counters } = buildFakeMeterProvider(); const instruments = createAltairInstruments({ meterProvider: provider, }); const router = new Router(); const plugin = createAltairUi(undefined, { instruments }); plugin.mount(router); const match = router.match("GET", "/graphql/ui"); expect(match).not.toBeNull(); const res = (match as NonNullable).handler() as { status: number; headers: Record; body: string; }; expect(res.status).toBe(200); expect(res.headers["Content-Type"]).toContain("text/html"); expect(res.body).toContain("Altair GraphQL Client"); expect(res.body).toContain('"graphqlEndpoint":"/graphql"'); // Counter observed. expect(counters[0]?.name).toBe("graphql.ui.requests_total"); expect(counters[0]?.addCalls[0]?.attrs).toEqual({ ui: "altair" }); }); it("asset request → 200 with correct content-type and Cache-Control", { timeout: 30000, }, () => { const router = new Router(); const plugin = createAltairUi(); plugin.mount(router); const html = plugin.renderHtml().body; const jsRef = html.match( /\/graphql\/ui\/assets\/([0-9a-f]{16})\/altair\.js/ ); if (!jsRef) throw new Error("js hash not found"); const hash = jsRef[1] as string; const m = router.match("GET", `/graphql/ui/assets/${hash}/altair.js`); expect(m).not.toBeNull(); const res = (m as NonNullable).handler({ params: { hash, name: "altair.js" }, }) as { status: number; headers: Record; body: string; }; expect(res.status).toBe(200); expect(res.headers["Content-Type"]).toContain("application/javascript"); expect(res.headers["Cache-Control"]).toContain("immutable"); }); it("collision against pre-mounted GET /graphql/ui refused with typed error", { timeout: 30000, }, () => { const router = new Router({ plugin: "first-owner" }); router.get("/graphql/ui", () => ({ status: 200, headers: {}, body: "", streaming: false, })); try { createAltairUi().mount(router); throw new Error("expected throw"); } catch (err) { expect(err).toBeInstanceOf(GraphqlUiAltairError); expect((err as GraphqlUiAltairError).altairCode).toBe( GRAPHQL_UI_ALTAIR_CODES.ENDPOINT_COLLISION ); } }); });