import { beforeEach, describe, expect, it, vi } from 'vitest'; import { db } from '../../../db/client.js'; import { getBootstrap } from '../../../services/app/bootstrap-service.js'; import { mockSelectQueue as queueSelect } from '../../helpers/db-mock.js'; vi.mock('../../../services/learning/progress-summary-service.js', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, getProgressSummary: vi.fn().mockResolvedValue({ hearts: 5, maxHearts: 5, nextHeartRestoreAt: null, dailyAttemptsLeft: 5, dailyAttemptsMax: 5, nextAttemptResetAt: null, highRewardSessionsLeft: 3, highRewardSessionsMax: 3, xp: 100, level: 2, xpToNextLevel: 100, streakDays: 1, checkInDays: 1, streakProtectedUntil: null, activeTrackId: null, isSubscribed: false, }), }; }); vi.mock('../../../services/learning/tracks-service.js', () => ({ getThemeTracks: vi.fn().mockResolvedValue([]), })); vi.mock('../../../services/subscription/subscription-api-service.js', () => ({ getClientSubscription: vi.fn().mockResolvedValue({ status: 'none', tier: 'free', expiresAt: null, autoRenew: false, }), })); vi.mock('../../../services/gamification/coin-service.js', () => ({ getCoinBalance: vi.fn().mockResolvedValue(260), })); vi.mock('../../../services/gamification/inventory-service.js', () => ({ getClientInventory: vi.fn().mockResolvedValue({ items: [{ itemId: 'hint_feather', quantity: 2, activeUntil: null, metadata: null }], }), })); function mockSelectQueue(queue: unknown[][]) { queueSelect(vi.mocked(db.select), queue); } function mockUpdate() { return { set: vi.fn().mockReturnValue({ where: vi.fn().mockResolvedValue(undefined) }) } as never; } describe('bootstrap-service', () => { beforeEach(() => { vi.clearAllMocks(); }); it('includes wallet, inventory, shop catalog, ad benefits, and subscription in bootstrap', async () => { mockSelectQueue([ [{ id: 'user-1', nickname: '多奇', avatarUrl: null, tier: 'free', xpTotal: 100 }], ]); vi.mocked(db.update).mockReturnValue(mockUpdate()); const result = await getBootstrap('user-1'); expect(result.user).toEqual({ id: 'user-1', nickname: '多奇', avatarUrl: null, tier: 'free', level: 2, region: null, }); expect(result.wallet).toEqual({ coinsBalance: 260 }); expect(result.inventory.items).toEqual([ { itemId: 'hint_feather', quantity: 2, activeUntil: null, metadata: null }, ]); expect(result.shop.products.map((product) => product.id)).toContain('hint-feather'); expect(result.shopBenefits).toBe(result.shop.benefits); expect(result.subscription).toEqual({ status: 'none', tier: 'free', expiresAt: null, autoRenew: false, }); }); });