This repository has been archived on 2026-09-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BPOS-TMP-REPO/apps/web/src/shared/gov/__tests__/surfaces.test.ts
T
ChaiHaoandClaude Opus 5 e5cfca638f 运营中心撤掉「数据」区;流程包首页重排:卡有层次、⋯ 管编辑与删除、新建变幽灵卡
**运营 · 数据(活动记忆四层)整个撤掉** —— 记忆的放行口还没成形,摆一个空区不如
不摆;真要做时随「数据资产」一并重开。区表、面归属、shell 映射、KnowledgeHome /
MemPage、活动详情里那枚入口一并清干净;旧深链 `#/gov/memory/layers` 落运营首页
(测试跟着改口径,不留死链)。运营中心自此六区。

**流程包首页**:
  · 「编排要素:四种」这枚计数删了 —— 网关早废,它是句没人用的口号;
  · 卡片重做。从前是「头 + 芯片行 + 一大片空 + 脚注」,158 高全靠留白撑;
    现在两段:头(图标 · 名 · 标识 · 生效版 · ⋯)+ **数据板**(活动/审批/边闭合/版本
    四格等宽、竖线分隔)。段段有内容、段间有线,高矮由内容说了算 —— 实测 111 高;
  · 卡上 **⋯ 菜单**:编辑(就地改名,落在卡面那一版上)/ 删除(整条流,菜单原地翻成
    确认,不另弹窗);
  · **新建按钮变幽灵卡**:虚线卡住在货架末位,与真卡同宽同高(111/111)。

删整条流是新能力:`DELETE /api/gov/process/:id` + `process.deleteFlow`。
生效版这道闸不设(明示动作,不是手滑),在途单那道照设(PinnedVersionError → 409);
**「还有调起规则指着它」由路由层反查** —— process 是 L7,不该知道 trigger 的存在。

实测(丁子乔账号):改名往返、删除被 2 条调起规则如实挡回、幽灵卡建 `tmp-drop`
再删掉整条流,库里回到只剩 unified-quote v4,试跑数据已清。五闸绿。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-29 22:38:52 +08:00

102 lines
4.3 KiB
TypeScript

/**
* 三面骨架的唯一事实源 —— 区归属、亮灯、旧链接改写。
*
* 这里守的是「配置自洽」:每个区恰属一面(不重不漏);亮灯只看该面功能;旧深链一条不死。
* 三者都是纯函数,壳层只消费 —— 配置错了在这里就红,不必等到点开界面才发现。
*/
import { describe, expect, it } from 'vitest';
import type { Session } from '@contracts';
import { sessionSchema } from '@contracts';
import { GOV_ZONES } from '../gov-route.ts';
import { SURFACE_ZONES, canSeeSurface, legacyGovTarget, surfaceOfZone } from '../surfaces.ts';
/** 经 schema 构造合法会话 —— §12.5 禁 `as unknown as`,测试同样遵守 */
const sessionWith = (fns: readonly string[]): Session =>
sessionSchema.parse({
staff: { id: 's-1', account: 'a', name: 'n', positionId: 'p-1', state: 'active' },
position: { id: 'p-1', code: 'PM', name: '项目经理' },
grants: fns.map((fn) => ({
positionId: 'p-1',
object: { kind: 'governance', function: fn },
action: 'read',
})),
});
describe('SURFACE_ZONES —— 每个区恰属一面', () => {
it('两面之和即全部区,不重不漏', () => {
const all = [...SURFACE_ZONES.design, ...SURFACE_ZONES.ops];
expect([...all].sort()).toEqual([...GOV_ZONES].sort());
expect(new Set(all).size).toBe(all.length);
});
it('设计面没有任何运行态区', () => {
for (const zone of ['tasks', 'cockpit', 'ledgers'] as const) {
expect(SURFACE_ZONES.design).not.toContain(zone);
}
});
it('surfaceOfZone 与区表一致', () => {
expect(surfaceOfZone('workflows')).toBe('design');
expect(surfaceOfZone('ledgers')).toBe('ops');
});
});
describe('canSeeSurface —— 入口按授权亮灯', () => {
it('持有该面任一功能的授权即亮', () => {
expect(canSeeSurface(sessionWith(['process']), 'design')).toBe(true);
expect(canSeeSurface(sessionWith(['task']), 'ops')).toBe(true);
});
it('只有另一面的授权不亮', () => {
expect(canSeeSurface(sessionWith(['task']), 'design')).toBe(false);
expect(canSeeSurface(sessionWith(['activity']), 'ops')).toBe(false);
});
it('trigger 与 data 两面都算 —— 定义归设计,处置与行数据归运营', () => {
expect(canSeeSurface(sessionWith(['trigger']), 'design')).toBe(true);
expect(canSeeSurface(sessionWith(['trigger']), 'ops')).toBe(true);
expect(canSeeSurface(sessionWith(['data']), 'design')).toBe(true);
expect(canSeeSurface(sessionWith(['data']), 'ops')).toBe(true);
});
it('无会话不亮;无治理授权不亮', () => {
expect(canSeeSurface(undefined, 'design')).toBe(false);
expect(canSeeSurface(sessionWith([]), 'ops')).toBe(false);
});
});
describe('legacyGovTarget —— 旧 #/gov 深链一条不死', () => {
it('设计态区改写到 /design,深链原样带走', () => {
expect(legacyGovTarget('#/gov/workflows/pkgs/pkg:unified-quote%404')).toBe(
'/design#/design/workflows/pkgs/pkg:unified-quote%404',
);
});
it('运行态区改写到 /ops', () => {
// 看板已迁「工作流」区(2026-07-30):工作流是流程包的运行时,一级看板归它
expect(legacyGovTarget('#/gov/tasks/board')).toBe('/ops#/ops/runs/board');
});
it('已删的区落到对应新家', () => {
expect(legacyGovTarget('#/gov/memory/metrics')).toBe('/ops#/ops/cockpit/dash');
// 活动记忆那一区 2026-07-29 整个撤了(运营 · 数据)—— 旧链接落运营首页,不给白屏
expect(legacyGovTarget('#/gov/memory/layers')).toBe('/ops#/ops/cockpit/dash');
});
/*
* 触发器与大模型**又独立成区**了(2026-07-29):触发器一度并进流程包页签、
* 模型一度住在系统区 —— 那两段旧链接要各回各家,不能停在中途那一站。
*/
it('搬回自己区的:触发器与大模型', () => {
expect(legacyGovTarget('#/gov/triggers/rules')).toBe('/design#/design/triggers/rules');
expect(legacyGovTarget('#/gov/workflows/rules')).toBe('/design#/design/triggers/rules');
expect(legacyGovTarget('#/gov/system/llm')).toBe('/design#/design/models/llm');
});
it('裸 #/gov 落运营首页;非 gov 哈希不管', () => {
expect(legacyGovTarget('#/gov')).toBe('/ops#/ops/cockpit/dash');
expect(legacyGovTarget('')).toBeUndefined();
expect(legacyGovTarget('#/whatever')).toBeUndefined();
});
});