fileditor-mcp 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/1781792324448_PixelTunnelSender.exe +0 -0
- package/LICENSE +21 -21
- package/README.md +128 -128
- package/docs/MCP-INTERFACE.cn.md +975 -1061
- package/docs/MCP-INTERFACE.en.md +881 -974
- package/docs/README.cn.md +128 -128
- package/package.json +30 -31
- package/src/handlers/applyDiff.js +298 -324
- package/src/handlers/insertContent.js +179 -153
- package/src/handlers/listFiles.js +65 -54
- package/src/handlers/readFile.js +109 -109
- package/src/handlers/searchAndReplace.js +198 -217
- package/src/handlers/setWorkspace.js +26 -26
- package/src/handlers/writeFile.js +97 -91
- package/src/index.js +22 -22
- package/src/server.js +85 -85
- package/src/tools/toolDefinitions.js +291 -301
- package/src/utils/fileUtils.js +238 -716
- package/test/ApplyDiffHandler.test.js +754 -754
- package/test/InsertContentHandler.test.js +371 -369
- package/test/ListFilesHandler.test.js +302 -300
- package/test/ReadFileHandler.test.js +213 -213
- package/test/SearchAndReplaceHandler.test.js +505 -505
- package/test/SetWorkspaceHandler.test.js +290 -290
- package/test/WriteFileHandler.test.js +289 -289
- package/test/runAllTests.js +233 -233
|
@@ -1,289 +1,289 @@
|
|
|
1
|
-
import { WriteFileHandler } from '../src/handlers/writeFile.js';
|
|
2
|
-
import { FileUtils } from '../src/utils/fileUtils.js';
|
|
3
|
-
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
4
|
-
import assert from 'node:assert';
|
|
5
|
-
import fs from 'fs/promises';
|
|
6
|
-
import { join } from 'path';
|
|
7
|
-
import { existsSync } from 'fs';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* WriteFileHandler 测试类
|
|
11
|
-
* 测试文件写入功能
|
|
12
|
-
*/
|
|
13
|
-
describe('WriteFileHandler', () => {
|
|
14
|
-
|
|
15
|
-
const testDir = './test_files_write';
|
|
16
|
-
const testFile1 = join(testDir, 'test1.txt');
|
|
17
|
-
const testFile2 = join(testDir, 'test2.txt');
|
|
18
|
-
|
|
19
|
-
beforeEach(async () => {
|
|
20
|
-
// 设置测试工作区
|
|
21
|
-
FileUtils.setWorkspaceRoot(process.cwd());
|
|
22
|
-
|
|
23
|
-
// 创建测试目录
|
|
24
|
-
if (!existsSync(testDir)) {
|
|
25
|
-
await fs.mkdir(testDir, { recursive: true });
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
afterEach(async () => {
|
|
30
|
-
// 清理测试文件
|
|
31
|
-
try {
|
|
32
|
-
if (existsSync(testFile1)) await fs.unlink(testFile1);
|
|
33
|
-
if (existsSync(testFile2)) await fs.unlink(testFile2);
|
|
34
|
-
if (existsSync(testDir)) await fs.rmdir(testDir);
|
|
35
|
-
} catch (error) {
|
|
36
|
-
console.warn('清理测试文件失败:', error.message);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe('单文件写入', () => {
|
|
41
|
-
|
|
42
|
-
it('应该成功创建新文件', async () => {
|
|
43
|
-
const content = 'Line 1\nLine 2\nLine 3';
|
|
44
|
-
const args = {
|
|
45
|
-
path: testFile1,
|
|
46
|
-
content: content,
|
|
47
|
-
line_count: 3
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const result = await WriteFileHandler.handle(args);
|
|
51
|
-
|
|
52
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
53
|
-
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
54
|
-
assert.ok(result.content[0].text.includes('3 lines'));
|
|
55
|
-
|
|
56
|
-
// 验证文件内容
|
|
57
|
-
const writtenContent = await fs.readFile(testFile1, 'utf8');
|
|
58
|
-
assert.strictEqual(writtenContent, content);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('应该覆盖现有文件', async () => {
|
|
62
|
-
// 先创建一个文件
|
|
63
|
-
await fs.writeFile(testFile1, 'Old content', 'utf8');
|
|
64
|
-
|
|
65
|
-
const newContent = 'New content\nSecond line';
|
|
66
|
-
const args = {
|
|
67
|
-
path: testFile1,
|
|
68
|
-
content: newContent,
|
|
69
|
-
line_count: 2
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const result = await WriteFileHandler.handle(args);
|
|
73
|
-
|
|
74
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
75
|
-
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
76
|
-
|
|
77
|
-
// 验证文件内容被覆盖
|
|
78
|
-
const writtenContent = await fs.readFile(testFile1, 'utf8');
|
|
79
|
-
assert.strictEqual(writtenContent, newContent);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('应该处理空文件写入', async () => {
|
|
83
|
-
const args = {
|
|
84
|
-
path: testFile1,
|
|
85
|
-
content: '',
|
|
86
|
-
line_count: 0
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const result = await WriteFileHandler.handle(args);
|
|
90
|
-
|
|
91
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
92
|
-
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
93
|
-
|
|
94
|
-
// 验证文件为空
|
|
95
|
-
const writtenContent = await fs.readFile(testFile1, 'utf8');
|
|
96
|
-
assert.strictEqual(writtenContent, '');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('应该发出警告当行数不匹配时', async () => {
|
|
100
|
-
const content = 'Line 1\nLine 2\nLine 3';
|
|
101
|
-
const args = {
|
|
102
|
-
path: testFile1,
|
|
103
|
-
content: content,
|
|
104
|
-
line_count: 5 // 错误的行数
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const result = await WriteFileHandler.handle(args);
|
|
108
|
-
|
|
109
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
110
|
-
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
111
|
-
// 注意:警告是通过console.warn输出的,这里检查结果仍然成功
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe('多文件写入', () => {
|
|
117
|
-
|
|
118
|
-
it('应该成功写入多个文件(相同内容)', async () => {
|
|
119
|
-
const content = 'Shared content\nLine 2';
|
|
120
|
-
const args = {
|
|
121
|
-
path: [testFile1, testFile2],
|
|
122
|
-
content: content,
|
|
123
|
-
line_count: 2
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const result = await WriteFileHandler.handle(args);
|
|
127
|
-
|
|
128
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
129
|
-
assert.ok(result.content[0].text.includes('Successfully wrote 2 files'));
|
|
130
|
-
assert.ok(result.content[0].text.includes('4 total lines'));
|
|
131
|
-
|
|
132
|
-
// 验证两个文件内容
|
|
133
|
-
const content1 = await fs.readFile(testFile1, 'utf8');
|
|
134
|
-
const content2 = await fs.readFile(testFile2, 'utf8');
|
|
135
|
-
assert.strictEqual(content1, content);
|
|
136
|
-
assert.strictEqual(content2, content);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('应该成功写入多个文件(不同内容)', async () => {
|
|
140
|
-
const content1 = 'File 1 content';
|
|
141
|
-
const content2 = 'File 2 content\nSecond line';
|
|
142
|
-
const args = {
|
|
143
|
-
path: [testFile1, testFile2],
|
|
144
|
-
content: [content1, content2],
|
|
145
|
-
line_count: [1, 2]
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const result = await WriteFileHandler.handle(args);
|
|
149
|
-
|
|
150
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
151
|
-
assert.ok(result.content[0].text.includes('Successfully wrote 2 files'));
|
|
152
|
-
assert.ok(result.content[0].text.includes('3 total lines'));
|
|
153
|
-
|
|
154
|
-
// 验证文件内容
|
|
155
|
-
const writtenContent1 = await fs.readFile(testFile1, 'utf8');
|
|
156
|
-
const writtenContent2 = await fs.readFile(testFile2, 'utf8');
|
|
157
|
-
assert.strictEqual(writtenContent1, content1);
|
|
158
|
-
assert.strictEqual(writtenContent2, content2);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('应该抛出错误当路径和内容数组长度不匹配时', async () => {
|
|
162
|
-
const args = {
|
|
163
|
-
path: [testFile1, testFile2],
|
|
164
|
-
content: ['Content 1'], // 只有一个内容,但有两个路径
|
|
165
|
-
line_count: [1, 1]
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
await assert.rejects(
|
|
169
|
-
() => WriteFileHandler.handle(args),
|
|
170
|
-
(error) => error.message.includes("doesn't match content count")
|
|
171
|
-
);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it('应该抛出错误当路径和行数数组长度不匹配时', async () => {
|
|
175
|
-
const args = {
|
|
176
|
-
path: [testFile1, testFile2],
|
|
177
|
-
content: ['Content 1', 'Content 2'],
|
|
178
|
-
line_count: [1] // 只有一个行数,但有两个路径
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
await assert.rejects(
|
|
182
|
-
() => WriteFileHandler.handle(args),
|
|
183
|
-
(error) => error.message.includes("doesn't match line_count array length")
|
|
184
|
-
);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
describe('错误处理', () => {
|
|
190
|
-
|
|
191
|
-
it('应该处理写入失败的情况', async () => {
|
|
192
|
-
// 使用无效路径
|
|
193
|
-
const invalidPath = '/invalid/path/that/does/not/exist/file.txt';
|
|
194
|
-
const args = {
|
|
195
|
-
path: invalidPath,
|
|
196
|
-
content: 'Some content',
|
|
197
|
-
line_count: 1
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
await assert.rejects(
|
|
201
|
-
() => WriteFileHandler.handle(args),
|
|
202
|
-
(error) => error.message.includes('Failed to write file(s)')
|
|
203
|
-
);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it('应该处理部分文件写入失败的情况', async () => {
|
|
207
|
-
const validPath = testFile1;
|
|
208
|
-
const invalidPath = '/invalid/path/file.txt';
|
|
209
|
-
const args = {
|
|
210
|
-
path: [validPath, invalidPath],
|
|
211
|
-
content: ['Valid content', 'Invalid content'],
|
|
212
|
-
line_count: [1, 1]
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
await assert.rejects(
|
|
216
|
-
() => WriteFileHandler.handle(args),
|
|
217
|
-
(error) => error.message.includes('Failed to write') &&
|
|
218
|
-
error.message.includes('file(s)')
|
|
219
|
-
);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
describe('边界情况', () => {
|
|
225
|
-
|
|
226
|
-
it('应该处理包含特殊字符的内容', async () => {
|
|
227
|
-
const content = 'Special chars: áéíóú 中文 @#$%^&*()';
|
|
228
|
-
const args = {
|
|
229
|
-
path: testFile1,
|
|
230
|
-
content: content,
|
|
231
|
-
line_count: 1
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
const result = await WriteFileHandler.handle(args);
|
|
235
|
-
|
|
236
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
237
|
-
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
238
|
-
|
|
239
|
-
// 验证特殊字符被正确写入
|
|
240
|
-
const writtenContent = await fs.readFile(testFile1, 'utf8');
|
|
241
|
-
assert.strictEqual(writtenContent, content);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it('应该处理大文件写入', async () => {
|
|
245
|
-
const bigContent = Array.from({ length: 1000 }, (_, i) => `Line ${i + 1}`).join('\n');
|
|
246
|
-
const args = {
|
|
247
|
-
path: testFile1,
|
|
248
|
-
content: bigContent,
|
|
249
|
-
line_count: 1000
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
const result = await WriteFileHandler.handle(args);
|
|
253
|
-
|
|
254
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
255
|
-
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
256
|
-
assert.ok(result.content[0].text.includes('1000 lines'));
|
|
257
|
-
|
|
258
|
-
// 验证大文件内容
|
|
259
|
-
const writtenContent = await fs.readFile(testFile1, 'utf8');
|
|
260
|
-
assert.strictEqual(writtenContent, bigContent);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
it('应该创建不存在的目录', async () => {
|
|
264
|
-
const deepPath = join(testDir, 'deep', 'nested', 'file.txt');
|
|
265
|
-
const args = {
|
|
266
|
-
path: deepPath,
|
|
267
|
-
content: 'Deep file content',
|
|
268
|
-
line_count: 1
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
const result = await WriteFileHandler.handle(args);
|
|
272
|
-
|
|
273
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
274
|
-
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
275
|
-
|
|
276
|
-
// 验证文件和目录被创建
|
|
277
|
-
assert.ok(existsSync(deepPath));
|
|
278
|
-
const content = await fs.readFile(deepPath, 'utf8');
|
|
279
|
-
assert.strictEqual(content, 'Deep file content');
|
|
280
|
-
|
|
281
|
-
// 清理深层目录
|
|
282
|
-
await fs.unlink(deepPath);
|
|
283
|
-
await fs.rmdir(join(testDir, 'deep', 'nested'));
|
|
284
|
-
await fs.rmdir(join(testDir, 'deep'));
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
});
|
|
1
|
+
import { WriteFileHandler } from '../src/handlers/writeFile.js';
|
|
2
|
+
import { FileUtils } from '../src/utils/fileUtils.js';
|
|
3
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
4
|
+
import assert from 'node:assert';
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { existsSync } from 'fs';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* WriteFileHandler 测试类
|
|
11
|
+
* 测试文件写入功能
|
|
12
|
+
*/
|
|
13
|
+
describe('WriteFileHandler', () => {
|
|
14
|
+
|
|
15
|
+
const testDir = './test_files_write';
|
|
16
|
+
const testFile1 = join(testDir, 'test1.txt');
|
|
17
|
+
const testFile2 = join(testDir, 'test2.txt');
|
|
18
|
+
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
// 设置测试工作区
|
|
21
|
+
FileUtils.setWorkspaceRoot(process.cwd());
|
|
22
|
+
|
|
23
|
+
// 创建测试目录
|
|
24
|
+
if (!existsSync(testDir)) {
|
|
25
|
+
await fs.mkdir(testDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(async () => {
|
|
30
|
+
// 清理测试文件
|
|
31
|
+
try {
|
|
32
|
+
if (existsSync(testFile1)) await fs.unlink(testFile1);
|
|
33
|
+
if (existsSync(testFile2)) await fs.unlink(testFile2);
|
|
34
|
+
if (existsSync(testDir)) await fs.rmdir(testDir);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.warn('清理测试文件失败:', error.message);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('单文件写入', () => {
|
|
41
|
+
|
|
42
|
+
it('应该成功创建新文件', async () => {
|
|
43
|
+
const content = 'Line 1\nLine 2\nLine 3';
|
|
44
|
+
const args = {
|
|
45
|
+
path: testFile1,
|
|
46
|
+
content: content,
|
|
47
|
+
line_count: 3
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const result = await WriteFileHandler.handle(args);
|
|
51
|
+
|
|
52
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
53
|
+
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
54
|
+
assert.ok(result.content[0].text.includes('3 lines'));
|
|
55
|
+
|
|
56
|
+
// 验证文件内容
|
|
57
|
+
const writtenContent = await fs.readFile(testFile1, 'utf8');
|
|
58
|
+
assert.strictEqual(writtenContent, content);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('应该覆盖现有文件', async () => {
|
|
62
|
+
// 先创建一个文件
|
|
63
|
+
await fs.writeFile(testFile1, 'Old content', 'utf8');
|
|
64
|
+
|
|
65
|
+
const newContent = 'New content\nSecond line';
|
|
66
|
+
const args = {
|
|
67
|
+
path: testFile1,
|
|
68
|
+
content: newContent,
|
|
69
|
+
line_count: 2
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const result = await WriteFileHandler.handle(args);
|
|
73
|
+
|
|
74
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
75
|
+
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
76
|
+
|
|
77
|
+
// 验证文件内容被覆盖
|
|
78
|
+
const writtenContent = await fs.readFile(testFile1, 'utf8');
|
|
79
|
+
assert.strictEqual(writtenContent, newContent);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('应该处理空文件写入', async () => {
|
|
83
|
+
const args = {
|
|
84
|
+
path: testFile1,
|
|
85
|
+
content: '',
|
|
86
|
+
line_count: 0
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const result = await WriteFileHandler.handle(args);
|
|
90
|
+
|
|
91
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
92
|
+
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
93
|
+
|
|
94
|
+
// 验证文件为空
|
|
95
|
+
const writtenContent = await fs.readFile(testFile1, 'utf8');
|
|
96
|
+
assert.strictEqual(writtenContent, '');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('应该发出警告当行数不匹配时', async () => {
|
|
100
|
+
const content = 'Line 1\nLine 2\nLine 3';
|
|
101
|
+
const args = {
|
|
102
|
+
path: testFile1,
|
|
103
|
+
content: content,
|
|
104
|
+
line_count: 5 // 错误的行数
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const result = await WriteFileHandler.handle(args);
|
|
108
|
+
|
|
109
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
110
|
+
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
111
|
+
// 注意:警告是通过console.warn输出的,这里检查结果仍然成功
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('多文件写入', () => {
|
|
117
|
+
|
|
118
|
+
it('应该成功写入多个文件(相同内容)', async () => {
|
|
119
|
+
const content = 'Shared content\nLine 2';
|
|
120
|
+
const args = {
|
|
121
|
+
path: [testFile1, testFile2],
|
|
122
|
+
content: content,
|
|
123
|
+
line_count: 2
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const result = await WriteFileHandler.handle(args);
|
|
127
|
+
|
|
128
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
129
|
+
assert.ok(result.content[0].text.includes('Successfully wrote 2 files'));
|
|
130
|
+
assert.ok(result.content[0].text.includes('4 total lines'));
|
|
131
|
+
|
|
132
|
+
// 验证两个文件内容
|
|
133
|
+
const content1 = await fs.readFile(testFile1, 'utf8');
|
|
134
|
+
const content2 = await fs.readFile(testFile2, 'utf8');
|
|
135
|
+
assert.strictEqual(content1, content);
|
|
136
|
+
assert.strictEqual(content2, content);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('应该成功写入多个文件(不同内容)', async () => {
|
|
140
|
+
const content1 = 'File 1 content';
|
|
141
|
+
const content2 = 'File 2 content\nSecond line';
|
|
142
|
+
const args = {
|
|
143
|
+
path: [testFile1, testFile2],
|
|
144
|
+
content: [content1, content2],
|
|
145
|
+
line_count: [1, 2]
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const result = await WriteFileHandler.handle(args);
|
|
149
|
+
|
|
150
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
151
|
+
assert.ok(result.content[0].text.includes('Successfully wrote 2 files'));
|
|
152
|
+
assert.ok(result.content[0].text.includes('3 total lines'));
|
|
153
|
+
|
|
154
|
+
// 验证文件内容
|
|
155
|
+
const writtenContent1 = await fs.readFile(testFile1, 'utf8');
|
|
156
|
+
const writtenContent2 = await fs.readFile(testFile2, 'utf8');
|
|
157
|
+
assert.strictEqual(writtenContent1, content1);
|
|
158
|
+
assert.strictEqual(writtenContent2, content2);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('应该抛出错误当路径和内容数组长度不匹配时', async () => {
|
|
162
|
+
const args = {
|
|
163
|
+
path: [testFile1, testFile2],
|
|
164
|
+
content: ['Content 1'], // 只有一个内容,但有两个路径
|
|
165
|
+
line_count: [1, 1]
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
await assert.rejects(
|
|
169
|
+
() => WriteFileHandler.handle(args),
|
|
170
|
+
(error) => error.message.includes("doesn't match content count")
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('应该抛出错误当路径和行数数组长度不匹配时', async () => {
|
|
175
|
+
const args = {
|
|
176
|
+
path: [testFile1, testFile2],
|
|
177
|
+
content: ['Content 1', 'Content 2'],
|
|
178
|
+
line_count: [1] // 只有一个行数,但有两个路径
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
await assert.rejects(
|
|
182
|
+
() => WriteFileHandler.handle(args),
|
|
183
|
+
(error) => error.message.includes("doesn't match line_count array length")
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('错误处理', () => {
|
|
190
|
+
|
|
191
|
+
it('应该处理写入失败的情况', async () => {
|
|
192
|
+
// 使用无效路径
|
|
193
|
+
const invalidPath = '/invalid/path/that/does/not/exist/file.txt';
|
|
194
|
+
const args = {
|
|
195
|
+
path: invalidPath,
|
|
196
|
+
content: 'Some content',
|
|
197
|
+
line_count: 1
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
await assert.rejects(
|
|
201
|
+
() => WriteFileHandler.handle(args),
|
|
202
|
+
(error) => error.message.includes('Failed to write file(s)')
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('应该处理部分文件写入失败的情况', async () => {
|
|
207
|
+
const validPath = testFile1;
|
|
208
|
+
const invalidPath = '/invalid/path/file.txt';
|
|
209
|
+
const args = {
|
|
210
|
+
path: [validPath, invalidPath],
|
|
211
|
+
content: ['Valid content', 'Invalid content'],
|
|
212
|
+
line_count: [1, 1]
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
await assert.rejects(
|
|
216
|
+
() => WriteFileHandler.handle(args),
|
|
217
|
+
(error) => error.message.includes('Failed to write') &&
|
|
218
|
+
error.message.includes('file(s)')
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('边界情况', () => {
|
|
225
|
+
|
|
226
|
+
it('应该处理包含特殊字符的内容', async () => {
|
|
227
|
+
const content = 'Special chars: áéíóú 中文 @#$%^&*()';
|
|
228
|
+
const args = {
|
|
229
|
+
path: testFile1,
|
|
230
|
+
content: content,
|
|
231
|
+
line_count: 1
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const result = await WriteFileHandler.handle(args);
|
|
235
|
+
|
|
236
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
237
|
+
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
238
|
+
|
|
239
|
+
// 验证特殊字符被正确写入
|
|
240
|
+
const writtenContent = await fs.readFile(testFile1, 'utf8');
|
|
241
|
+
assert.strictEqual(writtenContent, content);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('应该处理大文件写入', async () => {
|
|
245
|
+
const bigContent = Array.from({ length: 1000 }, (_, i) => `Line ${i + 1}`).join('\n');
|
|
246
|
+
const args = {
|
|
247
|
+
path: testFile1,
|
|
248
|
+
content: bigContent,
|
|
249
|
+
line_count: 1000
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const result = await WriteFileHandler.handle(args);
|
|
253
|
+
|
|
254
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
255
|
+
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
256
|
+
assert.ok(result.content[0].text.includes('1000 lines'));
|
|
257
|
+
|
|
258
|
+
// 验证大文件内容
|
|
259
|
+
const writtenContent = await fs.readFile(testFile1, 'utf8');
|
|
260
|
+
assert.strictEqual(writtenContent, bigContent);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('应该创建不存在的目录', async () => {
|
|
264
|
+
const deepPath = join(testDir, 'deep', 'nested', 'file.txt');
|
|
265
|
+
const args = {
|
|
266
|
+
path: deepPath,
|
|
267
|
+
content: 'Deep file content',
|
|
268
|
+
line_count: 1
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const result = await WriteFileHandler.handle(args);
|
|
272
|
+
|
|
273
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
274
|
+
assert.ok(result.content[0].text.includes('File written successfully'));
|
|
275
|
+
|
|
276
|
+
// 验证文件和目录被创建
|
|
277
|
+
assert.ok(existsSync(deepPath));
|
|
278
|
+
const content = await fs.readFile(deepPath, 'utf8');
|
|
279
|
+
assert.strictEqual(content, 'Deep file content');
|
|
280
|
+
|
|
281
|
+
// 清理深层目录
|
|
282
|
+
await fs.unlink(deepPath);
|
|
283
|
+
await fs.rmdir(join(testDir, 'deep', 'nested'));
|
|
284
|
+
await fs.rmdir(join(testDir, 'deep'));
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
});
|