import { afterAll, beforeAll, describe, expect, test } from 'vitest'; import { promises as fs } from 'fs'; import os from 'os'; import path from 'path'; import { clearKnowledgeCache, loadCoursewareKnowledge, retrieveChunks, tokenizeQuery, } from '@/lib/qa/knowledge'; import { createFileBundleByteStore } from '@/lib/courseware-repo/bundle-store'; import { packageSampleBundle } from '../bundle/fixtures'; describe('tokenizeQuery', () => { test('extracts CJK bigrams and ascii words', () => { const terms = tokenizeQuery('叶绿体 光合作用 how do plants work'); expect(terms).toContain('叶绿'); expect(terms).toContain('绿体'); expect(terms).toContain('光合'); expect(terms).toContain('plants'); expect(terms).toContain('work'); }); test('returns nothing for noise', () => { expect(tokenizeQuery('?!?!')).toEqual([]); expect(tokenizeQuery('a b')).toEqual([]); // single chars filtered }); }); describe('retrieveChunks', () => { const knowledge = { coursewareId: 'cw-1', language: 'zh-CN', scenes: [ { sceneId: 's1', order: 1, title: '光合作用概述', type: 'slide' as const, text: '叶绿体是光合作用的场所。光反应与暗反应。', narration: '今天我们来学习光合作用。', }, { sceneId: 's2', order: 2, title: '细胞呼吸', type: 'slide' as const, text: '线粒体是有氧呼吸的主要场所。', }, { sceneId: 's3', order: 3, title: '小测验', type: 'quiz' as const, text: '问:光合作用发生在细胞的哪个结构?', }, ], }; test('scores title matches above body matches', () => { const hits = retrieveChunks(knowledge, '光合作用 发生在哪里', 2); expect(hits.length).toBeGreaterThan(0); // Title match (s1: "光合作用概述" + s3 title "小测验"? s3 text has 光合作用) — // s1 must rank first on combined score. expect(hits[0].sceneId).toBe('s1'); }); test('respects the limit and drops non-matching scenes', () => { const hits = retrieveChunks(knowledge, '线粒体 有氧呼吸', 1); expect(hits).toHaveLength(1); expect(hits[0].sceneId).toBe('s2'); }); test('empty query returns nothing', () => { expect(retrieveChunks(knowledge, '啊 呢')).toEqual([]); }); }); describe('loadCoursewareKnowledge', () => { let bytesDir: string; beforeAll(async () => { bytesDir = await fs.mkdtemp(path.join(os.tmpdir(), 'qa-knowledge-')); const packaged = await packageSampleBundle({ coursewareId: 'kw-1' }); const store = createFileBundleByteStore(bytesDir); await store.save( 'kw-1', 1, new Uint8Array(await packaged.zip.arrayBuffer()), ); }); afterAll(async () => { await fs.rm(bytesDir, { recursive: true, force: true }); clearKnowledgeCache(); }); test('loads knowledge and quiz packs from the frozen bundle', async () => { const store = createFileBundleByteStore(bytesDir); const knowledge = await loadCoursewareKnowledge('kw-1', 1, store); expect(knowledge).not.toBeNull(); expect(knowledge!.title).toBe('示例课件:光合作用'); expect(knowledge!.language).toBe('zh-CN'); expect(knowledge!.knowledge.scenes).toHaveLength(3); expect(knowledge!.quiz.scenes).toHaveLength(1); expect(knowledge!.knowledge.scenes[0].narration).toContain('光合作用'); }); test('returns null for an unknown version', async () => { expect(await loadCoursewareKnowledge('kw-1', 99)).toBeNull(); }); });