// Shared fixtures for the frozen-bundle tests: a small but realistic courseware // (slide + quiz + interactive scenes, one agent, narration audio, media) and a // helper that packages it with the production packager. import type { GeneratedAgentConfig, Scene, Stage } from '@/lib/types/stage'; import type { MediaFileRecord } from '@/lib/utils/database'; import { packageCourseware } from '@/lib/bundle/packager'; export const AGENT: GeneratedAgentConfig = { id: 'agent-1', name: '苏老师', role: 'teacher', persona: '耐心、循循善诱的生物学老师', avatar: '', color: '#4caf50', priority: 1, }; export function textElement(content: string): unknown { return { type: 'text', content, id: `el-${content.length}`, left: 0, top: 0, width: 100, height: 20, rotate: 0, defaultFontName: 'sans-serif', defaultColor: '#000', }; } export function slideScene(id: string, order: number, title: string): Scene { return { id, stageId: 'cw-1', title, order, content: { type: 'slide', canvas: { id: `canvas-${id}`, viewportSize: 100, viewportRatio: 1.6, theme: { color: '#fff', backgroundColor: '#000', fontName: 'sans-serif' }, elements: [ textElement('

光合作用

'), textElement('

叶绿体是光合作用的场所。

'), ], }, }, actions: [ { id: `act-${id}`, type: 'speech', text: '今天我们来学习光合作用。', audioId: 'aud-1', voice: 'v1', }, ], } as unknown as Scene; } export function quizScene(id: string, order: number, title: string): Scene { return { id, stageId: 'cw-1', title, order, content: { type: 'quiz', questions: [ { id: 'q1', type: 'single', question: '光合作用发生在细胞的哪个结构?', options: [ { label: '线粒体', value: 'A' }, { label: '叶绿体', value: 'B' }, { label: '细胞核', value: 'C' }, ], answer: ['B'], analysis: '叶绿体是光合作用的场所。', hasAnswer: true, points: 1, }, { id: 'q2', type: 'short_answer', question: '简述光合作用的意义。', commentPrompt: '围绕能量转换和氧气生成作答', hasAnswer: false, points: 2, }, ], }, } as unknown as Scene; } export function interactiveScene(id: string, order: number, title: string): Scene { return { id, stageId: 'cw-1', title, order, content: { type: 'interactive', html: '
虚拟光合实验
', }, } as unknown as Scene; } export function makeStage(): Stage { return { id: 'cw-1', name: '示例课件:光合作用', description: '面向初中生的光合作用入门课', createdAt: 1700000000000, updatedAt: 1700000000001, languageDirective: 'zh-CN', style: 'light', generatedAgentConfigs: [AGENT], } as Stage; } export function makeScenes(): Scene[] { return [ slideScene('sc-1', 1, '光合作用概述'), quizScene('sc-2', 2, '小测验'), interactiveScene('sc-3', 3, '模拟实验'), ]; } export function mediaRecord( id: string, type: 'image' | 'video', mimeType: string, withPoster = false, ): MediaFileRecord { return { id, stageId: 'cw-1', type, blob: new Blob([new Uint8Array([1, 2, 3])], { type: mimeType }), mimeType, size: 3, prompt: '一张叶绿体示意图', params: '{}', ...(withPoster ? { poster: new Blob([new Uint8Array([9, 8, 7])], { type: 'image/jpeg' }) } : {}), createdAt: 1700000000000, } as MediaFileRecord; } export const AUDIO_BYTES = new Blob([new Uint8Array([4, 5, 6])], { type: 'audio/mpeg' }); export const FIXED_PUBLISHED_AT = '2026-08-15T00:00:00.000Z'; export interface PackageSampleOptions { coursewareId?: string; version?: number; withMedia?: boolean; missingAudio?: boolean; requireComplete?: boolean; } /** Package the standard fixture courseware with the production packager. */ export function packageSampleBundle(options: PackageSampleOptions = {}) { const { coursewareId = 'cw-1', version = 1, withMedia = true, missingAudio = false, requireComplete = false, } = options; return packageCourseware({ coursewareId, version, stage: makeStage(), scenes: makeScenes(), mediaRecords: withMedia ? [mediaRecord('cw-1:img-1', 'image', 'image/png'), mediaRecord('cw-1:vid-1', 'video', 'video/mp4', true)] : [], resolveAudioBytes: missingAudio ? async () => null : async (audioId) => (audioId === 'aud-1' ? AUDIO_BYTES : null), resolveMediaBytes: async (record) => record.id.includes('img') ? new Blob([new Uint8Array([7, 7, 7])], { type: 'image/png' }) : new Blob([new Uint8Array([8, 8, 8])], { type: 'video/mp4' }), publishedAt: FIXED_PUBLISHED_AT, appVersion: 'test', requireComplete, }); }