97 lines
2.9 KiB
TypeScript
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,
|
|
});
|
|
});
|
|
});
|