/** * Spec-conformance pinning test for IDMP-08 — Store adapter and LockProvider * are structural interfaces. Verifies that a plain object implementing the * shape is accepted everywhere a concrete class would be — no inheritance * required. */ import { describe, expect, it } from "@suaveplan/testing/runner"; import { DistributedIdempotencyGuard, IdempotencyGuard, type IdempotencyRecord, type IdempotencyStoreAdapter, type LockProvider, } from "../src/index.js"; describe("structural adapter interfaces [spec-conformance IDMP-08]", () => { it("IdempotencyStoreAdapter accepts a plain-object implementation", { timeout: 5000, }, async () => { const records = new Map(); const store: IdempotencyStoreAdapter = { async get(key) { return records.get(key); }, async set(key, record) { records.set(key, record); }, async delete(key) { return records.delete(key); }, async cleanup() { const before = records.size; records.clear(); return before; }, }; const guard = new IdempotencyGuard({ store }); const result = await guard.execute("k", async () => "ok"); expect(result).toBe("ok"); expect(records.size).toBe(1); }); it("LockProvider accepts a plain-object implementation", { timeout: 5000, }, async () => { const records = new Map(); const store: IdempotencyStoreAdapter = { async get(key) { return records.get(key); }, async set(key, record) { records.set(key, record); }, async delete(key) { return records.delete(key); }, async cleanup() { return 0; }, }; const taken = new Set(); const lockProvider: LockProvider = { async acquire(resource) { if (taken.has(resource)) return false; taken.add(resource); return true; }, async release(resource) { taken.delete(resource); }, }; const guard = new DistributedIdempotencyGuard({ store, lockProvider, retryCount: 0, retryBaseDelayMs: 0, }); const result = await guard.execute("k", async () => "ok"); expect(result).toBe("ok"); // Lock released after execution expect(taken.has("k")).toBe(false); }); });