import { vi } from "vitest"; import { fromPromise } from "xstate"; import { CreatePaymentOrderResponse, CreatePaymentOrderResponseSchema, type PayChannel, PaymentOrderStatusResponseSchema, PaymentPlansResponse, PaymentPlansResponseSchema, } from "@/data/schemas/payment"; import { paymentMachine } from "@/stores/payment/payment-machine"; import type { PaymentPlanCatalog } from "@/stores/payment/payment-state"; export interface PaymentPlansActorInput { catalog: PaymentPlanCatalog; } export interface CreateOrderInput { planId: string; payChannel: PayChannel; autoRenew: boolean; } export type CreateOrderSpy = (input: CreateOrderInput) => void; export const monthlyPlan = { planId: "vip_monthly", planName: "VIP Monthly", orderType: "vip_monthly", vipDays: 30, dolAmount: null, creditBalance: 3000, amountCents: 999, originalAmountCents: null, dailyPriceCents: 33, currency: "usd", }; export const lifetimePlan = { planId: "vip_lifetime", planName: "VIP Lifetime", orderType: "vip_lifetime", vipDays: 3650, dolAmount: null, creditBalance: 0, amountCents: 4999, originalAmountCents: null, dailyPriceCents: null, currency: "usd", }; export const creditPlan = { planId: "dol_1000", planName: "1000 Credits", orderType: "dol", vipDays: null, dolAmount: 1000, creditBalance: 1000, amountCents: 990, originalAmountCents: null, dailyPriceCents: null, currency: "usd", }; export const quarterlyPlan = { planId: "vip_quarterly", planName: "VIP Quarterly", orderType: "vip_quarterly", vipDays: 90, dolAmount: null, creditBalance: 9000, amountCents: 2499, originalAmountCents: null, dailyPriceCents: 28, currency: "usd", }; export const tipPlan = { planId: "tip_coffee_usd_4_99", planName: "Small Coffee", orderType: "tip", vipDays: null, dolAmount: null, creditBalance: 0, amountCents: 499, originalAmountCents: null, dailyPriceCents: null, currency: "USD", }; export function createTestPaymentMachine( overrides: Partial<{ cachedPlans: PaymentPlansResponse | null; refreshedPlans: PaymentPlansResponse; refreshPlans: () => Promise; onLoadCatalog: (catalog: PaymentPlanCatalog) => void; onRefreshCatalog: (catalog: PaymentPlanCatalog) => void; createOrderSpy: CreateOrderSpy; createOrderError: Error; orderStatus: "pending" | "paid" | "failed"; orderStatuses: ("pending" | "paid" | "failed")[]; }> = {}, ) { const createOrderSpy = overrides.createOrderSpy ?? vi.fn(); const orderStatus = overrides.orderStatus ?? "paid"; const orderStatuses = overrides.orderStatuses ?? []; let pollCount = 0; return paymentMachine.provide({ actors: { loadCachedPlans: fromPromise< PaymentPlansResponse | null, PaymentPlansActorInput >(async ({ input }) => { overrides.onLoadCatalog?.(input.catalog); return overrides.cachedPlans ?? null; }), refreshPlans: fromPromise( async ({ input }) => { overrides.onRefreshCatalog?.(input.catalog); if (overrides.refreshPlans) return overrides.refreshPlans(); return ( overrides.refreshedPlans ?? PaymentPlansResponseSchema.parse({ plans: [monthlyPlan, lifetimePlan, creditPlan], }) ); }, ), createOrder: fromPromise( async ({ input }) => { createOrderSpy(input); if (overrides.createOrderError) throw overrides.createOrderError; return CreatePaymentOrderResponseSchema.parse({ orderId: "pay_test_001", payParams: { provider: "stripe", clientSecret: "pi_test_secret_test", }, }); }, ), pollOrderStatus: fromPromise(async () => { const status = orderStatuses[Math.min(pollCount, orderStatuses.length - 1)] ?? orderStatus; pollCount += 1; return PaymentOrderStatusResponseSchema.parse({ orderId: "pay_test_001", status, orderType: "vip_monthly", planId: "vip_monthly", }); }), }, }); }