import { describe, expect, it } from "vitest"; import { generateKeyPairSync, sign } from "crypto"; import { ReceiptService } from "./receipts"; import type { ReceiptSummary } from "./types"; class ThrowingClient { async request() { throw new Error("offline"); } } describe("ReceiptService signature verification", () => { const { publicKey, privateKey } = generateKeyPairSync("ed25519"); const publicKeyPem = publicKey.export({ type: "spki", format: "pem" }).toString(); const baseReceipt: ReceiptSummary = { receiptId: "r1", jobId: "job-123", miner: "miner-1", coordinator: "coord-1", issuedAt: new Date().toISOString(), status: "completed", payload: { job_id: "job-123", provider: "miner-1", client: "client-1", }, }; const signPayload = (payload: Record): string => { const message = Buffer.from(JSON.stringify(payload)); return sign(null, message, privateKey).toString("base64"); }; it("validates with provided PEM keys", async () => { const sig = signPayload(baseReceipt.payload!); const receipt: ReceiptSummary = { ...baseReceipt, payload: { ...baseReceipt.payload, minerSignature: sig, coordinatorSignature: sig, }, }; const svc = new ReceiptService({ client: new ThrowingClient() as any, minerPublicKeyPem: publicKeyPem, coordinatorPublicKeyPem: publicKeyPem, signatureAlgorithm: "ed25519", }); const result = await svc.validateReceipt(receipt); expect(result.valid).toBe(true); }); it("fails with bad signature", async () => { const receipt: ReceiptSummary = { ...baseReceipt, payload: { ...baseReceipt.payload, minerSignature: "bad", coordinatorSignature: "bad", }, }; const svc = new ReceiptService({ client: new ThrowingClient() as any, minerPublicKeyPem: publicKeyPem, coordinatorPublicKeyPem: publicKeyPem, signatureAlgorithm: "ed25519", }); const result = await svc.validateReceipt(receipt); expect(result.valid).toBe(false); }); });