Files
tech-log-frontend/tests/integration/http-execution-v3-observability.test.ts
T
DongHyeonkaandClaude Opus 5 4bff9ca151 chore: sync the frontend template from 4dc033c to 8157ad4
The product was materialized from the template at `4dc033c` and has stayed
on it through 43 template commits, so it was missing all three rounds of
adapter remediation — including files it never had, such as the shared
`abortable-operation` primitive and the `exact-snapshot` decoder that
later fixes are written against. Taking only the newest round was not
possible for that reason: the delta is coherent only as a whole.

The product had not touched `src/adapters` at all since materialization,
so the 140-file delta applied with a three-way merge and no conflicts.
`package.json` was the single overlap and merged cleanly: the product owns
`name`, the template contributed `check:adapter-inventory`,
`check:remediation-ledger` and the image-resolve-signal type fixture.
All 24 product-owned files — README, index.html, CI workflow, i18n
catalog, home page, generated schemas, evidence scripts, component and
visual snapshots — are byte-identical to `main`.

`template.lock.json` now pins the synced revision and tree.

Verified in this repository, not inherited from the template: six type
projects, lint, nine gates (adapter inventory, remediation ledger,
registries, diagnostics, realtime boundaries, architecture, browser
file/storage boundaries, optional recipes, documentation), the production
build, and 2,054 of 2,073 tests. The 19 failures are all in
`tests/unit/ci-artifact-contract.test.ts` and are the same pre-existing
sandbox RLIMIT, EMFILE, umask and `/tmp` permission behaviour the template
records; four suites that failed once under parallel load pass in
isolation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 12:04:58 +09:00

315 lines
9.5 KiB
TypeScript

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<Record<string, unknown>>;
}>;
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<string, unknown>,
) {
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("<html/>", {
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: {},
}),
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: {},
}),
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: {},
}),
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: {},
}),
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<Readonly<Record<string, unknown>>> = [];
const installed = createReferenceFeatureInstalledInput({
contractOperations: Object.freeze({
async execute(
_operationId: string,
_input: unknown,
context?: Readonly<Record<string, unknown>>,
) {
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: {},
}),
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);
});
});