import assert from 'node:assert/strict'; import test, { after } from 'node:test'; import { fileURLToPath } from 'node:url'; import { createServer } from 'vite'; const vite = await createServer({ root: fileURLToPath(new URL('..', import.meta.url)), configFile: false, optimizeDeps: { noDiscovery: true }, server: { middlewareMode: true }, appType: 'custom' }); after(async () => { await vite.close(); }); const learning = await vite.ssrLoadModule('/src/services/learning.ts'); const { selectLearningTopic } = learning; const assignment = (overrides = {}) => ({ source: 'manual', status: '待完成', scenarioName: '', ...overrides }); test('优先选择第一条未完成且非 daily 的非空专项场景', () => { const topic = selectLearningTopic([ assignment({ source: 'daily', scenarioName: '每日三题' }), assignment({ status: '已完成', scenarioName: '催费沟通' }), assignment({ scenarioName: ' ' }), assignment({ scenarioName: ' 客服应对投诉 ' }), assignment({ scenarioName: '电梯困人处置' }) ], '生活顾问 岗前 SOP'); assert.equal(topic, '客服应对投诉'); }); test('无未完成专项时选择第一条非 daily 的非空场景', () => { const topic = selectLearningTopic([ assignment({ source: 'daily', status: '已完成', scenarioName: '每日三题' }), assignment({ status: '已完成', scenarioName: '催费沟通' }), assignment({ status: '已完成', scenarioName: '客户投诉处理' }) ], '生活顾问 岗前 SOP'); assert.equal(topic, '催费沟通'); }); test('无可用专项场景时回退到调用方的岗位默认主题', () => { const topic = selectLearningTopic([ assignment({ source: 'daily', scenarioName: '每日三题' }), assignment({ scenarioName: ' ' }) ], '生活顾问 岗前 SOP'); assert.equal(topic, '生活顾问 岗前 SOP'); }); test('专项场景名进入 topics 且去重,同时保留岗位默认主题和快捷问句', () => { const topics = learning.buildLearningTopics?.([ assignment({ scenarioName: ' 客服应对投诉 ' }), assignment({ status: '已完成', scenarioName: '客服应对投诉' }), assignment({ source: 'daily', scenarioName: '每日三题' }), assignment({ scenarioName: '' }), assignment({ scenarioName: '电梯困人处置' }) ], '生活顾问 岗前 SOP', ['催费话术', '投诉应对']) ?? []; assert.deepEqual(topics, [ '生活顾问 岗前 SOP', '客服应对投诉', '电梯困人处置', '催费话术', '投诉应对' ]); });