import { beforeEach, describe, expect, it, vi } from 'vitest'; import { db } from '../../../db/client.js'; import { getBootstrap } from '../../../services/app/bootstrap-service.js'; function selectRows(rows: unknown[]) { return { from: vi.fn().mockReturnValue({ where: vi.fn().mockReturnValue({ orderBy: vi.fn().mockResolvedValue(rows), limit: vi.fn().mockResolvedValue(rows), then: (resolve: (value: unknown) => unknown) => Promise.resolve(rows).then(resolve), }), orderBy: vi.fn().mockResolvedValue(rows), }), }; } function mockSelectQueue(queue: unknown[][]) { let index = 0; vi.mocked(db.select).mockImplementation((() => { const rows = index < queue.length ? queue[index]! : []; index += 1; return selectRows(rows); }) as never); } 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 }], // getProgressSummary [{ id: 'user-1', tier: 'free', xpTotal: 100, activeTrackId: null, dailyAttemptsLeft: 5, dailyAttemptsDate: new Date(), checkInDays: 1, lastCheckInDate: new Date(), streakProtectedUntil: null, heartsRemaining: 5 }], [{ tier: 'free', heartsRemaining: 5, heartsLastRestore: null }], [{ streakDays: 1, streakLastDate: new Date() }], [], [{ id: 'user-1', tier: 'free', xpTotal: 100, activeTrackId: null, dailyAttemptsLeft: 5, dailyAttemptsDate: new Date(), checkInDays: 1, lastCheckInDate: new Date(), streakProtectedUntil: null, heartsRemaining: 5 }], [{ used: 1, restored: 0 }], // getThemeTracks [], // getClientSubscription [], // getCoinBalance [{ coinsBalance: 260 }], // getClientInventory [{ itemId: 'hint_feather', quantity: 2, activeUntil: null, metadata: null }], ]); 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, }); 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, }); }); });