graph-seed-meta-bootstrap.test.js
4.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/**
* Unit Tests — verifyCounts 必须镜像 seedCompanyAgents 的 meta-bootstrap 过滤
*
* `seedCompanyAgents` 对 `type: 'meta-bootstrap'` 的公司角色是 `continue`:
* 既不写 org_graph_nodes 也不写 system_agents。而 `verifyCounts` 复算期望值时
* 原先把 `company.json` 的 `agents[]` **全部**算进去 —— 于是任何一家公司的
* agents[] 里出现一个 meta-bootstrap 角色,对账就确定性变红。
*
* 不是假设:用户手工走查建的 `mycompany` 有 10 个角色,其中 `orggraph-planning`
* 是 meta-bootstrap,对账报 `expected 10 / actual 9`。也就是说这条缺陷是被
* **正常使用**触发的 —— 界面上点一遍新建公司就会踩到。
*
* 这个文件单独存在(不并进 graph-seed-service.test.js)的原因:那边 hermetic 块的
* 断言钉着精确的行数/节点数,往它的夹具里加一个角色会连带改掉既有断言。
* 这里用 `GRAPH_SEED_ROOT` 自建一个只有两个角色的最小夹具,互不干扰。
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import fs from 'fs';
import os from 'os';
import path from 'path';
const MODULES = ['../../services/graph-db', '../../services/graph-seed-service'];
describe('verifyCounts × meta-bootstrap 过滤', () => {
let seed;
let root;
let dbPath;
const COMPANY = 'co-mb';
const NORMAL_ROLE = 'org-planner';
const BOOTSTRAP_ROLE = 'orggraph-planning';
beforeAll(() => {
root = fs.mkdtempSync(path.join(os.tmpdir(), 'seed-mb-root-'));
dbPath = path.join(root, 'graph.sqlite');
// 最小夹具:system/agents/<id>/{agent.md, config.json} + system/agents/_registry.json
// + data/companies/<cid>/company.json
//
// config.json 放在**本体**里(28 批次 4):公司不再持有 agent 实体,
// isMetaBootstrap() 读的就是本体那一份。放回公司侧的话它读不到、恒返回 false,
// 症状正是本文件要防的那个 —— meta-bootstrap 角色被算进期望值。
for (const [id, type] of [[NORMAL_ROLE, 'executor'], [BOOTSTRAP_ROLE, 'meta-bootstrap']]) {
const d = path.join(root, 'system', 'agents', id);
fs.mkdirSync(d, { recursive: true });
fs.writeFileSync(path.join(d, 'agent.md'), `# ${id}\n`);
fs.writeFileSync(path.join(d, 'config.json'), JSON.stringify({ type }));
}
fs.writeFileSync(path.join(root, 'system', 'agents', '_registry.json'), JSON.stringify({}));
const companyDir = path.join(root, 'data', 'companies', COMPANY);
fs.mkdirSync(companyDir, { recursive: true });
fs.writeFileSync(
path.join(companyDir, 'company.json'),
JSON.stringify({ id: COMPANY, name: COMPANY, agents: [NORMAL_ROLE, BOOTSTRAP_ROLE] })
);
process.env.GRAPH_SEED_ROOT = root;
process.env.GRAPH_DB_PATH = dbPath;
for (const m of MODULES) delete require.cache[require.resolve(m)];
seed = require('../../services/graph-seed-service');
});
afterAll(() => {
delete process.env.GRAPH_SEED_ROOT;
delete process.env.GRAPH_DB_PATH;
try { fs.rmSync(root, { recursive: true, force: true }); } catch {}
for (const m of MODULES) delete require.cache[require.resolve(m)];
});
it('种完之后对账干净:meta-bootstrap 角色不算进期望值', () => {
// runStartupSeed 只做 reconcile + seedAgents,**不含** seedCompanyAgents
// (两个 server 入口启动时也不种公司角色)—— 所以公司那部分要显式调。
// 少了这一行的话对账会报 expected 1 / actual 0,看着像本条要防的那个 bug,
// 其实是夹具没种东西:两种原因症状相同,所以下面第二条用例专门把它们分开。
seed.runStartupSeed();
seed.seedCompanyAgents();
const v = seed.verifyCounts();
// 期望值把 meta-bootstrap 算进去时,这里会是
// [{check: "org_graph_nodes COUNT(*) WHERE company_id='co-mb'", expected: 2, actual: 1}]
expect(v.mismatches).toEqual([]);
expect(v.ok).toBe(true);
});
it('这个夹具确实覆盖到了那条分支(否则上一条是空跑)', () => {
const db = require('../../services/graph-db').getDb();
const rows = db
.prepare(`SELECT role_name FROM org_graph_nodes WHERE company_id = ? ORDER BY role_name`)
.all(COMPANY)
.map((r) => r.role_name);
// 普通角色种进去了、meta-bootstrap 角色没有 —— 两侧都断言,
// 只断言「对账干净」的话,seed 一个都没种时它也会“通过”
expect(rows).toEqual([NORMAL_ROLE]);
});
});