import { describe, expect, it } from "vitest"; import { z } from "zod"; import { PaymentPlan, PaymentPlansResponse, TipPaymentPlansResponse, } from "@/data/dto/payment"; describe("PaymentPlan", () => { it("parses the camelCase payment plan shape", () => { const plan = PaymentPlan.from({ planId: "vip_monthly", planName: "VIP Monthly", orderType: "vip_monthly", vipDays: 30, dolAmount: null, creditBalance: 3000, amountCents: 1999, originalAmountCents: 2499, dailyPriceCents: 66, currency: "JPY", }); expect(plan.planId).toBe("vip_monthly"); expect(plan.creditBalance).toBe(3000); expect(plan.toJson()).toEqual({ planId: "vip_monthly", planName: "VIP Monthly", orderType: "vip_monthly", vipDays: 30, dolAmount: null, creditBalance: 3000, amountCents: 1999, originalAmountCents: 2499, dailyPriceCents: 66, currency: "JPY", isFirstRechargeOffer: false, firstRechargeDiscountPercent: null, promotionType: null, }); }); it("rejects the legacy snake_case payment plan shape", () => { expect(() => PaymentPlan.fromJson({ plan_id: "vip_monthly", plan_name: "VIP Monthly", order_type: "vip_monthly", amount_cents: 1999, original_amount_cents: 2499, daily_price_cents: 66, currency: "JPY", vip_days: 30, dol_amount: null, }), ).toThrow(z.ZodError); }); }); describe("PaymentPlansResponse", () => { it("parses plans from the new backend response data shape", () => { const response = PaymentPlansResponse.from({ plans: [ { planId: "dol_1000", planName: "1000 Credits", orderType: "dol", vipDays: null, dolAmount: 1000, creditBalance: 1000, amountCents: 990, originalAmountCents: null, dailyPriceCents: null, currency: "USD", }, ], }); expect(response.plans).toHaveLength(1); expect(response.plans[0]?.dolAmount).toBe(1000); }); it("parses first recharge offer metadata", () => { const response = PaymentPlansResponse.from({ isFirstRecharge: true, firstRechargeOffer: { enabled: true, type: "first_recharge_half_price", discountPercent: 50, }, plans: [ { planId: "vip_monthly", planName: "VIP Monthly", orderType: "vip_monthly", vipDays: 30, dolAmount: null, creditBalance: 3000, amountCents: 995, originalAmountCents: 1990, dailyPriceCents: null, currency: "USD", isFirstRechargeOffer: true, firstRechargeDiscountPercent: 50, promotionType: "first_recharge_half_price", }, ], }); expect(response.isFirstRecharge).toBe(true); expect(response.firstRechargeOffer?.discountPercent).toBe(50); expect(response.plans[0]).toMatchObject({ amountCents: 995, originalAmountCents: 1990, isFirstRechargeOffer: true, firstRechargeDiscountPercent: 50, promotionType: "first_recharge_half_price", }); }); it("defaults nullable first recharge offer metadata", () => { const response = PaymentPlansResponse.from({ isFirstRecharge: true, firstRechargeOffer: { enabled: null, type: null, discountPercent: null, }, plans: [], }); expect(response.firstRechargeOffer).toEqual({ enabled: false, type: "", discountPercent: 0, }); }); }); describe("TipPaymentPlansResponse", () => { it("keeps only fields used by the tip payment flow", () => { const response = TipPaymentPlansResponse.fromJson({ plans: [ { planId: "tip_coffee_usd_4_99", planName: "Small Coffee", orderType: "tip", tipType: "coffee_small", description: "Buy Elio a small coffee", amountCents: 499, currency: "USD", autoRenew: false, isFirstRechargeOffer: false, firstRechargeDiscountPercent: 0, }, ], }); expect(response.toJson()).toEqual({ plans: [ { planId: "tip_coffee_usd_4_99", planName: "Small Coffee", amountCents: 499, currency: "USD", }, ], }); }); });