import { describe, expect, it } from "vitest"; import { UserEntitlementsSchema } from "@/data/schemas/user"; import { UserSchema } from "@/data/schemas/user/user"; describe("User", () => { it("parses the latest backend user payload", () => { const user = UserSchema.parse({ id: "d7bb2b3d-e9c5-474c-85fe-f503048f30dd", username: "guest_bbc90a38", email: "device_bbc90a38b996187348f54cf2cb0b789e@guest.local", platform: "web", country: "Hong Kong", countryCode: "HK", intimacy: 0, dolBalance: 0, creditBalance: 0, dailyFreeChatLimit: 30, dailyFreeChatUsed: 1, dailyFreeChatRemaining: 29, dailyFreePrivateLimit: 2, dailyFreePrivateUsed: 0, dailyFreePrivateRemaining: 2, dailyQuotas: { chat: { limit: 30, used: 1, remaining: 29, }, privateMessage: { limit: 2, used: 0, remaining: 2, }, }, personalityTraits: { caring: 0.5, playful: 0.5, serious: 0.5, cheerful: 0.5, romantic: 0.5, }, preferredLanguage: "zh", createdAt: "2026-06-12T08:08:27.378706+00:00", lastMessageAt: "2026-06-24T11:00:23.156561+00:00", }); expect(user).toEqual({ id: "d7bb2b3d-e9c5-474c-85fe-f503048f30dd", username: "guest_bbc90a38", email: "device_bbc90a38b996187348f54cf2cb0b789e@guest.local", platform: "web", country: "Hong Kong", countryCode: "HK", fbAsid: "", fbPsid: "", intimacy: 0, dolBalance: 0, creditBalance: 0, dailyFreeChatLimit: 30, dailyFreeChatUsed: 1, dailyFreeChatRemaining: 29, dailyFreePrivateLimit: 2, dailyFreePrivateUsed: 0, dailyFreePrivateRemaining: 2, personalityTraits: { caring: 0.5, playful: 0.5, serious: 0.5, cheerful: 0.5, romantic: 0.5, }, preferredLanguage: "zh", createdAt: "2026-06-12T08:08:27.378706+00:00", lastMessageAt: "2026-06-24T11:00:23.156561+00:00", }); expect("dailyQuotas" in user).toBe(false); }); it("defaults nullable nested user fields through schema helpers", () => { const user = UserSchema.parse({ id: "user-1", username: "guest_user", personalityTraits: null, }); expect(user.personalityTraits).toEqual({ caring: 0.5, playful: 0.5, serious: 0.5, cheerful: 0.5, romantic: 0.5, }); }); it("parses the latest backend entitlements payload", () => { const entitlements = UserEntitlementsSchema.parse({ userId: "user-1", isGuest: false, isVip: true, vipExpiresAt: "2026-07-26T00:00:00+00:00", creditBalance: 120, dolBalance: 120, policy: { membershipState: "vip", nonVipEntitlementsShared: true, guestSameAsLoggedInNonVip: true, refreshAfterPayment: "GET /api/user/entitlements", }, costs: { normal_message: 2, private_message: 10, voice_message: 20, photo: 40, voice_call_minute: 50, private_album_10: 300, private_album_20: 600, }, quotas: { normalChatFreeDaily: 30, privateUnlockFreeDaily: 1, voiceMessageFreeDaily: 0, photoFreeDaily: 0, }, historyUnlock: { enabled: true, order: "oldest_first", chargeMode: "highest_cost_per_locked_message", insufficientBalanceBehavior: "no_deduction", costs: { private_message: 10, voice_message: 20, photo: 40, }, }, voiceCallBilling: { enabled: true, protocolVersion: 2, chargeMode: "per_turn", sharesNormalChatFreeDaily: true, freeTurnsDaily: 30, costPerPaidTurn: 2, usageType: "voice_call_turn", legacyMinuteCostUsed: false, }, }); expect(entitlements.isVip).toBe(true); expect(entitlements.creditBalance).toBe(120); expect(entitlements.historyUnlock.costs.photo).toBe(40); expect(entitlements.voiceCallBilling).toMatchObject({ protocolVersion: 2, chargeMode: "per_turn", costPerPaidTurn: 2, }); }); it("defaults nullable entitlement sections through schema helpers", () => { const entitlements = UserEntitlementsSchema.parse({ userId: "user-1", policy: null, costs: null, quotas: null, historyUnlock: null, }); expect(entitlements.policy.membershipState).toBe(""); expect(entitlements.costs.private_message).toBe(0); expect(entitlements.quotas.normalChatFreeDaily).toBe(0); expect(entitlements.historyUnlock).toMatchObject({ enabled: false, order: "", chargeMode: "", insufficientBalanceBehavior: "", costs: { private_message: 0, voice_message: 0, photo: 0, }, }); }); });