/* Unit tests for usage covering its documented public contract and regression-sensitive edge cases. Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. RFC-1212: initial test coverage. */ import { beforeEach, test, expect } from "vitest"; import { executeAction } from "./action-pipeline.ts"; import { __resetFreeWindowCache } from "./usage.ts"; import { createFixedWindowLimiter } from "./limits.ts"; import { buildAgentSurfaceManifest } from "@warpgogol/werkstatt-shared/agent"; import type { AgentUsageRecord } from "@warpgogol/werkstatt-shared/agent"; import type { CapabilityRecord } from "@warpgogol/werkstatt-shared/ontology"; import type { AgentGatePorts } from "./ports.ts"; const MANIFEST = buildAgentSurfaceManifest({ generatedAt: "2026-01-01T00:00:00.000Z", site: "test-site", baseUrl: "https://test.example", languages: { default: "en", supported: ["en"] }, }); const METERED: CapabilityRecord = { id: "quote.submit", version: 1, kind: "action", sideEffect: "write", title: { en: "Submit a quote" }, description: { en: "Creates a quote request." }, input: { type: "object", additionalProperties: false, properties: { email: { type: "string" } }, }, output: { type: "object", additionalProperties: false, properties: { receiptId: { type: "string" } }, }, requires: { entitlements: ["agent.actions", "quote.paid"], sections: [] }, integration: { eventKind: "lead", source: "agent" }, limits: { perMinutePerIp: 10, maxPayloadBytes: 10240 }, metering: { product: "agent-services.quote-run", unit: "invocation", pricePerUnit: { amount: 0.05, currency: "EUR" }, freeUnitsPerWindow: 2, window: "month", }, }; const UNMETERED: CapabilityRecord = { ...METERED, id: "lead.submit", metering: undefined }; const PREVIEW_METERED: CapabilityRecord = { ...METERED, id: "quote.prepare", sideEffect: "none", }; function ports(collector: AgentUsageRecord[], dispatchFails = false): AgentGatePorts { return { knowledge: { read: async () => null }, dispatch: { send: async (event) => { if (dispatchFails) throw new Error("transport down"); return { receiptId: event.eventId, status: "accepted" as const, duplicate: false, submittedAt: "2026-01-01T00:00:00.000Z", }; }, }, now: () => new Date("2026-01-01T00:00:00.000Z"), usage: (r) => collector.push(r), createRateLimiter: (max, windowSeconds = 60) => createFixedWindowLimiter(windowSeconds, max), }; } function run( capability: CapabilityRecord, collector: AgentUsageRecord[], opts: { dispatchFails?: boolean; verified?: string; idempotencyKey?: string } = {}, ) { return executeAction({ capability, manifest: MANIFEST, ports: ports(collector, opts.dispatchFails), validatedValue: { email: "agent@example.com" }, agentIdentity: opts.verified ? { verifiedAgentId: opts.verified } : {}, idempotencyKey: opts.idempotencyKey, bodyHash: "abc", }); } beforeEach(() => __resetFreeWindowCache()); test("AC-5: a dispatched metered call emits exactly one AgentUsageRecord", async () => { const records: AgentUsageRecord[] = []; const result = await run(METERED, records, { verified: "agent-42", idempotencyKey: "key-1", }); expect(result.kind).toBe("receipt"); expect(records).toHaveLength(1); expect(records[0]).toMatchObject({ schema: "gogol.agent-usage/1", siteId: "test-site", capability: "quote.submit", callerIdentity: "agent-42", units: 1, idempotencyKey: "key-1", }); }); test("AC-5: anonymous callers carry callerIdentity anonymous", async () => { const records: AgentUsageRecord[] = []; await run(METERED, records); expect(records[0]!.callerIdentity).toBe("anonymous"); }); test("AC-5: free allowance window marks early calls non-billable, then billable", async () => { const records: AgentUsageRecord[] = []; await run(METERED, records, { verified: "agent-42" }); await run(METERED, records, { verified: "agent-42" }); await run(METERED, records, { verified: "agent-42" }); expect(records.map((r) => r.billable)).toEqual([false, false, true]); }); test("AC-6: an unmetered capability emits no usage record", async () => { const records: AgentUsageRecord[] = []; const result = await run(UNMETERED, records); expect(result.kind).toBe("receipt"); expect(records).toHaveLength(0); }); test("AC-6: a dispatch failure emits no usage record", async () => { const records: AgentUsageRecord[] = []; const result = await run(METERED, records, { dispatchFails: true }); expect(result.kind).toBe("problem"); expect(records).toHaveLength(0); }); test("AC-6: a preview capability never dispatches → no usage record", async () => { const records: AgentUsageRecord[] = []; const result = await run(PREVIEW_METERED, records); expect(result.kind).toBe("preview"); expect(records).toHaveLength(0); }); test("no usage port → metering drops silently and the call still succeeds", async () => { const result = await executeAction({ capability: METERED, manifest: MANIFEST, ports: { knowledge: { read: async () => null }, dispatch: { send: async (event) => ({ receiptId: event.eventId, status: "accepted" as const, duplicate: false, submittedAt: "2026-01-01T00:00:00.000Z", }), }, now: () => new Date("2026-01-01T00:00:00.000Z"), }, validatedValue: { email: "a@b.c" }, agentIdentity: {}, }); expect(result.kind).toBe("receipt"); });