import { describe, expect, it } from "vitest"; import type { UserView } from "@/stores/user/user-view"; import { getProfileViewModel, getProfileWalletView, normalizeProfileCount, } from "../profile-view-model"; const baseUser: UserView = { id: "user-1", username: "Chase", countryCode: "US", creditBalance: 120, dailyFreeChatLimit: 30, dailyFreeChatRemaining: 24, dailyFreePrivateLimit: 4, dailyFreePrivateRemaining: 2, isVip: false, }; describe("getProfileViewModel", () => { it("derives guest state for logged-out users", () => { const view = getProfileViewModel({ loginStatus: "notLoggedIn", user: { currentUser: null, avatarUrl: null, creditBalance: 0, }, }); expect(view.state).toBe("guest"); expect(view.canActivateVip).toBe(true); expect(view.canShowSettings).toBe(false); }); it("derives member state and shows VIP activation for non-VIP users", () => { const view = getProfileViewModel({ loginStatus: "email", user: { currentUser: baseUser, avatarUrl: "https://example.com/avatar.png", creditBalance: 120, }, }); expect(view.state).toBe("member"); expect(view.name).toBe("Chase"); expect(view.avatarUrl).toBe("https://example.com/avatar.png"); expect(view.canActivateVip).toBe(true); expect(view.canShowSettings).toBe(true); }); it("derives VIP state and hides VIP activation for VIP users", () => { const view = getProfileViewModel({ loginStatus: "email", user: { currentUser: { ...baseUser, isVip: true }, avatarUrl: null, creditBalance: 120, }, }); expect(view.state).toBe("vip"); expect(view.canActivateVip).toBe(false); }); it("keeps Guest users in the signed-out profile state", () => { const view = getProfileViewModel({ loginStatus: "guest", user: { currentUser: null, avatarUrl: null, creditBalance: null, }, }); expect(view.state).toBe("guest"); expect(view.name).toBe("User name"); expect(view.isInitializingUser).toBe(false); }); }); describe("getProfileWalletView", () => { it("normalizes missing, negative, and fractional wallet values", () => { const view = getProfileWalletView({ currentUser: { ...baseUser, dailyFreeChatLimit: -1, dailyFreeChatRemaining: 12.9, dailyFreePrivateLimit: Number.NaN, dailyFreePrivateRemaining: 3.7, }, avatarUrl: null, creditBalance: -42.8, }); expect(view).toEqual({ creditBalance: 0, dailyFreeChatLimit: 0, dailyFreeChatRemaining: 12, dailyFreePrivateLimit: 0, dailyFreePrivateRemaining: 3, }); }); it("normalizes undefined and null counts to zero", () => { expect(normalizeProfileCount(undefined)).toBe(0); expect(normalizeProfileCount(null)).toBe(0); }); });