| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- import test from 'node:test';
- import assert from 'node:assert/strict';
- import { buildModel } from '../src/core/kfs/build.js';
- import { workshopReview, workshopOutputRefs, readWorkshopQuery, OUTPUT_GROUP_NONE } from '../src/core/workshop-outputs.js';
- import { reportCenter, entriesForType, stageOutcome } from '../src/core/library/reports.js';
- import { DELIVERY_SCHEMA } from '../skills/ksf-workshop-runtime/scripts/report-contract.mjs';
- const chapter = (id, text = '产品判断与支持依据') => ({ id, title: id, sections: [{ kind: 'para', text }] });
- const artifact = (id, other = {}) => ({ id, memberId: 'member-a', title: '测试报告', workshopKey: 'topic-a',
- reportSeriesKey: 'topic-a:D1-01:report', version: 1, dataMode: 'live', report: { chapters: [chapter('market-analysis')] }, ...other });
- const groups = { groups: [{ id: 'g1', name: '一组' }, { id: 'g2', name: '二组' }], assignments: { 'member-a': 'g1', 'member-b': 'g2' } };
- const review = (artifacts, q = {}, state = groups, members = []) => workshopReview({ artifacts, members }, state, q);
- test('归一化保留真实任务系列、版本、课题和模式,非法版本保持未知', () => {
- const raw = { artifact: [{ objectId: 'one', title: '报告', reportSeriesKey: 't:D1-01:r', workshopKey: 't',
- dataMode: 'sample', version: 2, sessionRef: { objectId: 's' } }, { objectId: 'two', version: '3' }] };
- const db = buildModel(raw), a = db.artifactById.one;
- assert.equal(a.reportSeriesKey, 't:D1-01:r'); assert.equal(a.workshopKey, 't');
- assert.equal(a.version, 2); assert.equal(a.dataMode, 'sample'); assert.equal(a.sessionId, 's');
- assert.equal(db.artifactById.two.version, null);
- });
- test('只按有正文的明确章节或任务定位;标题和成果类型不会把运行记录当提案', () => {
- assert.equal(workshopOutputRefs(artifact('a', { reportSeriesKey: '', report: null, title: '市场分析概念提案', type: 'concept' })).refs.length, 0);
- assert.equal(workshopOutputRefs(artifact('a', { report: { chapters: [chapter('market-analysis', '')] } })).refs.length, 0);
- assert.equal(workshopOutputRefs(artifact('a', { reportSeriesKey: '', report: { chapters: [chapter('market-analysis'), chapter('social-needs')] } })).refs.length, 2);
- assert.equal(workshopOutputRefs(artifact('a', { report: null, rawText: '真实的比较正文' })).refs[0].basis, 'task');
- assert.equal(workshopOutputRefs(artifact('a', { reportSeriesKey: 't:D2-08:proposal', report: { chapters: [chapter('conclusion')] } })).refs[0].key, 'proposal');
- });
- test('明确标为方法材料的配置卡不因课表环节被计作核心市场报告', () => {
- const a = artifact('method', { type: 'category_report', report: { executive: [],
- delivery: { schema: DELIVERY_SCHEMA, moduleId: 'D1-01', kind: 'method', contentStatus: 'ready', outputs: [] },
- chapters: [chapter('configuration')] } });
- const view = review([a]);
- assert.equal(view.rows[0].reportType, 'method');assert.deepEqual(view.rows[0].refs, []);
- });
- test('默认区分真实资料、样例、人工和未知,不根据标题猜模式', () => {
- const rows = ['live', 'sample', 'manual', ''].map((dataMode, i) => artifact(String(i), { dataMode }));
- assert.deepEqual(review(rows).rows.map(r => r.id), ['0']);
- assert.equal(review(rows, { mode: 'all' }).rows.length, 4);
- assert.deepEqual(review(rows, { mode: 'unknown' }).rows.map(r => r.id), ['3']);
- assert.deepEqual(readWorkshopQuery({ mode: 'bogus', stage: 'D1', reportType: 'bogus' }), { group: '', workshop: '', mode: 'live', stage: '', reportType: '', search: '', history: false });
- });
- test('共享稳定身份决定分组;同名学员各自归属,失效组不按原快照猜', () => {
- const rows = [artifact('a', { ownerName: '同名' }), artifact('b', { ownerName: '同名', memberId: 'member-b' }), artifact('c', { memberId: 'unknown', groupId: 'g1' })];
- assert.deepEqual(review(rows, { group: 'g2' }).rows.map(r => r.id), ['b']);
- assert.equal(review(rows, { group: OUTPUT_GROUP_NONE }).rows[0].id, 'c');
- assert.equal(review(rows, {}, { ...groups, assignments: { 'member-a': 'deleted-group' } }).ungrouped, 3);
- });
- test('同一稳定课题、作者、系列选择最新版本;同版本冲突及未知版本不隐藏', () => {
- const rows = [artifact('v1'), artifact('v2', { version: 2 }), artifact('conflict', { version: 2 }), artifact('unknown', { version: null })];
- assert.deepEqual(review(rows).rows.map(r => r.id).sort(), ['conflict', 'unknown', 'v2']);
- assert.equal(review(rows).rows[0].historyCount, 4);
- assert.equal(review(rows, { history: '1' }).rows.length, 4);
- assert.equal(review([artifact('a'), artifact('b', { memberId: 'member-b', version: 2 }), artifact('c', { workshopKey: 'topic-b', version: 3 })]).rows.length, 3);
- assert.equal(review([artifact('live'), artifact('sample', { version: 5, dataMode: 'sample' })], { mode: 'all' }).rows.length, 2);
- });
- test('缺系列或稳定身份时保留每条记录,不按相同标题合并', () => {
- for (const missing of [{ reportSeriesKey: '' }, { memberId: '' }, { workshopKey: '' }]) {
- const out = review([artifact('one', missing), artifact('two', { ...missing, version: 2 })]);
- assert.equal(out.rows.length, 2); assert.equal(out.missingSeries, 2);
- }
- });
- test('课题、小组、阶段、报告分类和搜索可共同筛选,分类统计落在个人报告上', () => {
- const rows = [artifact('target', { type: 'category_report', stageObj: { code: 'S1' } }), artifact('other-topic', { workshopKey: 't2' }), artifact('other-person', { type: 'competitor_report', memberId: 'member-b', stageObj: { code: 'S1' } })];
- const q = { workshop: 'topic-a', group: 'g1', stage: 'S1', reportType: 'category', search: 'target' };
- const out = review(rows, q);
- assert.deepEqual(out.rows.map(r => r.id), ['target']);
- assert.equal(out.categories.find(category => category.key === 'category').count, 1);
- assert.equal(out.categories.find(category => category.key === 'competitor').count, 0);
- assert.equal(review(rows, {}, null).groupStateAvailable, false);
- });
- test('成果目录按小组、成员、报告展开,并保留当前筛选下没有报告的成员', () => {
- const members = [
- { id: 'member-a', name: '甲' },
- { id: 'member-b', name: '乙' },
- { id: 'member-c', name: '丙' },
- ];
- const rows = [
- artifact('a1', { memberId: 'member-a', ownerName: '甲', reportSeriesKey: 'topic-a:D1-01:report' }),
- artifact('a2', { memberId: 'member-a', ownerName: '甲', reportSeriesKey: 'topic-a:D1-02:report' }),
- artifact('b1', { memberId: 'member-b', ownerName: '乙', reportSeriesKey: 'topic-a:D1-03:report' }),
- ];
- const out = review(rows, { mode: 'all' }, groups, members);
- const first = out.hierarchy.find(group => group.id === 'g1');
- const second = out.hierarchy.find(group => group.id === 'g2');
- const ungrouped = out.hierarchy.find(group => group.id === OUTPUT_GROUP_NONE);
- assert.deepEqual(first.members.map(member => [member.name, member.reportCount]), [['甲', 2]]);
- assert.deepEqual(first.members[0].reports.map(row => row.id), ['a1', 'a2']);
- assert.deepEqual(first.members[0].reports[0].refs.map(ref => ref.key), ['market']);
- assert.equal(first.members.length, 1);
- assert.equal(first.memberCount, 1);
- assert.deepEqual(second.members.map(member => [member.name, member.reportCount]), [['乙', 1]]);
- assert.deepEqual(ungrouped.members.map(member => [member.name, member.reportCount]), [['丙', 0]]);
- assert.equal(review(rows, { mode: 'all', reportType: 'unclassified' }, groups, members).rows.length, 3);
- });
- test('分类按钮筛选的是组员自己的报告,分类缺失的报告仍归入未归类', () => {
- const rows = [
- artifact('market', { type: 'category_report' }),
- artifact('competitor', { type: 'competitor_report', reportSeriesKey: 'topic-a:D1-02:report' }),
- artifact('unknown', { type: '', reportSeriesKey: 'topic-a:D1-03:report' }),
- ];
- const out = review(rows, { mode: 'all', reportType: 'competitor' }, groups, [{ id: 'member-a', name: '甲' }]);
- assert.deepEqual(out.rows.map(row => [row.id, row.reportType]), [['competitor', 'competitor']]);
- assert.equal(out.categories.find(category => category.key === 'category').count, 1);
- assert.equal(out.categories.find(category => category.key === 'competitor').count, 1);
- assert.equal(out.categories.find(category => category.key === 'unclassified').count, 1);
- assert.deepEqual(out.hierarchy.flatMap(group => group.members.flatMap(member => member.reports.map(row => row.id))), ['competitor']);
- });
- test('成员姓名搜索保留零报告成员,报告搜索只显示命中的作者和小组', () => {
- const members = [{ id: 'member-a', name: '甲' }, { id: 'member-c', name: '丙' }];
- const rows = [artifact('a1', { memberId: 'member-a', ownerName: '甲' })];
- const noReport = review(rows, { mode: 'all', search: '丙' }, groups, members);
- assert.equal(noReport.rows.length, 0);
- assert.deepEqual(noReport.hierarchy.map(group => [group.name, group.members.map(member => member.name)]), [['未分组', ['丙']]]);
- const report = review(rows, { mode: 'all', search: '甲' }, groups, members);
- assert.deepEqual(report.hierarchy.map(group => [group.name, group.members.map(member => member.name)]), [['一组', ['甲']]]);
- assert.deepEqual(report.hierarchy[0].members[0].reports.map(row => row.id), ['a1']);
- });
- test('评估、运行记录与概念提案分开,同时都可从总清单打开', () => {
- const db = buildModel({ artifact: [{ objectId: 'accept', artifactType: 'assessment_report', title: '运行验收', stageCode: 'S3' }, { objectId: 'concept', artifactType: 'concept', stageCode: 'S3' }] });
- assert.deepEqual(entriesForType(db, 'concept').map(r => r.id), ['concept']);
- assert.deepEqual(entriesForType(db, 'assessment').map(r => r.id), ['accept']);
- assert.equal(reportCenter(db).total, 2);
- assert.equal(stageOutcome(db, 'S3').total, 2);
- assert.equal(stageOutcome(db, 'S3').groups.find(g => g.key === 'assessment').items[0].id, 'accept');
- });
|