Files
Curio/src/app/api/mastery/__tests__/route.test.ts
T
arnaudne 5bf6013460 feat: full feature buildout — streaming, i18n, mastery map, admin, jobs
Progressive lesson streaming via onSegment callback (fixes SSE for non-English
users — locale was shadowed in lesson-reader useEffect). Adds: BullMQ workers,
Redis stream buffer, token budget enforcement, Langfuse tracing, golden-eval
runner, Playwright e2e scaffolding, lesson depth/locale/preferences schema,
mastery map UI, admin panel (blueprints/users/reports/quality/misconceptions),
image queries, source citations, view transitions, reading animations, i18n
(next-intl), PDF export, surprise endpoint, and 402 passing unit tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 22:08:14 +02:00

81 lines
2.2 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { NextRequest } from 'next/server';
vi.mock('@/lib/db/queries', () => ({
getMasteryMap: vi.fn(),
}));
import { GET } from '../route';
import { getMasteryMap } from '@/lib/db/queries';
const mockGet = vi.mocked(getMasteryMap);
const NOW = new Date('2026-06-21T10:00:00Z');
const LATER = new Date('2026-06-28T10:00:00Z');
const ENTRIES = [
{
conceptId: 'c1',
conceptName: 'Closures',
blueprintId: 'bp1',
blueprintTitle: 'JavaScript',
blueprintIntentKey: 'javascript',
score: 0.8,
lastSeen: NOW,
nextReview: LATER,
},
{
conceptId: 'c2',
conceptName: 'Promises',
blueprintId: 'bp1',
blueprintTitle: 'JavaScript',
blueprintIntentKey: 'javascript',
score: 0.45,
lastSeen: NOW,
nextReview: NOW,
},
];
function makeRequest(userId?: string) {
const url = userId
? `http://localhost:3000/api/mastery?userId=${userId}`
: 'http://localhost:3000/api/mastery';
return new NextRequest(url);
}
describe('GET /api/mastery', () => {
beforeEach(() => vi.clearAllMocks());
it('returns 400 when userId is missing', async () => {
const res = await GET(makeRequest());
expect(res.status).toBe(400);
});
it('returns 200 with empty groups when no mastery', async () => {
mockGet.mockResolvedValue([]);
const res = await GET(makeRequest('u1'));
expect(res.status).toBe(200);
const body = await res.json();
expect(body.groups).toHaveLength(0);
});
it('groups entries by blueprintTitle', async () => {
mockGet.mockResolvedValue(ENTRIES);
const res = await GET(makeRequest('u1'));
expect(res.status).toBe(200);
const body = await res.json();
expect(body.groups).toHaveLength(1);
expect(body.groups[0].blueprintTitle).toBe('JavaScript');
expect(body.groups[0].concepts).toHaveLength(2);
});
it('includes score in each concept entry', async () => {
mockGet.mockResolvedValue(ENTRIES);
const res = await GET(makeRequest('u1'));
const body = await res.json();
const concepts = body.groups[0].concepts;
expect(concepts[0].score).toBe(0.8);
expect(concepts[1].score).toBe(0.45);
});
});