Archived
MVP底座④:统一报价 1:1 流程包单实例 —— 四域物化,种子整体切换
14 语义节点/16 边:A5 是内置审批点(驳回走 reject 声明 reopen A4), G2 的两条互斥分支显式汇合于 G2J —— 隐式合并过不了闭合校验(实测)。 顺手修 graph-to-pkg:卡点带 to 时还原成语义边,否则回边画成卡点一存盘就没了。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 流程包 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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user