/*
Unit tests for idempotency covering its documented public contract and regression-sensitive edge cases.
- No network — fetch stubbed.
- RFC-1112: initial receipt store test.
*/
import { test, expect } from "vitest";
import { restRedisReceiptStore, type ReceiptStoreRecord } from "../idempotency.ts";
import type { ActionReceipt } from "@warpgogol/werkstatt-shared/agent";
const RECEIPT: ActionReceipt = {
receiptId: "11111111-1111-1111-1111-111111111111",
status: "accepted",
duplicate: false,
submittedAt: "2026-07-05T00:00:00.000Z",
};
const CONFIG = { url: "https://eu-redis.upstash.io", token: "redis-secret" };
function stubFetch(status: number, result: unknown) {
return async () => new Response(JSON.stringify({ result }), { status });
}
test("restRedisReceiptStore.get returns null on a miss", async () => {
const store = restRedisReceiptStore(CONFIG, stubFetch(200, null) as unknown as typeof fetch);
expect(await store.get("key-1")).toBeNull();
});
test("restRedisReceiptStore.get parses the stored record", async () => {
const record: ReceiptStoreRecord = { receipt: RECEIPT, bodyHash: "abc123" };
const store = restRedisReceiptStore(
CONFIG,
stubFetch(200, JSON.stringify(record)) as unknown as typeof fetch,
);
const got = await store.get("key-1");
expect(got).toEqual(record);
});
test("restRedisReceiptStore.get uses the configured key prefix", async () => {
let capturedBody = "";
const fetchSpy = (async (_url: unknown, init?: { body?: string }) => {
capturedBody = init?.body ?? "";
return new Response(JSON.stringify({ result: null }), { status: 200 });
}) as unknown as typeof fetch;
const store = restRedisReceiptStore({ ...CONFIG, prefix: "custom-prefix" }, fetchSpy);
await store.get("key-9");
expect(capturedBody).toBe(JSON.stringify(["GET", "custom-prefix:key-9"]));
});
test("restRedisReceiptStore.put issues SET key JSON NX EX ttl", async () => {
let capturedBody = "";
const fetchSpy = (async (_url: unknown, init?: { body?: string }) => {
capturedBody = init?.body ?? "";
return new Response(JSON.stringify({ result: "OK" }), { status: 200 });
}) as unknown as typeof fetch;
const store = restRedisReceiptStore(CONFIG, fetchSpy);
await store.put("key-1", "hash-1", RECEIPT, 86400);
const command = JSON.parse(capturedBody) as string[];
expect(command[0]).toBe("SET");
expect(command[1]).toBe("gogol-agent-idem:key-1");
const record = JSON.parse(command[2]!) as ReceiptStoreRecord;
expect(record.bodyHash).toBe("hash-1");
expect(record.receipt.receiptId).toBe(RECEIPT.receiptId);
expect(command.slice(3)).toEqual(["NX", "EX", "86400"]);
});
test("restRedisReceiptStore throws on transport errors", async () => {
const store = restRedisReceiptStore(CONFIG, stubFetch(500, null) as unknown as typeof fetch);
await expect(() => store.get("key-1")).rejects.toThrow();
await expect(() => store.put("key-1", "h", RECEIPT, 60)).rejects.toThrow();
});