import { describe, expect, it, vi } from "vitest"; import { createContractHttpExecutor } from "../../src/adapters/http/http-execution-v3.ts"; import type { HttpExecutionObservation, } from "../../src/adapters/http/http-execution-v3.ts"; import { createHttpObservationProjector } from "../../src/bootstrap/runtime-adapters.ts"; import { createReferenceFeatureInstalledInput } from "../../src/features/reference-feature/adapters/create-reference-feature-input.ts"; import { DIAGNOSTIC_CONTEXT_ALLOWLIST, projectDiagnosticRecord, type DiagnosticRecordInput, } from "../../src/contracts/diagnostics.ts"; import { projectTelemetryEvent, type TelemetryEventName, } from "../../src/contracts/telemetry.ts"; import { TEST_CREATE_HTTP_CONTRACT, TEST_LIST_HTTP_CONTRACT, } from "../helpers/external-contract-fixture.ts"; const ROUTE_ID = "TEST_ROUTE"; function scopeSnapshot(isCurrent: () => boolean = () => true) { return Object.freeze({ generation: 1, fingerprint: "scope-1", identities: Object.freeze({}) as never, signal: new AbortController().signal, isCurrent, }); } type RecordedDiagnostic = DiagnosticRecordInput; type RecordedTelemetry = Readonly<{ eventName: TelemetryEventName; attributes: Readonly>; }>; function recordingSinks() { const diagnostics: RecordedDiagnostic[] = []; const telemetry: RecordedTelemetry[] = []; return { diagnostics, telemetry, projector: createHttpObservationProjector({ diagnostics: { record(input: DiagnosticRecordInput) { diagnostics.push(input); }, }, telemetry: { emit( eventName: TelemetryEventName, attributes: Record, ) { telemetry.push(Object.freeze({ eventName, attributes })); }, }, }), }; } describe("V3 HTTP observability projection", () => { it("projects every V3 terminal outcome through the closed diagnostics allowlist", async () => { const sinks = recordingSinks(); const cases: readonly Readonly<{ label: string; fetcher: typeof fetch; }>[] = [ { label: "SUCCESS", fetcher: (async () => Response.json([{ id: "a", name: "A" }])) as unknown as typeof fetch, }, { label: "PROBLEM", fetcher: (async () => Response.json( { type: "about:blank", title: "nope", status: 400 }, { status: 400 }, )) as unknown as typeof fetch, }, { label: "TRANSPORT_FAILURE", fetcher: (async () => { throw new TypeError("network down"); }) as unknown as typeof fetch, }, { label: "CONTRACT_VIOLATION", fetcher: (async () => new Response("", { status: 200, headers: { "content-type": "text/html" }, })) as unknown as typeof fetch, }, ]; for (const testCase of cases) { const executor = createContractHttpExecutor({ baseUrl: "https://api.example/", maxRetryAttempts: 0, attachCredentials: () => ({ kind: "READY" as const, headers: {}, credentials: "omit" as const, }), fetcher: testCase.fetcher, observe: sinks.projector, }); await executor.execute( TEST_LIST_HTTP_CONTRACT, { limit: 5 }, { routeId: ROUTE_ID, scope: scopeSnapshot() }, ); } expect(sinks.diagnostics).toHaveLength(cases.length); for (const recorded of sinks.diagnostics) { expect(recorded.eventId).toBe("http.request.completed"); const contextKeys = Object.keys(recorded.context ?? {}); expect(contextKeys).toEqual( expect.arrayContaining([ "route_id", "operation_id", "operation", "outcome", "error_kind", "http_status_group", "attempt_count_bucket", "duration_bucket", ]), ); for (const key of contextKeys) { expect(DIAGNOSTIC_CONTEXT_ALLOWLIST).toContain(key); } expect(contextKeys).not.toContain("attempts"); expect(contextKeys).not.toContain("certainty"); const projection = projectDiagnosticRecord(recorded); expect(projection.success).toBe(true); } }); it("emits one failure telemetry event for a non-abort terminal failure", async () => { const sinks = recordingSinks(); const executor = createContractHttpExecutor({ baseUrl: "https://api.example/", maxRetryAttempts: 0, attachCredentials: () => ({ kind: "READY" as const, headers: {}, credentials: "omit" as const, }), fetcher: (async () => { throw new TypeError("network down"); }) as unknown as typeof fetch, observe: sinks.projector, }); const outcome = await executor.execute( TEST_LIST_HTTP_CONTRACT, { limit: 5 }, { routeId: ROUTE_ID, scope: scopeSnapshot() }, ); expect(outcome.kind).toBe("TRANSPORT_FAILURE"); expect(sinks.telemetry).toHaveLength(1); const emitted = sinks.telemetry[0]; expect(emitted?.eventName).toBe("api.request.failed"); expect(emitted?.attributes).toMatchObject({ route_id: ROUTE_ID, operation_id: "TEST_LIST_ENTITIES", error_kind: "NETWORK_FAILURE", http_status_group: "none", attempt_count_bucket: "1", }); const projected = projectTelemetryEvent( emitted?.eventName ?? "api.request.failed", emitted?.attributes ?? {}, ); expect(projected.success).toBe(true); expect(sinks.diagnostics).toHaveLength(1); }); it("does not emit failure telemetry for caller cancellation or scope fencing", async () => { const cancelled = recordingSinks(); const callerController = new AbortController(); callerController.abort(); const cancelledExecutor = createContractHttpExecutor({ baseUrl: "https://api.example/", maxRetryAttempts: 0, attachCredentials: () => ({ kind: "READY" as const, headers: {}, credentials: "omit" as const, }), fetcher: (async () => Response.json([{ id: "a", name: "A" }])) as unknown as typeof fetch, observe: cancelled.projector, }); const cancelledOutcome = await cancelledExecutor.execute( TEST_LIST_HTTP_CONTRACT, { limit: 5 }, { routeId: ROUTE_ID, scope: scopeSnapshot(), signal: callerController.signal, }, ); expect(cancelledOutcome.kind).toBe("CANCELLED"); expect(cancelled.diagnostics).toHaveLength(1); expect(cancelled.telemetry).toHaveLength(0); const fenced = recordingSinks(); const fencedExecutor = createContractHttpExecutor({ baseUrl: "https://api.example/", maxRetryAttempts: 0, attachCredentials: () => ({ kind: "READY" as const, headers: {}, credentials: "omit" as const, }), fetcher: (async () => Response.json([{ id: "a", name: "A" }])) as unknown as typeof fetch, observe: fenced.projector, }); const fencedOutcome = await fencedExecutor.execute( TEST_LIST_HTTP_CONTRACT, { limit: 5 }, { routeId: ROUTE_ID, scope: scopeSnapshot(() => false) }, ); expect(fencedOutcome.kind).toBe("CONTRACT_VIOLATION"); expect(fenced.diagnostics).toHaveLength(1); expect(fenced.telemetry).toHaveLength(0); }); it("preserves the feature route id through the installed operation executor", async () => { const seen: Array>> = []; const installed = createReferenceFeatureInstalledInput({ contractOperations: Object.freeze({ async execute( _operationId: string, _input: unknown, context?: Readonly>, ) { seen.push(Object.freeze({ ...(context ?? {}) })); return Object.freeze({ kind: "SUCCESS" as const, value: [], metadata: Object.freeze({ status: 200 }), effect: "NOT_APPLICABLE" as const, }); }, }), }); await installed.input.listResources({ limit: 20 }); await installed.input.getResource("resource-1"); expect(seen.map((context) => context.routeId)).toEqual([ "REFERENCE_RESOURCE_LIST", "REFERENCE_RESOURCE_DETAIL", ]); }); it("cannot change the HTTP result when diagnostics or telemetry throws", async () => { const projector = createHttpObservationProjector({ diagnostics: { record() { throw new Error("diagnostics sink exploded"); }, }, telemetry: { emit() { throw new Error("telemetry sink exploded"); }, }, }); const observe = vi.fn((observation: HttpExecutionObservation) => { projector(observation); }); const executor = createContractHttpExecutor({ baseUrl: "https://api.example/", maxRetryAttempts: 0, attachCredentials: () => ({ kind: "READY" as const, headers: {}, credentials: "omit" as const, }), fetcher: (async () => Response.json({ id: "created", name: "Created" }, { status: 201, })) as unknown as typeof fetch, observe, }); const outcome = await executor.execute( TEST_CREATE_HTTP_CONTRACT, { name: "Created" }, { routeId: ROUTE_ID, scope: scopeSnapshot(), intent: Object.freeze({ intentId: "intent-1", operationId: "TEST_CREATE_ENTITY", canonicalInputIdentity: "opaque-input-identity", idempotencyKey: "key-1", createdAtMonotonicMs: 1, }), }, ); expect(outcome.kind).toBe("SUCCESS"); expect(observe).toHaveBeenCalledTimes(1); }); });