import { describe, it, expect } from "vitest"; import { createPendingOp, isValidPendingOp, } from "../../../src/contracts/pending-op.js"; import { PENDING_OP_FIXTURES } from "../../fixtures/pending-op.js"; describe("pending-op contract", () => { describe("createPendingOp()", () => { it("creates a pending op with required fields", () => { const op = createPendingOp("op-1", "user-1", "hidden", "active"); expect(op.opId).toBe("op-1"); expect(op.userId).toBe("user-1"); expect(op.targetState).toBe("hidden"); expect(op.previousState).toBe("active"); expect(typeof op.issuedAt).toBe("number"); expect(Number.isFinite(op.issuedAt)).toBe(true); expect(op.issuedAt).toBeGreaterThan(0); expect(op.timeoutId).toBeNull(); }); }); describe("isValidPendingOp()", () => { it("accepts valid fixture", () => { expect(() => isValidPendingOp(PENDING_OP_FIXTURES.valid)).not.toThrow(); }); it("accepts timeoutId: null", () => { expect(() => isValidPendingOp(PENDING_OP_FIXTURES.timeoutNull)).not.toThrow(); }); it("accepts expired issuedAt (age check is caller's job)", () => { expect(() => isValidPendingOp(PENDING_OP_FIXTURES.expiredIssuedAt)).not.toThrow(); }); it("throws on empty opId", () => { expect(() => isValidPendingOp(PENDING_OP_FIXTURES.emptyOpId)).toThrow(TypeError); }); it("throws on non-finite issuedAt", () => { const bad = { ...PENDING_OP_FIXTURES.valid, issuedAt: NaN }; expect(() => isValidPendingOp(bad)).toThrow(TypeError); }); it("throws on negative issuedAt", () => { const bad = { ...PENDING_OP_FIXTURES.valid, issuedAt: -1 }; expect(() => isValidPendingOp(bad)).toThrow(TypeError); }); it("throws if not an object", () => { expect(() => isValidPendingOp(null)).toThrow(TypeError); }); it("throws on unknown keys", () => { const bad = { ...PENDING_OP_FIXTURES.valid, extra: true }; expect(() => isValidPendingOp(bad)).toThrow(TypeError); }); it("throws on string timeoutId", () => { const bad = { ...PENDING_OP_FIXTURES.valid, timeoutId: "not-a-number" }; expect(() => isValidPendingOp(bad)).toThrow(TypeError); }); }); });