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,369 +1,371 @@
|
|
|
1
|
-
import { InsertContentHandler } from '../src/handlers/insertContent.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
|
-
* InsertContentHandler Test Class
|
|
11
|
-
* Test content insertion functionality
|
|
12
|
-
*/
|
|
13
|
-
describe('InsertContentHandler', () => {
|
|
14
|
-
|
|
15
|
-
const testDir = './test_files_insert';
|
|
16
|
-
const testFile1 = join(testDir, 'test1.txt');
|
|
17
|
-
const testFile2 = join(testDir, 'test2.txt');
|
|
18
|
-
const baseContent = 'Line 1\nLine 2\nLine 3\nLine 4\nLine 5';
|
|
19
|
-
|
|
20
|
-
beforeEach(async () => {
|
|
21
|
-
// Set test workspace
|
|
22
|
-
FileUtils.setWorkspaceRoot(process.cwd());
|
|
23
|
-
|
|
24
|
-
// Create test directory and files
|
|
25
|
-
if (!existsSync(testDir)) {
|
|
26
|
-
await fs.mkdir(testDir, { recursive: true });
|
|
27
|
-
}
|
|
28
|
-
await fs.writeFile(testFile1, baseContent, 'utf8');
|
|
29
|
-
await fs.writeFile(testFile2, baseContent, 'utf8');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
afterEach(async () => {
|
|
33
|
-
// Clean up test files
|
|
34
|
-
try {
|
|
35
|
-
if (existsSync(testFile1)) await fs.unlink(testFile1);
|
|
36
|
-
if (existsSync(testFile2)) await fs.unlink(testFile2);
|
|
37
|
-
if (existsSync(testDir)) await fs.rmdir(testDir);
|
|
38
|
-
} catch (error) {
|
|
39
|
-
console.warn('Failed to clean up test files:', error.message);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('Single File Insertion', () => {
|
|
44
|
-
|
|
45
|
-
it('should insert content before the specified line number', async () => {
|
|
46
|
-
const args = {
|
|
47
|
-
path: testFile1,
|
|
48
|
-
line: 3,
|
|
49
|
-
content: 'Inserted line'
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const result = await InsertContentHandler.handle(args);
|
|
53
|
-
|
|
54
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
55
|
-
assert.ok(result.content[0].text.includes('Successfully inserted'));
|
|
56
|
-
assert.ok(result.content[0].text.includes('line 3'));
|
|
57
|
-
|
|
58
|
-
// Verify file content
|
|
59
|
-
const content = await fs.readFile(testFile1, 'utf8');
|
|
60
|
-
const lines = content.split('\n');
|
|
61
|
-
assert.strictEqual(lines[2], 'Inserted line');
|
|
62
|
-
assert.strictEqual(lines[3], 'Line 3');
|
|
63
|
-
assert.strictEqual(lines.length, 6); // Originally 5 lines + 1 inserted line
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should insert content at the beginning of the file', async () => {
|
|
67
|
-
const args = {
|
|
68
|
-
path: testFile1,
|
|
69
|
-
line: 1,
|
|
70
|
-
content: 'First line'
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const result = await InsertContentHandler.handle(args);
|
|
74
|
-
|
|
75
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
76
|
-
assert.ok(result.content[0].text.includes('Successfully inserted'));
|
|
77
|
-
|
|
78
|
-
// Verify file content
|
|
79
|
-
const content = await fs.readFile(testFile1, 'utf8');
|
|
80
|
-
const lines = content.split('\n');
|
|
81
|
-
assert.strictEqual(lines[0], 'First line');
|
|
82
|
-
assert.strictEqual(lines[1], 'Line 1');
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should insert content at the end of the file (line=0)', async () => {
|
|
86
|
-
const args = {
|
|
87
|
-
path: testFile1,
|
|
88
|
-
line: 0,
|
|
89
|
-
content: 'Last line'
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const result = await InsertContentHandler.handle(args);
|
|
93
|
-
|
|
94
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
95
|
-
assert.ok(result.content[0].text.includes('end of file'));
|
|
96
|
-
|
|
97
|
-
// Verify file content
|
|
98
|
-
const content = await fs.readFile(testFile1, 'utf8');
|
|
99
|
-
const lines = content.split('\n');
|
|
100
|
-
assert.strictEqual(lines[lines.length - 1], 'Last line');
|
|
101
|
-
assert.strictEqual(lines.length, 6);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('should support negative line numbers for insertion', async () => {
|
|
105
|
-
const args = {
|
|
106
|
-
path: testFile1,
|
|
107
|
-
line: -1, // Insert before the last line
|
|
108
|
-
content: 'Before last line'
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const result = await InsertContentHandler.handle(args);
|
|
112
|
-
|
|
113
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
114
|
-
assert.ok(result.content[0].text.includes('from end'));
|
|
115
|
-
|
|
116
|
-
// Verify file content
|
|
117
|
-
const content = await fs.readFile(testFile1, 'utf8');
|
|
118
|
-
const lines = content.split('\n');
|
|
119
|
-
assert.strictEqual(lines[4], 'Before last line');
|
|
120
|
-
assert.strictEqual(lines[5], 'Line 5');
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('should support inserting multiple lines of content', async () => {
|
|
124
|
-
const multiLineContent = 'Line A\nLine B\nLine C';
|
|
125
|
-
const args = {
|
|
126
|
-
path: testFile1,
|
|
127
|
-
line: 2,
|
|
128
|
-
content: multiLineContent
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const result = await InsertContentHandler.handle(args);
|
|
132
|
-
|
|
133
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
134
|
-
assert.ok(result.content[0].text.includes('3 line(s)'));
|
|
135
|
-
|
|
136
|
-
// Verify file content
|
|
137
|
-
const content = await fs.readFile(testFile1, 'utf8');
|
|
138
|
-
const lines = content.split('\n');
|
|
139
|
-
assert.strictEqual(lines[1], 'Line A');
|
|
140
|
-
assert.strictEqual(lines[2], 'Line B');
|
|
141
|
-
assert.strictEqual(lines[3], 'Line C');
|
|
142
|
-
assert.strictEqual(lines[4], 'Line 2');
|
|
143
|
-
assert.strictEqual(lines.length, 8); // Originally 5 lines + 3 inserted lines
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should throw an error if the file does not exist', async () => {
|
|
147
|
-
const args = {
|
|
148
|
-
path: './nonexistent.txt',
|
|
149
|
-
line: 1,
|
|
150
|
-
content: 'Some content'
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
await assert.rejects(
|
|
154
|
-
() => InsertContentHandler.handle(args),
|
|
155
|
-
(error) => error.message.includes('File not found')
|
|
156
|
-
);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('should throw an error if the line number is out of range', async () => {
|
|
160
|
-
const args = {
|
|
161
|
-
path: testFile1,
|
|
162
|
-
line: 10, // Exceeds file length
|
|
163
|
-
content: 'Some content'
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
await assert.rejects(
|
|
167
|
-
() => InsertContentHandler.handle(args),
|
|
168
|
-
(error) => error.message.includes('exceeds file length')
|
|
169
|
-
);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('should throw an error if the negative line number is out of range', async () => {
|
|
173
|
-
const args = {
|
|
174
|
-
path: testFile1,
|
|
175
|
-
line: -10, // Negative number too large
|
|
176
|
-
content: 'Some content'
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
await assert.rejects(
|
|
180
|
-
() => InsertContentHandler.handle(args),
|
|
181
|
-
(error) => error.message.includes('out of range')
|
|
182
|
-
);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
describe('Multiple Files Insertion', () => {
|
|
188
|
-
|
|
189
|
-
it('should insert the same content at the same position in multiple files', async () => {
|
|
190
|
-
const args = {
|
|
191
|
-
path: [testFile1, testFile2],
|
|
192
|
-
line: 2,
|
|
193
|
-
content: 'Common insert'
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
const result = await InsertContentHandler.handle(args);
|
|
197
|
-
|
|
198
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
199
|
-
assert.ok(result.content[0].text.includes('Successfully processed 2 file(s)'));
|
|
200
|
-
|
|
201
|
-
// Verify content in both files
|
|
202
|
-
const content1 = await fs.readFile(testFile1, 'utf8');
|
|
203
|
-
const content2 = await fs.readFile(testFile2, 'utf8');
|
|
204
|
-
|
|
205
|
-
const lines1 = content1.split('\n');
|
|
206
|
-
const lines2 = content2.split('\n');
|
|
207
|
-
|
|
208
|
-
assert.strictEqual(lines1[1], 'Common insert');
|
|
209
|
-
assert.strictEqual(lines2[1], 'Common insert');
|
|
210
|
-
assert.strictEqual(lines1[2], 'Line 2');
|
|
211
|
-
assert.strictEqual(lines2[2], 'Line 2');
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it('should insert different content at different positions in multiple files', async () => {
|
|
215
|
-
const args = {
|
|
216
|
-
path: [testFile1, testFile2],
|
|
217
|
-
line: [1, 3],
|
|
218
|
-
content: ['Insert at start', 'Insert at middle']
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
const result = await InsertContentHandler.handle(args);
|
|
222
|
-
|
|
223
|
-
assert.strictEqual(result.content[0].type, 'text');
|
|
224
|
-
assert.ok(result.content[0].text.includes('Successfully processed 2 file(s)'));
|
|
225
|
-
|
|
226
|
-
// Verify file content
|
|
227
|
-
const content1 = await fs.readFile(testFile1, 'utf8');
|
|
228
|
-
const content2 = await fs.readFile(testFile2, 'utf8');
|
|
229
|
-
|
|
230
|
-
const lines1 = content1.split('\n');
|
|
231
|
-
const lines2 = content2.split('\n');
|
|
232
|
-
|
|
233
|
-
assert.strictEqual(lines1[0], 'Insert at start');
|
|
234
|
-
assert.strictEqual(lines1[1], 'Line 1');
|
|
235
|
-
|
|
236
|
-
assert.strictEqual(lines2[2], 'Insert at middle');
|
|
237
|
-
assert.strictEqual(lines2[3], 'Line 3');
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it('should throw an error if the line number array lengths do not match', async () => {
|
|
241
|
-
const args = {
|
|
242
|
-
path: [testFile1, testFile2],
|
|
243
|
-
line: [1], // Only one line number, but two files
|
|
244
|
-
content: ['Content 1', 'Content 2']
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
await assert.rejects(
|
|
248
|
-
() => InsertContentHandler.handle(args),
|
|
249
|
-
(error) => error.message.includes('must match file count')
|
|
250
|
-
);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
assert.ok(text.includes('
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
assert.strictEqual(lines
|
|
364
|
-
assert.strictEqual(lines[
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
});
|
|
1
|
+
import { InsertContentHandler } from '../src/handlers/insertContent.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
|
+
* InsertContentHandler Test Class
|
|
11
|
+
* Test content insertion functionality
|
|
12
|
+
*/
|
|
13
|
+
describe('InsertContentHandler', () => {
|
|
14
|
+
|
|
15
|
+
const testDir = './test_files_insert';
|
|
16
|
+
const testFile1 = join(testDir, 'test1.txt');
|
|
17
|
+
const testFile2 = join(testDir, 'test2.txt');
|
|
18
|
+
const baseContent = 'Line 1\nLine 2\nLine 3\nLine 4\nLine 5';
|
|
19
|
+
|
|
20
|
+
beforeEach(async () => {
|
|
21
|
+
// Set test workspace
|
|
22
|
+
FileUtils.setWorkspaceRoot(process.cwd());
|
|
23
|
+
|
|
24
|
+
// Create test directory and files
|
|
25
|
+
if (!existsSync(testDir)) {
|
|
26
|
+
await fs.mkdir(testDir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
await fs.writeFile(testFile1, baseContent, 'utf8');
|
|
29
|
+
await fs.writeFile(testFile2, baseContent, 'utf8');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(async () => {
|
|
33
|
+
// Clean up test files
|
|
34
|
+
try {
|
|
35
|
+
if (existsSync(testFile1)) await fs.unlink(testFile1);
|
|
36
|
+
if (existsSync(testFile2)) await fs.unlink(testFile2);
|
|
37
|
+
if (existsSync(testDir)) await fs.rmdir(testDir);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.warn('Failed to clean up test files:', error.message);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('Single File Insertion', () => {
|
|
44
|
+
|
|
45
|
+
it('should insert content before the specified line number', async () => {
|
|
46
|
+
const args = {
|
|
47
|
+
path: testFile1,
|
|
48
|
+
line: 3,
|
|
49
|
+
content: 'Inserted line'
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const result = await InsertContentHandler.handle(args);
|
|
53
|
+
|
|
54
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
55
|
+
assert.ok(result.content[0].text.includes('Successfully inserted'));
|
|
56
|
+
assert.ok(result.content[0].text.includes('line 3'));
|
|
57
|
+
|
|
58
|
+
// Verify file content
|
|
59
|
+
const content = await fs.readFile(testFile1, 'utf8');
|
|
60
|
+
const lines = content.split('\n');
|
|
61
|
+
assert.strictEqual(lines[2], 'Inserted line');
|
|
62
|
+
assert.strictEqual(lines[3], 'Line 3');
|
|
63
|
+
assert.strictEqual(lines.length, 6); // Originally 5 lines + 1 inserted line
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should insert content at the beginning of the file', async () => {
|
|
67
|
+
const args = {
|
|
68
|
+
path: testFile1,
|
|
69
|
+
line: 1,
|
|
70
|
+
content: 'First line'
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const result = await InsertContentHandler.handle(args);
|
|
74
|
+
|
|
75
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
76
|
+
assert.ok(result.content[0].text.includes('Successfully inserted'));
|
|
77
|
+
|
|
78
|
+
// Verify file content
|
|
79
|
+
const content = await fs.readFile(testFile1, 'utf8');
|
|
80
|
+
const lines = content.split('\n');
|
|
81
|
+
assert.strictEqual(lines[0], 'First line');
|
|
82
|
+
assert.strictEqual(lines[1], 'Line 1');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should insert content at the end of the file (line=0)', async () => {
|
|
86
|
+
const args = {
|
|
87
|
+
path: testFile1,
|
|
88
|
+
line: 0,
|
|
89
|
+
content: 'Last line'
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const result = await InsertContentHandler.handle(args);
|
|
93
|
+
|
|
94
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
95
|
+
assert.ok(result.content[0].text.includes('end of file'));
|
|
96
|
+
|
|
97
|
+
// Verify file content
|
|
98
|
+
const content = await fs.readFile(testFile1, 'utf8');
|
|
99
|
+
const lines = content.split('\n');
|
|
100
|
+
assert.strictEqual(lines[lines.length - 1], 'Last line');
|
|
101
|
+
assert.strictEqual(lines.length, 6);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should support negative line numbers for insertion', async () => {
|
|
105
|
+
const args = {
|
|
106
|
+
path: testFile1,
|
|
107
|
+
line: -1, // Insert before the last line
|
|
108
|
+
content: 'Before last line'
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const result = await InsertContentHandler.handle(args);
|
|
112
|
+
|
|
113
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
114
|
+
assert.ok(result.content[0].text.includes('from end'));
|
|
115
|
+
|
|
116
|
+
// Verify file content
|
|
117
|
+
const content = await fs.readFile(testFile1, 'utf8');
|
|
118
|
+
const lines = content.split('\n');
|
|
119
|
+
assert.strictEqual(lines[4], 'Before last line');
|
|
120
|
+
assert.strictEqual(lines[5], 'Line 5');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should support inserting multiple lines of content', async () => {
|
|
124
|
+
const multiLineContent = 'Line A\nLine B\nLine C';
|
|
125
|
+
const args = {
|
|
126
|
+
path: testFile1,
|
|
127
|
+
line: 2,
|
|
128
|
+
content: multiLineContent
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const result = await InsertContentHandler.handle(args);
|
|
132
|
+
|
|
133
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
134
|
+
assert.ok(result.content[0].text.includes('3 line(s)'));
|
|
135
|
+
|
|
136
|
+
// Verify file content
|
|
137
|
+
const content = await fs.readFile(testFile1, 'utf8');
|
|
138
|
+
const lines = content.split('\n');
|
|
139
|
+
assert.strictEqual(lines[1], 'Line A');
|
|
140
|
+
assert.strictEqual(lines[2], 'Line B');
|
|
141
|
+
assert.strictEqual(lines[3], 'Line C');
|
|
142
|
+
assert.strictEqual(lines[4], 'Line 2');
|
|
143
|
+
assert.strictEqual(lines.length, 8); // Originally 5 lines + 3 inserted lines
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should throw an error if the file does not exist', async () => {
|
|
147
|
+
const args = {
|
|
148
|
+
path: './nonexistent.txt',
|
|
149
|
+
line: 1,
|
|
150
|
+
content: 'Some content'
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
await assert.rejects(
|
|
154
|
+
() => InsertContentHandler.handle(args),
|
|
155
|
+
(error) => error.message.includes('File not found')
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should throw an error if the line number is out of range', async () => {
|
|
160
|
+
const args = {
|
|
161
|
+
path: testFile1,
|
|
162
|
+
line: 10, // Exceeds file length
|
|
163
|
+
content: 'Some content'
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
await assert.rejects(
|
|
167
|
+
() => InsertContentHandler.handle(args),
|
|
168
|
+
(error) => error.message.includes('exceeds file length')
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should throw an error if the negative line number is out of range', async () => {
|
|
173
|
+
const args = {
|
|
174
|
+
path: testFile1,
|
|
175
|
+
line: -10, // Negative number too large
|
|
176
|
+
content: 'Some content'
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
await assert.rejects(
|
|
180
|
+
() => InsertContentHandler.handle(args),
|
|
181
|
+
(error) => error.message.includes('out of range')
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('Multiple Files Insertion', () => {
|
|
188
|
+
|
|
189
|
+
it('should insert the same content at the same position in multiple files', async () => {
|
|
190
|
+
const args = {
|
|
191
|
+
path: [testFile1, testFile2],
|
|
192
|
+
line: 2,
|
|
193
|
+
content: 'Common insert'
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const result = await InsertContentHandler.handle(args);
|
|
197
|
+
|
|
198
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
199
|
+
assert.ok(result.content[0].text.includes('Successfully processed 2 file(s)'));
|
|
200
|
+
|
|
201
|
+
// Verify content in both files
|
|
202
|
+
const content1 = await fs.readFile(testFile1, 'utf8');
|
|
203
|
+
const content2 = await fs.readFile(testFile2, 'utf8');
|
|
204
|
+
|
|
205
|
+
const lines1 = content1.split('\n');
|
|
206
|
+
const lines2 = content2.split('\n');
|
|
207
|
+
|
|
208
|
+
assert.strictEqual(lines1[1], 'Common insert');
|
|
209
|
+
assert.strictEqual(lines2[1], 'Common insert');
|
|
210
|
+
assert.strictEqual(lines1[2], 'Line 2');
|
|
211
|
+
assert.strictEqual(lines2[2], 'Line 2');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should insert different content at different positions in multiple files', async () => {
|
|
215
|
+
const args = {
|
|
216
|
+
path: [testFile1, testFile2],
|
|
217
|
+
line: [1, 3],
|
|
218
|
+
content: ['Insert at start', 'Insert at middle']
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const result = await InsertContentHandler.handle(args);
|
|
222
|
+
|
|
223
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
224
|
+
assert.ok(result.content[0].text.includes('Successfully processed 2 file(s)'));
|
|
225
|
+
|
|
226
|
+
// Verify file content
|
|
227
|
+
const content1 = await fs.readFile(testFile1, 'utf8');
|
|
228
|
+
const content2 = await fs.readFile(testFile2, 'utf8');
|
|
229
|
+
|
|
230
|
+
const lines1 = content1.split('\n');
|
|
231
|
+
const lines2 = content2.split('\n');
|
|
232
|
+
|
|
233
|
+
assert.strictEqual(lines1[0], 'Insert at start');
|
|
234
|
+
assert.strictEqual(lines1[1], 'Line 1');
|
|
235
|
+
|
|
236
|
+
assert.strictEqual(lines2[2], 'Insert at middle');
|
|
237
|
+
assert.strictEqual(lines2[3], 'Line 3');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should throw an error if the line number array lengths do not match', async () => {
|
|
241
|
+
const args = {
|
|
242
|
+
path: [testFile1, testFile2],
|
|
243
|
+
line: [1], // Only one line number, but two files
|
|
244
|
+
content: ['Content 1', 'Content 2']
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
await assert.rejects(
|
|
248
|
+
() => InsertContentHandler.handle(args),
|
|
249
|
+
(error) => error.message.includes('must match file count')
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should throw an error if the content array lengths do not match', async () => {
|
|
254
|
+
const args = {
|
|
255
|
+
path: [testFile1, testFile2],
|
|
256
|
+
line: [1, 2],
|
|
257
|
+
content: ['Only one content'] // Only one content, but two files
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
await assert.rejects(
|
|
261
|
+
() => InsertContentHandler.handle(args),
|
|
262
|
+
(error) => error.message.includes('must match file count')
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should handle cases where some file operations fail', async () => {
|
|
267
|
+
const nonexistentFile = './nonexistent.txt';
|
|
268
|
+
const args = {
|
|
269
|
+
path: [testFile1, nonexistentFile],
|
|
270
|
+
line: [1, 1],
|
|
271
|
+
content: ['Valid content', 'Invalid content']
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const result = await InsertContentHandler.handle(args);
|
|
275
|
+
|
|
276
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
277
|
+
const text = result.content[0].text;
|
|
278
|
+
|
|
279
|
+
assert.ok(text.includes('Successfully processed 1 file(s)'));
|
|
280
|
+
assert.ok(text.includes('Errors encountered'));
|
|
281
|
+
assert.ok(text.includes('File not found'));
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('Edge Cases', () => {
|
|
287
|
+
|
|
288
|
+
it('should handle empty file insertion', async () => {
|
|
289
|
+
const emptyFile = join(testDir, 'empty.txt');
|
|
290
|
+
await fs.writeFile(emptyFile, '', 'utf8');
|
|
291
|
+
|
|
292
|
+
const args = {
|
|
293
|
+
path: emptyFile,
|
|
294
|
+
line: 0,
|
|
295
|
+
content: 'First content in empty file'
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const result = await InsertContentHandler.handle(args);
|
|
299
|
+
|
|
300
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
301
|
+
assert.ok(result.content[0].text.includes('Successfully inserted'));
|
|
302
|
+
// Verify content
|
|
303
|
+
const content = await fs.readFile(emptyFile, 'utf8');
|
|
304
|
+
assert.strictEqual(content, '\nFirst content in empty file'); // Fix expected value
|
|
305
|
+
|
|
306
|
+
await fs.unlink(emptyFile);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('should handle content with special characters', async () => {
|
|
310
|
+
const specialContent = 'Special: áéíóú Chinese @#$%^&*()';
|
|
311
|
+
const args = {
|
|
312
|
+
path: testFile1,
|
|
313
|
+
line: 1,
|
|
314
|
+
content: specialContent
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const result = await InsertContentHandler.handle(args);
|
|
318
|
+
|
|
319
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
320
|
+
assert.ok(result.content[0].text.includes('Successfully inserted'));
|
|
321
|
+
|
|
322
|
+
// Verify special characters inserted correctly
|
|
323
|
+
const content = await fs.readFile(testFile1, 'utf8');
|
|
324
|
+
const lines = content.split('\n');
|
|
325
|
+
assert.strictEqual(lines[0], specialContent);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('should handle empty string insertion', async () => {
|
|
329
|
+
const args = {
|
|
330
|
+
path: testFile1,
|
|
331
|
+
line: 2,
|
|
332
|
+
content: ''
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const result = await InsertContentHandler.handle(args);
|
|
336
|
+
|
|
337
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
338
|
+
assert.ok(result.content[0].text.includes('Successfully inserted'));
|
|
339
|
+
|
|
340
|
+
// Verify empty line inserted
|
|
341
|
+
const content = await fs.readFile(testFile1, 'utf8');
|
|
342
|
+
const lines = content.split('\n');
|
|
343
|
+
assert.strictEqual(lines[1], '');
|
|
344
|
+
assert.strictEqual(lines[2], 'Line 2');
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should handle large content insertion', async () => {
|
|
348
|
+
const bigContent = Array.from({ length: 100 }, (_, i) => `Big line ${i + 1}`).join('\n');
|
|
349
|
+
const args = {
|
|
350
|
+
path: testFile1,
|
|
351
|
+
line: 3,
|
|
352
|
+
content: bigContent
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const result = await InsertContentHandler.handle(args);
|
|
356
|
+
|
|
357
|
+
assert.strictEqual(result.content[0].type, 'text');
|
|
358
|
+
assert.ok(result.content[0].text.includes('100 line(s)'));
|
|
359
|
+
|
|
360
|
+
// Verify file line count
|
|
361
|
+
const content = await fs.readFile(testFile1, 'utf8');
|
|
362
|
+
const lines = content.split('\n');
|
|
363
|
+
assert.strictEqual(lines.length, 105); // Originally 5 lines + 100 inserted lines
|
|
364
|
+
assert.strictEqual(lines[2], 'Big line 1');
|
|
365
|
+
assert.strictEqual(lines[101], 'Big line 100');
|
|
366
|
+
assert.strictEqual(lines[102], 'Line 3');
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
});
|