/** * 流程包 1:1 —— 守三件事:拓扑与 MVP 图一致;审批点驳回走声明不走边;网关条件读得到出参。 */ import { describe, expect, it } from 'vitest'; import { MVP_PACKAGE } from '../seed-mvp-flow.ts'; describe('MVP_PACKAGE', () => { it('14 语义节点 · 16 边 · 四网关一审批点双终点', () => { expect(MVP_PACKAGE.nodes).toHaveLength(14); expect(MVP_PACKAGE.edges).toHaveLength(16); expect(MVP_PACKAGE.nodes.filter((n) => n.kind === 'end')).toHaveLength(2); expect(MVP_PACKAGE.nodes.filter((n) => n.kind === 'gateway')).toHaveLength(4); expect(MVP_PACKAGE.nodes.filter((n) => n.kind === 'approval')).toHaveLength(1); }); it('分叉必有汇合:G2 的两条互斥分支并回 G2J —— 不给汇合点,A4 会被判隐式合并', () => { const g2j = MVP_PACKAGE.nodes.find((n) => n.id === 'G2J'); expect(g2j?.kind).toBe('gateway'); if (g2j?.kind !== 'gateway') return; expect(g2j.gateway).toBe('exclusive'); expect(g2j.mode).toBe('join'); expect(MVP_PACKAGE.edges.filter((e) => e.to === 'G2J')).toHaveLength(2); // A4 的非回边入边只剩一条:汇合之后再进活动 expect(MVP_PACKAGE.edges.filter((e) => e.to === 'A4' && !e.reopen)).toHaveLength(1); }); it('A5 是审批点:PM 审,驳回 reopen 回 A4 —— 驳回不是边', () => { const a5 = MVP_PACKAGE.nodes.find((n) => n.id === 'A5'); expect(a5?.kind).toBe('approval'); if (a5?.kind !== 'approval') return; expect(a5.approver.position).toBe('PM'); expect(a5.reject.kind).toBe('reopen'); if (a5.reject.kind === 'reopen') expect(a5.reject.nodeId).toBe('A4'); // 唯一的 reopen 边是议价「有异议」回 A4 expect(MVP_PACKAGE.edges.filter((e) => e.reopen).map((e) => `${e.from}→${e.to}`)).toEqual(['G4→A4']); }); it('网关条件按出参写:G1/G2 读旗,G4 读 outcome', () => { const cond = (from: string, to: string) => MVP_PACKAGE.edges.find((e) => e.from === from && e.to === to)?.condition; expect(cond('G1', 'A2')).toBe('needAlternative = true'); expect(cond('G2', 'G2J')).toBe('needSourcing = false'); expect(cond('G4', 'A7')).toBe('outcome = "lost"'); }); it('手绘图存证在包上 —— 呈现 1:1 的真相(含两枚卡点)', () => { expect(MVP_PACKAGE.diagram).toBeDefined(); const nodes = (MVP_PACKAGE.diagram as { nodes: { id: string; kind: string }[] }).nodes; expect(nodes.filter((n) => n.kind === 'callout')).toHaveLength(2); expect(nodes).toHaveLength(16); }); });