graph-engine-target-dir.test.js
6.37 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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/**
* Unit Tests — graph-engine 的公司级目标代码目录(39 号文档 P5)
*
* 验的是 `ensureProjectRoot` 在配了 `company.json.target_dir` 时的行为:
* T1 没配 target_dir:走老的 sandbox 路径,行为不变(零回归)
* T2 配了 target_dir:两个不同 root_run_id 的 run(同一公司)解析到同一个 root_path
* T3 target_dir 校验失败(git 树不干净):ensureProjectRoot 优雅降级成 null + timeline 记录,
* 不静默 fallback 回 sandbox
* T4 GRAPH_RUNS_DIR 测试覆盖优先级高于 target_dir(不能让业务配置绕过测试隔离)
*
* `ensureProjectRoot` 本身不碰 SQLite(只读写文件系统),所以不需要像
* graph-boundary.test.js 那样做 GRAPH_DB_PATH 隔离;但会真的往 `data/companies/` 写
* 东西(company.json 走 companyRootDir,不认测试注入,除非设 GRAPH_RUNS_DIR——T1-T3
* 就是要验「不设 GRAPH_RUNS_DIR 时的生产行为」,所以只能用真路径 + 用后即删,
* 手法同 path-resolver.test.js 的 sandbox 用例)。
*/
const fs = require('fs');
const os = require('os');
const path = require('path');
const engine = require('../../services/graph-engine');
const { companyRootDir } = require('../../services/company-paths');
describe('graph-engine 公司级目标代码目录(target_dir)', () => {
const cleanups = [];
afterEach(() => {
delete process.env.GRAPH_RUNS_DIR;
while (cleanups.length) {
const p = cleanups.pop();
try { fs.rmSync(p, { recursive: true, force: true }); } catch { /* best-effort */ }
}
});
function writeCompany(companyId, patch) {
const dir = companyRootDir(companyId);
fs.mkdirSync(dir, { recursive: true });
cleanups.push(dir);
fs.writeFileSync(path.join(dir, 'company.json'), JSON.stringify({ id: companyId, name: companyId, ...patch }));
return dir;
}
function fakeRun(companyId, rootRunId) {
return { root_run_id: rootRunId, company_id: companyId };
}
it('T1 没配 target_dir:走 sandbox,行为跟改动前一样', () => {
const cid = 'tdcfg-t1';
writeCompany(cid, {});
const run = fakeRun(cid, 'r-t1');
const rec = engine.ensureProjectRoot({ run });
expect(rec).not.toBe(null);
expect(rec.mode).toBe('sandbox');
expect(rec.root_path).toContain(path.join('data', 'companies', cid, 'projects'));
expect(fs.existsSync(rec.root_path)).toBe(true);
cleanups.push(path.join(companyRootDir(cid), '..', 'runs')); // 无害;下面按 run 目录精确清
cleanups.push(path.dirname(path.dirname(rec.root_path))); // projects/ 目录
cleanups.push(path.join(companyRootDir(cid), 'runs', run.root_run_id));
});
it('T2 配了 target_dir:同一公司两个不同 run 解析到同一个 root_path,且不产生 pid 目录', () => {
const cid = 'tdcfg-t2';
const target = fs.mkdtempSync(path.join(os.tmpdir(), 'graph-engine-target-'));
cleanups.push(target);
fs.writeFileSync(path.join(target, 'README.md'), '# existing repo, no .git');
writeCompany(cid, { target_dir: target, target_mode: 'linked-existing' });
const runA = fakeRun(cid, 'r-t2-a');
const runB = fakeRun(cid, 'r-t2-b');
cleanups.push(path.join(companyRootDir(cid), 'runs', runA.root_run_id));
cleanups.push(path.join(companyRootDir(cid), 'runs', runB.root_run_id));
const recA = engine.ensureProjectRoot({ run: runA });
const recB = engine.ensureProjectRoot({ run: runB });
expect(recA).not.toBe(null);
expect(recB).not.toBe(null);
expect(recA.root_path).toBe(target);
expect(recB.root_path).toBe(target);
expect(recA.mode).toBe('linked-existing');
// 没有新的 sandbox pid 目录被造出来
const projectsDir = path.join(companyRootDir(cid), 'projects');
expect(fs.existsSync(projectsDir)).toBe(false);
});
it('T2b target_mode 缺省时按 linked-existing 处理(目标目录本来就该已经存在)', () => {
const cid = 'tdcfg-t2b';
const target = fs.mkdtempSync(path.join(os.tmpdir(), 'graph-engine-target-'));
cleanups.push(target);
writeCompany(cid, { target_dir: target }); // 不传 target_mode
const run = fakeRun(cid, 'r-t2b');
cleanups.push(path.join(companyRootDir(cid), 'runs', run.root_run_id));
const rec = engine.ensureProjectRoot({ run });
expect(rec.root_path).toBe(target);
expect(rec.mode).toBe('linked-existing');
});
it('T3 target_dir 校验失败(git 树不干净):不静默降级回 sandbox,返回 null', () => {
const { execFileSync } = require('child_process');
const cid = 'tdcfg-t3';
const target = fs.mkdtempSync(path.join(os.tmpdir(), 'graph-engine-target-dirty-'));
cleanups.push(target);
execFileSync('git', ['init', '-q', '-b', 'main'], { cwd: target });
execFileSync('git', ['config', 'user.email', 't@l'], { cwd: target });
execFileSync('git', ['config', 'user.name', 't'], { cwd: target });
fs.writeFileSync(path.join(target, 'a.txt'), 'one');
execFileSync('git', ['add', '-A'], { cwd: target });
execFileSync('git', ['commit', '-q', '-m', 'init'], { cwd: target });
fs.writeFileSync(path.join(target, 'b.txt'), 'untracked, dirty tree');
writeCompany(cid, { target_dir: target, target_mode: 'linked-existing' });
const run = fakeRun(cid, 'r-t3');
cleanups.push(path.join(companyRootDir(cid), 'runs', run.root_run_id));
const rec = engine.ensureProjectRoot({ run });
expect(rec).toBe(null); // 优雅失败,不是编一个假 sandbox 路径
const projectsDir = path.join(companyRootDir(cid), 'projects');
expect(fs.existsSync(projectsDir)).toBe(false); // 确认真的没有偷偷退化成 sandbox
});
it('T4 GRAPH_RUNS_DIR 测试覆盖优先级高于 target_dir(测试隔离不能被业务配置绕过)', () => {
const cid = 'tdcfg-t4';
const target = fs.mkdtempSync(path.join(os.tmpdir(), 'graph-engine-target-override-'));
cleanups.push(target);
writeCompany(cid, { target_dir: target, target_mode: 'linked-existing' });
const runsDir = fs.mkdtempSync(path.join(os.tmpdir(), 'graph-engine-runs-override-'));
cleanups.push(runsDir);
process.env.GRAPH_RUNS_DIR = runsDir;
const run = fakeRun(cid, 'r-t4');
const rec = engine.ensureProjectRoot({ run });
expect(rec.mode).toBe('runs-dir-override');
expect(rec.root_path).toBe(path.join(runsDir, run.root_run_id, 'project'));
expect(rec.root_path).not.toBe(target);
});
});