/*
RFC-0191: prove the lifecycle event contracts — the closed kind catalog, the typed
payload schema the Stripe adapter validates against, and the guard. Pure — no I/O, no Stripe.
- No network, no Stripe SDK — the schema is pure.
- RFC-0191: initial lifecycle contract test.
*/
import { test, expect } from "vitest";
import {
LIFECYCLE_EVENT_KINDS,
LifecycleEventPayloadSchema,
isLifecycleEventKind,
} from "../lifecycle.ts";
test("the lifecycle catalog covers invoice, subscription, and refund events", () => {
for (const k of [
"invoice.paid",
"invoice.payment_failed",
"subscription.created",
"subscription.updated",
"subscription.canceled",
"payment.refunded",
]) {
expect((LIFECYCLE_EVENT_KINDS as readonly string[]).includes(k)).toBeTruthy();
}
});
test("the payload schema accepts a Stripe-mapped invoice.paid event", () => {
const r = LifecycleEventPayloadSchema.safeParse({
eventKind: "invoice.paid",
stripeEventId: "evt_123",
stripeCustomerId: "cus_123",
stripeInvoiceId: "in_123",
invoiceKind: "cycle",
invoiceStatus: "paid",
amountCents: 7000,
currency: "EUR",
});
expect(r.success).toBe(true);
});
test("the payload schema rejects an unknown event kind and a negative amount", () => {
expect(
LifecycleEventPayloadSchema.safeParse({
eventKind: "invoice.exploded",
stripeEventId: "evt_1",
stripeCustomerId: "cus_1",
}).success,
).toBe(false);
expect(
LifecycleEventPayloadSchema.safeParse({
eventKind: "invoice.paid",
stripeEventId: "evt_1",
stripeCustomerId: "cus_1",
amountCents: -5,
}).success,
).toBe(false);
});
test("isLifecycleEventKind guards the closed catalog", () => {
expect(isLifecycleEventKind("subscription.updated")).toBe(true);
expect(isLifecycleEventKind("offer.selected")).toBe(false); // a funnel kind, not lifecycle
});