Three trust boundaries checked a caller's object and then read it again to use it. Between those two reads an accessor or a Proxy can answer differently, so the value that passed validation and the value that was installed were not the same value. A credential owner's answer was read field by field outside the auth boundary: a throwing `kind` getter escaped into the transport catch and an auth outage reached operators as `NETWORK_FAILURE`. Contract composition validated a contribution and then copied it, so a policy that answered 10,000 to the ceiling check and 999,999 to the copy installed the second value. The cursor runtime validated its profile once and re-read it on every page, so raising `maxPages` after construction widened a cap that had already been checked. `src/contracts/exact-snapshot.ts` is the one descriptor-based decoder they now share: every property is read exactly once, an accessor, a symbol, an inherited or non-enumerable field and a throwing trap all resolve to a typed failure, and validation runs on the owned copy. Separately, the `responseBody: NONE` probe awaited a bare `read()`. The deadline produced a bounded public result while the raw reader kept its lease, so the body stayed locked and the outer compensator could not cancel it. The probe now takes the operation lifetime and owns the cancel and the lock release itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
315 lines
9.5 KiB
TypeScript
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);
|
|
});
|
|
});
|