4e38b5a791
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>
68 lines
2.0 KiB
TypeScript
68 lines
2.0 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { NextRequest } from 'next/server';
|
|
import { requireAuth } from '@/lib/auth-helpers';
|
|
|
|
const mockTx = {
|
|
delete: vi.fn().mockReturnValue({ where: vi.fn().mockResolvedValue([]) }),
|
|
};
|
|
|
|
const mockDb = {
|
|
transaction: vi.fn().mockImplementation(async (fn: (tx: typeof mockTx) => Promise<void>) => fn(mockTx)),
|
|
};
|
|
|
|
vi.mock('@/lib/db', () => ({ db: mockDb }));
|
|
|
|
vi.mock('@/lib/db/schema', () => ({
|
|
users: {},
|
|
responses: {},
|
|
mastery: {},
|
|
journeyInstances: {},
|
|
}));
|
|
|
|
vi.mock('drizzle-orm', () => ({
|
|
eq: vi.fn((col, val) => ({ col, val })),
|
|
}));
|
|
|
|
function makeRequest(): NextRequest {
|
|
return new NextRequest('http://localhost/api/user/account', { method: 'DELETE' });
|
|
}
|
|
|
|
describe('DELETE /api/user/account', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockTx.delete.mockReturnValue({ where: vi.fn().mockResolvedValue([]) });
|
|
mockDb.transaction.mockImplementation(async (fn: (tx: typeof mockTx) => Promise<void>) => fn(mockTx));
|
|
});
|
|
|
|
it('returns 200 {ok:true} on happy path', async () => {
|
|
const { DELETE } = await import('../route');
|
|
const res = await DELETE();
|
|
const body = await res.json();
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(body).toEqual({ ok: true });
|
|
});
|
|
|
|
it('runs transaction and deletes all four tables', async () => {
|
|
const { DELETE } = await import('../route');
|
|
await DELETE();
|
|
|
|
expect(mockDb.transaction).toHaveBeenCalledOnce();
|
|
expect(mockTx.delete).toHaveBeenCalledTimes(4);
|
|
});
|
|
|
|
it('returns 401 when requireAuth throws', async () => {
|
|
vi.mocked(requireAuth).mockRejectedValueOnce(new Error('redirect'));
|
|
|
|
const { DELETE } = await import('../route');
|
|
await expect(DELETE()).rejects.toThrow('redirect');
|
|
});
|
|
|
|
it('propagates transaction errors', async () => {
|
|
mockDb.transaction.mockRejectedValueOnce(new Error('db error'));
|
|
|
|
const { DELETE } = await import('../route');
|
|
await expect(DELETE()).rejects.toThrow('db error');
|
|
});
|
|
});
|