feat: add unified learning workflows and six-tab assistant
This commit is contained in:
+12
-8
@@ -103,7 +103,7 @@ function csv(rows) {
|
||||
const sameStemDifferentAnswers = [semanticRecords[0], store.validateRecord({ ...semanticRecords[0], course: '另一课程', answer: 'B', correctTexts: undefined })];
|
||||
assert.equal(matcher.match(pressurePage, { role: '主操' }, { records: sameStemDifferentAnswers, revision: 1 }).status, 'conflict', '不同大小写的正确文本应保留为答案冲突');
|
||||
|
||||
// Existing v2 data retains its source texts; recompute stale loose keys in memory.
|
||||
// Existing v2 data retains its source texts and backup while upgrading to v3.
|
||||
const oldMemory = new Map([['yianxue.assistant.bank.v2', {
|
||||
records: semanticRecords.map(item => ({ ...item, questionKey: 'old-loose-key', optionKey: 'old-loose-options', id: 'old-id' })),
|
||||
revision: 7, updatedAt: '2026-10-10T00:00:00.000Z'
|
||||
@@ -118,7 +118,9 @@ function csv(rows) {
|
||||
assert.notEqual(item.id, 'old-id');
|
||||
});
|
||||
assert.notEqual(upgraded.records[0].questionKey, upgraded.records[1].questionKey);
|
||||
assert.equal(oldMemory.size, 1, '不更换稳定存储键或清空旧库');
|
||||
assert.equal(oldMemory.size, 2, '写入新的 v3 键并保留原始 v2 备份');
|
||||
assert.equal(oldMemory.get('yianxue.assistant.bank.v2').records[0].id, 'old-id', '升级不应改写原始 v2 数据');
|
||||
assert.equal(upgraded.schemaVersion, 3);
|
||||
|
||||
const escaped = record({ options: [{ label: 'A', text: "'-50" }, { label: 'B', text: "'引用" }, { label: 'C', text: "''-50" }] });
|
||||
let state = await store.importFiles([file('风险管理_题库_1题.csv', csv([escaped]))], { role: '主操' });
|
||||
@@ -137,7 +139,9 @@ function csv(rows) {
|
||||
const revision = state.revision;
|
||||
await assert.rejects(store.importFiles([file('坏.csv', csv([record({ answer: 'H' })]))], { role: '主操' }), /不存在的选项/);
|
||||
assert.equal((await store.load()).revision, revision, '无效导入应保持原题库不变');
|
||||
await assert.rejects(store.importFiles([file('没有岗位.csv', csv([record()]))]), /选择主操/);
|
||||
const roleFree = await runtime().store.importFiles([file('没有岗位.csv', csv([record()]))]);
|
||||
assert.equal(roleFree.total, 1, '无岗位的旧导出可直接导入统一题库');
|
||||
assert.equal(roleFree.counts.未标明, 1);
|
||||
await assert.rejects(store.importFiles([file('坏.json', JSON.stringify([record({ correctTexts: ['作业甲'] })]))]), /不一致/);
|
||||
await assert.rejects(store.importFiles([file('空.json', '{"records":[]}')]), /没有题目/);
|
||||
await store.setSetting('role', '班长');
|
||||
@@ -156,7 +160,7 @@ function csv(rows) {
|
||||
assert.equal(matcher.match({ ...q, question: q.question.replace('不应', '应') }, { role: '主操' }, state).status, 'missing');
|
||||
assert.equal(matcher.match({ ...q, options: q.options.slice(0, 2) }, { role: '主操' }, state).status, 'missing');
|
||||
assert.equal(matcher.match({ ...q, type: '多选' }, { role: '主操' }, state).status, 'missing');
|
||||
assert.equal(matcher.match(q, {}, state).status, 'unsupported');
|
||||
assert.equal(matcher.match(q, {}, state).status, 'matched');
|
||||
assert.equal(matcher.match({ ...q, questionImages: ['image.png'] }, { role: '主操' }, state).status, 'unsupported');
|
||||
assert.equal(matcher.match({ ...q, options: [q.options[0], { label: 'B', text: q.options[0].text }] }, { role: '主操' }, state).status, 'unsupported');
|
||||
assert.deepEqual(Array.from(matcher.match({ ...q, options: q.options.map(option => ({ ...option, label: option.label + '.' })) }, { role: '主操' }, state).correctLabels), ['A']);
|
||||
@@ -171,13 +175,13 @@ function csv(rows) {
|
||||
assert.equal(answer.status, 'matched'); assert.equal(answer.records.length, 1);
|
||||
answer = matcher.match(q, { role: '主操', course: '重大危险源' }, groupedState);
|
||||
assert.equal(answer.status, 'matched'); assert.deepEqual(Array.from(answer.correctLabels), ['C']);
|
||||
assert.equal(matcher.search('温度', {}, groupedState).total, 0);
|
||||
assert.equal(matcher.search('温度', {}, groupedState).total, 2);
|
||||
assert.equal(matcher.search('', { role: '主操' }, groupedState).total, 0);
|
||||
let found = matcher.search('温度 -50.5 作业乙', { role: '主操' }, groupedState);
|
||||
assert.equal(found.total, 2, '同题同选项同答案合并,冲突答案分别展示');
|
||||
assert(found.items.every(item => item.conflict));
|
||||
assert(found.items.some(item => item.sources.length === 2));
|
||||
assert.equal(matcher.search('温度', { role: '班长' }, groupedState).total, 1);
|
||||
assert(found.items.some(item => item.sources.length === 3));
|
||||
assert.equal(matcher.search('温度', { role: '班长' }, groupedState).total, 2, '遗留岗位设置不应过滤统一搜索');
|
||||
assert.equal(matcher.search('温度', { role: '主操', course: '风险管理' }, groupedState).items[0].conflict, false);
|
||||
assert.equal(matcher.search('温度不存在', { role: '主操' }, groupedState).total, 0);
|
||||
found = matcher.search('温度', { role: '主操', limit: 1, offset: 0 }, groupedState);
|
||||
@@ -216,7 +220,7 @@ function csv(rows) {
|
||||
await assert.rejects(store.importFiles([file('损坏.zip', await invalidZip.generateAsync({ type: 'nodebuffer' }))]), /不存在的选项/);
|
||||
assert.equal((await store.load()).total, 1);
|
||||
await assert.rejects(store.importFiles([file('题库.zip', await zip.generateAsync({ type: 'nodebuffer' })), file('一份.csv', csv([record()]))]), /只选择一个/);
|
||||
console.log('PASS 严格语义匹配与宽松搜索分离、原答案证据、旧库重算键、本地存储和原子导入、岗位隔离、换序/冲突/搜索分页');
|
||||
console.log('PASS 严格语义匹配与宽松搜索分离、原答案证据、旧库安全升级、本地存储和原子导入、统一题库、换序/冲突/搜索分页');
|
||||
|
||||
const realZip = path.join(rootDir, '题库.zip');
|
||||
if (fs.existsSync(realZip)) {
|
||||
|
||||
Reference in New Issue
Block a user