import { expect, test, type Page } from '@playwright/test' // Showcase D23: register() in instrumentation.ts starts the OTLP exporters when the platform // injects OTEL_EXPORTER_OTLP_ENDPOINT, and every server action logs through shared/api/observe's // logger(). This proves the whole chain against a real `next start` production server (not dev, // not a mock of register()): a log a server action emits reaches the fake platform's // /otel/v1/logs route within the exporter's 5 s batch interval. const platform = 'http://127.0.0.1:4010' interface LogRecordJson { body?: { stringValue?: string } attributes?: Array<{ key: string; value: { stringValue?: string } }> } type OtelState = { otel: { logs: Array<{ authorization: string | null; body: { resourceLogs: Array<{ scopeLogs: Array<{ logRecords: LogRecordJson[] }> }> } }> } } async function otelLogRecords(): Promise { const response = await fetch(`${platform}/__e2e/state`) const state = (await response.json()) as OtelState return state.otel.logs.flatMap((post) => post.body.resourceLogs.flatMap((r) => r.scopeLogs.flatMap((s) => s.logRecords))) } function attr(record: LogRecordJson, key: string): string | undefined { return record.attributes?.find((kv) => kv.key === key)?.value.stringValue } async function createNote(page: Page, title: string): Promise { await page.goto('/') await page.getByLabel('Title').fill(title) await page.getByLabel('Body').fill('observability e2e') await page.getByRole('button', { name: 'Create note' }).click() await expect(page.getByRole('heading', { name: title })).toBeVisible() } test('a note.created server action log reaches the OTLP receiver within the batch interval', async ({ page }) => { const title = `otlp ${Date.now()}-${Math.random().toString(36).slice(2, 7)}` await createNote(page, title) await expect .poll( async () => (await otelLogRecords()).some((r) => r.body?.stringValue === 'note.created' && attr(r, 'outcome') === 'ok'), // BatchExporter's default flush interval is 5 s (exporter.ts); this allows one full // interval plus slack for the request round trip and CI scheduling jitter. { timeout: 9_000 }, ) .toBe(true) }) test('the OTLP request carries the deployment token, not the public client', async ({ page }) => { const title = `otlp-auth ${Date.now()}-${Math.random().toString(36).slice(2, 7)}` await createNote(page, title) await expect .poll(async () => (await otelLogRecords()).some((r) => r.body?.stringValue === 'note.created'), { timeout: 9_000 }) .toBe(true) const response = await fetch(`${platform}/__e2e/state`) const state = (await response.json()) as OtelState expect(state.otel.logs.every((post) => post.authorization === 'Bearer e2e-deployment-token')).toBe(true) })