prompt-builder.test.js
5.35 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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
const path = require('path');
const fs = require('fs');
const { PromptBuilder } = require('../../core/prompt-builder');
describe('PromptBuilder', () => {
const baseDir = path.join(__dirname, '..', 'fixtures');
const configPath = path.join(baseDir, 'config.json');
let config;
let builder;
beforeEach(() => {
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
builder = new PromptBuilder(config, baseDir);
});
describe('getProtocol', () => {
it('should load executor protocol', () => {
const protocol = builder.getProtocol('executor');
expect(protocol).toContain('[RESULT: DONE]');
expect(protocol).toContain('[RESULT: ERROR]');
});
it('should load validator protocol', () => {
const protocol = builder.getProtocol('validator');
expect(protocol).toContain('[RESULT: PASS]');
expect(protocol).toContain('[RESULT: FAIL]');
});
it('should load master protocol', () => {
const protocol = builder.getProtocol('master');
expect(protocol).toContain('[CALL:');
expect(protocol).toContain('{{AVAILABLE_AGENTS}}');
});
it('should return empty string for unknown type', () => {
const protocol = builder.getProtocol('unknown');
expect(protocol).toBe('');
});
});
describe('getAgentPrompt', () => {
it('should load coder prompt', () => {
const prompt = builder.getAgentPrompt('coder');
expect(prompt).toContain('Coder');
expect(prompt).toContain('代码');
});
it('should load verifier prompt', () => {
const prompt = builder.getAgentPrompt('verifier');
expect(prompt).toContain('Verifier');
});
it('should throw for unknown agent', () => {
expect(() => builder.getAgentPrompt('unknown')).toThrow('Agent not found');
});
});
describe('build', () => {
it('should combine protocol and agent prompt for executor', () => {
const result = builder.build('coder');
// 应该包含 executor protocol
expect(result).toContain('[RESULT: DONE]');
expect(result).toContain('[RESULT: ERROR]');
// 应该包含 coder prompt
expect(result).toContain('Coder');
});
it('should combine protocol and agent prompt for validator', () => {
const result = builder.build('verifier');
// 应该包含 validator protocol
expect(result).toContain('[RESULT: PASS]');
expect(result).toContain('[RESULT: FAIL]');
// 应该包含 verifier prompt
expect(result).toContain('Verifier');
});
it('should replace {{AVAILABLE_AGENTS}} in master protocol', () => {
const result = builder.build('master');
// 不应该包含占位符
expect(result).not.toContain('{{AVAILABLE_AGENTS}}');
// 应该包含实际的 agent 列表
expect(result).toContain('coder');
expect(result).toContain('verifier');
expect(result).toContain('frontend-tester');
});
it('should append user task when provided', () => {
const result = builder.build('coder', '实现一个计算器');
expect(result).toContain('当前任务');
expect(result).toContain('实现一个计算器');
});
it('should not include task section when no task provided', () => {
const result = builder.build('coder');
expect(result).not.toContain('当前任务');
});
});
describe('build - companyContextPath (P1 virtual reference)', () => {
const os = require('os');
let tmpFile;
afterEach(() => {
if (tmpFile && fs.existsSync(tmpFile)) {
fs.unlinkSync(tmpFile);
}
tmpFile = undefined;
});
it('appends company-context.md content when companyContextPath exists', () => {
tmpFile = path.join(os.tmpdir(), `company-context-${Date.now()}.md`);
fs.writeFileSync(tmpFile, '## 所属公司\n- **公司ID**: test-co', 'utf-8');
config.agents.coder.companyContextPath = tmpFile;
const result = builder.build('coder');
expect(result).toContain('## 所属公司');
expect(result).toContain('test-co');
});
// <timestamp> in getSystemStateXML() is wall-clock and can legitimately
// differ by a millisecond between two build() calls, so strip it before
// comparing outputs for structural equality.
const stripTimestamp = (s) => s.replace(/<timestamp>.*?<\/timestamp>/, '<timestamp/>');
it('does not change output when companyContextPath is null (legacy mode)', () => {
const baseline = stripTimestamp(builder.build('coder'));
config.agents.coder.companyContextPath = null;
const withNull = stripTimestamp(builder.build('coder'));
expect(withNull).toBe(baseline);
});
it('does not change output when companyContextPath points to a nonexistent file', () => {
const baseline = stripTimestamp(builder.build('coder'));
config.agents.coder.companyContextPath = path.join(os.tmpdir(), 'does-not-exist-xyz.md');
const withMissing = stripTimestamp(builder.build('coder'));
expect(withMissing).toBe(baseline);
});
});
describe('caching', () => {
it('should cache loaded files', () => {
// 第一次加载
builder.build('coder');
// 第二次加载应该使用缓存
const cacheSize = builder.promptCache.size;
builder.build('coder');
expect(builder.promptCache.size).toBe(cacheSize);
});
});
});