import { mkdtemp, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import JSZip from 'jszip'; import { afterEach, describe, expect, it } from 'vitest'; import { packageUniqueMakeloreCourse, persistUniqueMakeloreCourse, readUniqueMakeloreCoursePackage, } from '@/lib/makelore-course/package'; import { loadMakeloreCourse } from '@/lib/makelore-runtime/course-store'; import { validateCoursePackageDescriptor } from '../../../packages/@makelore/learning-contracts/src/index'; import type { Scene, Stage } from '@/lib/types/stage'; describe('unique Makelore course package', () => { let root = ''; const previousPackageRoot = process.env.MAKELORE_COURSE_PACKAGE_DIR; const previousStoreRoot = process.env.MAKELORE_COURSE_STORE_DIR; afterEach(async () => { if (root) await rm(root, { recursive: true, force: true }); if (previousPackageRoot === undefined) delete process.env.MAKELORE_COURSE_PACKAGE_DIR; else process.env.MAKELORE_COURSE_PACKAGE_DIR = previousPackageRoot; if (previousStoreRoot === undefined) delete process.env.MAKELORE_COURSE_STORE_DIR; else process.env.MAKELORE_COURSE_STORE_DIR = previousStoreRoot; }); it('freezes classroom, quiz and knowledge without a product version', async () => { const stage = { id: 'stage-1', name: '变量入门', createdAt: Date.now(), updatedAt: Date.now(), generatedAgentConfigs: [{ id: 'teacher-1', name: 'AI Teacher', role: 'teacher', persona: '耐心讲解', avatar: '/avatars/teacher.png', color: '#3b82f6', priority: 10 }], } as Stage; const scenes = [{ id: 'scene-1', stageId: stage.id, title: '变量是什么', order: 1, type: 'interactive', content: { type: 'interactive', html: '
' }, createdAt: Date.now(), updatedAt: Date.now(), }] as Scene[]; const course = await packageUniqueMakeloreCourse({ courseId: 'course-1', stage, scenes }); const zip = await JSZip.loadAsync(course.bytes); const descriptor = JSON.parse(await zip.file('descriptor.json')!.async('string')); expect(descriptor).toMatchObject({ courseId: 'course-1', formatVersion: 1, sceneCount: 1 }); expect(validateCoursePackageDescriptor(descriptor)).toMatchObject({ ok: true, errors: [] }); expect(descriptor).not.toHaveProperty('version'); expect(zip.file('classroom.json')).not.toBeNull(); expect(zip.file('knowledge/knowledge.json')).not.toBeNull(); root = await mkdtemp(join(tmpdir(), 'makelore-course-package-')); process.env.MAKELORE_COURSE_PACKAGE_DIR = join(root, 'packages'); process.env.MAKELORE_COURSE_STORE_DIR = join(root, 'courses'); await persistUniqueMakeloreCourse('course-1', course); await expect(readUniqueMakeloreCoursePackage('course-1')).resolves.toEqual(course.bytes); await expect(loadMakeloreCourse('course-1', course.contentHash)).resolves.toMatchObject({ title: '变量入门' }); }); });