Files
Curio/src/app/api/socratic/__tests__/route.test.ts
T
arnaudne 4e38b5a791 feat: initial commit — Curio mastery tutor
Full Next.js App Router application with:
- AI-graded lesson checkpoints (BKT/Elo mastery tracking)
- Auth.js v5: credentials, Google, GitHub, generic OIDC
- Anonymous-session-first with migrate-on-signin
- Admin panel: users, blueprints, reports, site settings
- Password reset + email verification (nodemailer/SMTP)
- Site config: require_auth + signups_enabled flags
- server-only guards on all DB/generation/verification modules
- PostgreSQL 16 + pgvector, Redis cache, Drizzle ORM
- 271 unit tests (Vitest), golden-eval harness, Playwright e2e stubs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 22:25:43 +02:00

68 lines
2.2 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { NextRequest } from 'next/server';
vi.mock('@/lib/verification/socratic', () => ({
runSocraticProbe: vi.fn(),
}));
import { POST } from '../route';
import { runSocraticProbe } from '@/lib/verification/socratic';
const mockProbe = vi.mocked(runSocraticProbe);
const VALID_BODY = {
gradeId: '123e4567-e89b-12d3-a456-426614174000',
userId: 'user-abc',
followUpText: 'I meant that closures hold a reference to the outer scope, not a copy.',
};
function makeRequest(body: unknown) {
return new NextRequest('http://localhost:3000/api/socratic', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
}
describe('POST /api/socratic', () => {
beforeEach(() => vi.clearAllMocks());
it('returns 400 when gradeId is missing', async () => {
const res = await POST(makeRequest({ userId: 'u', followUpText: 'text' }));
expect(res.status).toBe(400);
});
it('returns 400 when gradeId is not a UUID', async () => {
const res = await POST(makeRequest({ ...VALID_BODY, gradeId: 'not-a-uuid' }));
expect(res.status).toBe(400);
});
it('returns 400 when followUpText is empty', async () => {
const res = await POST(makeRequest({ ...VALID_BODY, followUpText: '' }));
expect(res.status).toBe(400);
});
it('returns 200 with reply and upgradedVerdict on success', async () => {
mockProbe.mockResolvedValue({ reply: 'Yes, that is correct.', upgradedVerdict: 'mastered' });
const res = await POST(makeRequest(VALID_BODY));
expect(res.status).toBe(200);
const body = await res.json();
expect(body.reply).toBe('Yes, that is correct.');
expect(body.upgradedVerdict).toBe('mastered');
});
it('returns 404 when grade not found', async () => {
mockProbe.mockRejectedValue(
new Error(`Grade not found: ${VALID_BODY.gradeId}`),
);
const res = await POST(makeRequest(VALID_BODY));
expect(res.status).toBe(404);
});
it('returns 500 on unexpected error', async () => {
mockProbe.mockRejectedValue(new Error('DB down'));
const res = await POST(makeRequest(VALID_BODY));
expect(res.status).toBe(500);
});
});