| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- import { test } from 'node:test';
- import assert from 'node:assert/strict';
- import { pipelineSummary, reportOverview, findingsOf, chapters } from '../src/core/report.js';
- import { liveBoard, forMe, stuckMembers } from '../src/core/kfs/stats.js';
- import { buildModel } from '../src/core/kfs/build.js';
- import { reportEvidence, reportConclusions, conclusionToLedger, traceInsight } from '../src/core/evidence/chain.js';
- import { latest, fmtAgo, fmtDate, toTime, truncate, clamp } from '../src/core/kfs/util.js';
- import { normDecision, cardSummary } from '../src/core/opportunity/opportunity.js';
- const raw = {
- artifact: [
- { objectId: 'r', title: '报告 - competitor_report', ownerName: '张伟', stage: 'S1品类分析',
- report: {
- title: '测试报告', subtitle: '副标题', pipeline: [{ label: '采集', value: 2727 }, { label: '去噪', value: 1190 }, { label: '精准', value: 730 }],
- chapters: [
- { id: 'ch1', no: 1, title: '一', stage: 'S1', lead: '问题一', sections: [{ kind: 'para', text: 'x' }] },
- { id: 'ch2', no: 2, title: '二', stage: 'S2', lead: '问题二', sections: [{ kind: 'findings', data: [{ conclusion: 'A', evidence: 'e1' }, { conclusion: 'A', evidence: 'e1' }] }] },
- { id: 'ch3', no: 3, title: '三', stage: 'S3', sections: [{ kind: 'mystery', data: { a: 1 } }] },
- ],
- evidence: { e1: { claim: 'A', ownerCount: 3, quotes: [{ text: 'q' }], derivedFrom: ['a1'] } },
- } },
- { objectId: 'a1', title: '成果一', ownerName: '李娜', stage: 'S2需求洞察' },
- ],
- insight: [{ objectId: 'i1', title: '归纳', derivedArtifacts: ['a1'] }],
- session: [],
- project: [],
- source: [],
- opportunity: [],
- };
- const db = buildModel(raw);
- test('数据管线:首尾推算过滤率', () => {
- const p = pipelineSummary({ pipeline: [{ label: 'a', value: 2727 }, { label: 'b', value: 1190 }, { label: 'c', value: 730 }] });
- assert.equal(p.filterRate, 73);
- assert.equal(p.steps.length, 3);
- assert.equal(pipelineSummary({}), null);
- });
- test('报告概览:章数 / 结论 / 引用 / 原文 / 跨阶段', () => {
- const r = reportOverview(db, 'r');
- assert.equal(r.chapterCount, 3);
- assert.equal(r.conclusionCount, 1);
- assert.equal(r.refsInLib, 1);
- assert.equal(r.quoteCount, 1);
- assert.deepEqual(r.stages, ['S1', 'S2', 'S3']);
- assert.equal(r.pipeline.filterRate, 73);
- assert.equal(reportOverview(db, 'a1'), null, '没有 report 的成果不进报告目录');
- });
- test('结论块去重(同一证据锚点只出现一次)', () => {
- const f = findingsOf(db, 'r');
- assert.equal(f.length, 1);
- assert.equal(f[0].ownerCount, 3);
- });
- test('章节归一:缺 id 用序号补齐', () => {
- const c = chapters({ chapters: [{ title: 'x' }, { id: 'y', title: 'z' }] });
- assert.equal(c[0].id, 'ch1');
- assert.equal(c[1].no, 2);
- });
- test('证据锚点 → 台账行 id(含不存在的成果也如实返回)', () => {
- db.artifactById.ghost = undefined;
- assert.deepEqual(conclusionToLedger(db, 'r', 'e1'), ['a1']);
- assert.equal(reportEvidence(db, 'r', 'nope'), null);
- assert.deepEqual(reportConclusions(db, 'r').map((c) => c.id), ['e1']);
- });
- test('归纳 → 来自的报告', () => {
- // 现实中归纳引用的成果是带报告的成果本身(reportArtifact),不是旁证;用这种形态验证
- const db2 = buildModel({ insight: [{ objectId: 'i2', title: '归纳2', derivedArtifacts: ['r'] }], artifact: raw.artifact });
- const t = traceInsight(db2, 'i2');
- assert.deepEqual(t.reports.map((r) => r.id), ['r']);
- assert.equal(t.reports[0].title, '测试报告');
- });
- test('时间工具:Parse 日期 / ISO / 毫秒都能读,空值不炸', () => {
- assert.equal(toTime({ __type: 'Date', iso: '2026-10-29T00:00:00Z' }), Date.parse('2026-10-29T00:00:00Z'));
- assert.equal(toTime(null), 0);
- assert.equal(toTime('坏数据'), 0);
- assert.equal(latest([null, '2026-01-01T00:00:00Z', '2025-01-01T00:00:00Z']), '2026-01-01T00:00:00.000Z');
- assert.equal(fmtDate(null), '—');
- assert.equal(fmtAgo(new Date(Date.now() - 3 * 3600000).toISOString()), '3\u2009小时前');
- assert.equal(truncate('一二三四五', 3), '一二三…');
- assert.equal(clamp(120, 0, 100), 100);
- });
- test('决策归一:未知类型保留 label,缺 label 给「决策」', () => {
- assert.equal(normDecision({ type: 'weird', label: '暂缓(补原文)' }).label, '暂缓(补原文)');
- assert.equal(normDecision({ type: 'weird' }).label, '决策');
- assert.equal(normDecision(null), null);
- });
- test('卡片摘要不含内部大对象', () => {
- const o = db.opportunities[0] || buildModel({ opportunity: [{ objectId: 'o', title: 'T', oneLiner: 'x'.repeat(200) }] }).opportunities[0];
- const s = cardSummary(o, 10);
- assert.equal(s.title, 'T');
- assert.equal(Object.keys(s).includes('linkedArtifacts'), false);
- });
- test('现场看板:没有任何会话时不崩', () => {
- const b = liveBoard(db);
- assert.equal(b.stages.length, 5);
- assert.deepEqual(stuckMembers(db), []);
- assert.equal(forMe(db, '').count, 0);
- });
|