71 lines
2.2 KiB
TypeScript
71 lines
2.2 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { NextRequest } from 'next/server';
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
readJob: vi.fn(),
|
|
readClassroom: vi.fn(),
|
|
finalizeSingle: vi.fn(),
|
|
finalizeLarge: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('@/lib/server/classroom-job-store', () => ({
|
|
isValidClassroomJobId: () => true,
|
|
readClassroomGenerationJob: mocks.readJob,
|
|
}));
|
|
|
|
vi.mock('@/lib/server/classroom-storage', () => ({
|
|
buildRequestOrigin: () => 'https://engine.example',
|
|
readClassroom: mocks.readClassroom,
|
|
}));
|
|
|
|
vi.mock('@/lib/course-framework/store', () => ({
|
|
readCourseRecordReconciled: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('@/lib/makelore-course/finalize', () => ({
|
|
finalizeSingleLearningCourse: mocks.finalizeSingle,
|
|
finalizeLargeLearningCourse: mocks.finalizeLarge,
|
|
frozenLearningFinalizeResponse: vi.fn(),
|
|
}));
|
|
|
|
import { FrozenLearningCourseMutationError } from '@/lib/makelore-course/immutability';
|
|
import { POST } from '@/app/api/runtime/v1/courses/jobs/[jobId]/finalize/route';
|
|
|
|
describe('private Learning Engine finalize route', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
vi.stubEnv('LEARNING_ENGINE_TOKEN', 'engine-secret');
|
|
mocks.readJob.mockResolvedValue({
|
|
id: 'job-1',
|
|
ownerPrincipalId: 'owner-1',
|
|
status: 'succeeded',
|
|
result: { classroomId: 'course-1' },
|
|
input: { interactiveMode: true, enableTTS: true },
|
|
});
|
|
mocks.readClassroom.mockResolvedValue({ id: 'course-1', stage: {}, scenes: [] });
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllEnvs();
|
|
});
|
|
|
|
it('returns a stable conflict when an immutable course source no longer matches', async () => {
|
|
mocks.finalizeSingle.mockRejectedValue(new FrozenLearningCourseMutationError('course-1'));
|
|
const request = new NextRequest(
|
|
'https://engine.example/api/runtime/v1/courses/jobs/job-1/finalize',
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
Authorization: 'Bearer engine-secret',
|
|
'X-Owner-User-Id': 'owner-1',
|
|
},
|
|
},
|
|
);
|
|
|
|
const response = await POST(request, { params: Promise.resolve({ jobId: 'job-1' }) });
|
|
|
|
expect(response.status).toBe(409);
|
|
await expect(response.json()).resolves.toMatchObject({ error: 'course_frozen' });
|
|
});
|
|
});
|