三面拆分①:surface 配置与区表 —— 归属/亮灯/旧链改写皆纯函数先测后写

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
2026-07-27 20:50:32 +08:00
co-authored by Claude Fable 5
parent ac85ecf2ea
commit e578b5bc34
6 changed files with 309 additions and 50 deletions
@@ -0,0 +1,90 @@
/**
* 三面骨架的唯一事实源 —— 区归属、亮灯、旧链接改写。
*
* 这里守的是「配置自洽」:每个区恰属一面(不重不漏);亮灯只看该面功能;旧深链一条不死。
* 三者都是纯函数,壳层只消费 —— 配置错了在这里就红,不必等到点开界面才发现。
*/
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', () => {
expect(legacyGovTarget('#/gov/tasks/board')).toBe('/ops#/ops/tasks/board');
});
it('已删的区落到对应新家', () => {
expect(legacyGovTarget('#/gov/triggers/rules')).toBe('/design#/design/workflows/rules');
expect(legacyGovTarget('#/gov/memory/metrics')).toBe('/ops#/ops/cockpit/dash');
expect(legacyGovTarget('#/gov/memory/layers')).toBe('/ops#/ops/knowledge/layers');
});
it('裸 #/gov 落运营首页;非 gov 哈希不管', () => {
expect(legacyGovTarget('#/gov')).toBe('/ops#/ops/cockpit/dash');
expect(legacyGovTarget('')).toBeUndefined();
expect(legacyGovTarget('#/whatever')).toBeUndefined();
});
});