duoqi-api/src/__tests__/services/app/bootstrap-service.test.ts
Wang Zhuoxuan 5e7b7b1cda
Some checks failed
CI/CD Pipeline / Unit Tests (push) Failing after 34s
CI/CD Pipeline / Build & Deploy Test (push) Has been skipped
CI/CD Pipeline / Build & Deploy Production (push) Has been skipped
Add region-based leaderboard support
2026-06-08 15:43:54 +08:00

97 lines
2.9 KiB
TypeScript

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<typeof import('../../../services/learning/progress-summary-service.js')>();
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,
});
});
});