fileditor-mcp 1.0.0
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/LICENSE +21 -0
- package/README.md +128 -0
- package/docs/MCP-INTERFACE.cn.md +976 -0
- package/docs/MCP-INTERFACE.en.md +881 -0
- package/docs/README.cn.md +128 -0
- package/package.json +27 -0
- package/src/handlers/applyDiff.js +298 -0
- package/src/handlers/insertContent.js +179 -0
- package/src/handlers/listFiles.js +65 -0
- package/src/handlers/readFile.js +109 -0
- package/src/handlers/searchAndReplace.js +198 -0
- package/src/handlers/setWorkspace.js +26 -0
- package/src/handlers/writeFile.js +98 -0
- package/src/index.js +20 -0
- package/src/server.js +85 -0
- package/src/tools/toolDefinitions.js +291 -0
- package/src/utils/fileUtils.js +238 -0
- package/test/ApplyDiffHandler.test.js +754 -0
- package/test/InsertContentHandler.test.js +371 -0
- package/test/ListFilesHandler.test.js +302 -0
- package/test/ReadFileHandler.test.js +213 -0
- package/test/SearchAndReplaceHandler.test.js +505 -0
- package/test/SetWorkspaceHandler.test.js +290 -0
- package/test/WriteFileHandler.test.js +289 -0
- package/test/runAllTests.js +233 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Main test runner
|
|
5
|
+
* Run all tool handler tests
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { readdir } from 'fs/promises';
|
|
11
|
+
|
|
12
|
+
class TestRunner {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.testDir = './test';
|
|
15
|
+
this.passedTests = 0;
|
|
16
|
+
this.failedTests = 0;
|
|
17
|
+
this.totalTests = 0;
|
|
18
|
+
this.results = [];
|
|
19
|
+
} /**
|
|
20
|
+
* Run all test files
|
|
21
|
+
*/
|
|
22
|
+
async runAllTests() {
|
|
23
|
+
console.log('๐ Starting to run all tests...\n'); try {
|
|
24
|
+
// Get all test files
|
|
25
|
+
const testFiles = await this.getTestFiles();
|
|
26
|
+
|
|
27
|
+
if (testFiles.length === 0) {
|
|
28
|
+
console.log('โ No test files found');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
console.log(`๐ Found ${testFiles.length} test files:\n`); testFiles.forEach(file => console.log(` - ${file}`));
|
|
33
|
+
console.log('');
|
|
34
|
+
|
|
35
|
+
// Run each test file
|
|
36
|
+
for (const testFile of testFiles) {
|
|
37
|
+
await this.runSingleTest(testFile);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Print summary
|
|
41
|
+
this.printSummary();
|
|
42
|
+
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('โ Error occurred while running tests:', error.message);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
} /**
|
|
48
|
+
* Get all test files
|
|
49
|
+
*/
|
|
50
|
+
async getTestFiles() {
|
|
51
|
+
try {
|
|
52
|
+
const files = await readdir(this.testDir);
|
|
53
|
+
return files
|
|
54
|
+
.filter(file => file.endsWith('.test.js'))
|
|
55
|
+
.sort();
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error('โ Unable to read test directory:', error.message);
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
} /**
|
|
61
|
+
* Run single test file
|
|
62
|
+
*/
|
|
63
|
+
async runSingleTest(testFile) {
|
|
64
|
+
const testPath = join(this.testDir, testFile);
|
|
65
|
+
const testName = testFile.replace('.test.js', '');
|
|
66
|
+
|
|
67
|
+
console.log(`๐งช Running test: ${testName}`);
|
|
68
|
+
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
const startTime = Date.now();
|
|
71
|
+
|
|
72
|
+
const child = spawn('node', ['--test', testPath], {
|
|
73
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
74
|
+
shell: process.platform === 'win32'
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
let stdout = '';
|
|
78
|
+
let stderr = '';
|
|
79
|
+
|
|
80
|
+
child.stdout.on('data', (data) => {
|
|
81
|
+
stdout += data.toString();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
child.stderr.on('data', (data) => {
|
|
85
|
+
stderr += data.toString();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
child.on('close', (code) => {
|
|
89
|
+
const duration = Date.now() - startTime;
|
|
90
|
+
const result = {
|
|
91
|
+
name: testName,
|
|
92
|
+
file: testFile,
|
|
93
|
+
success: code === 0,
|
|
94
|
+
duration,
|
|
95
|
+
stdout,
|
|
96
|
+
stderr
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
this.processTestResult(result);
|
|
100
|
+
resolve();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
child.on('error', (error) => {
|
|
104
|
+
const duration = Date.now() - startTime;
|
|
105
|
+
const result = {
|
|
106
|
+
name: testName,
|
|
107
|
+
file: testFile,
|
|
108
|
+
success: false,
|
|
109
|
+
duration,
|
|
110
|
+
stdout: '',
|
|
111
|
+
stderr: error.message
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
this.processTestResult(result);
|
|
115
|
+
resolve();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
} /**
|
|
119
|
+
* Process test result
|
|
120
|
+
*/
|
|
121
|
+
processTestResult(result) {
|
|
122
|
+
this.results.push(result); if (result.success) {
|
|
123
|
+
console.log(` โ
${result.name} - Passed (${result.duration}ms)`);
|
|
124
|
+
this.passedTests++;
|
|
125
|
+
} else {
|
|
126
|
+
console.log(` โ ${result.name} - Failed (${result.duration}ms)`);
|
|
127
|
+
this.failedTests++;
|
|
128
|
+
|
|
129
|
+
// Show error information
|
|
130
|
+
if (result.stderr) {
|
|
131
|
+
console.log(` Error: ${result.stderr.split('\n')[0]}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.totalTests++;
|
|
136
|
+
console.log('');
|
|
137
|
+
} /**
|
|
138
|
+
* Print test summary
|
|
139
|
+
*/
|
|
140
|
+
printSummary() {
|
|
141
|
+
console.log('๐ Test Summary');
|
|
142
|
+
console.log('='.repeat(50));
|
|
143
|
+
console.log(`Total tests: ${this.totalTests}`);
|
|
144
|
+
console.log(`Passed: ${this.passedTests}`);
|
|
145
|
+
console.log(`Failed: ${this.failedTests}`);
|
|
146
|
+
console.log(`Success rate: ${this.totalTests > 0 ? (this.passedTests / this.totalTests * 100).toFixed(2) : 0}%`);
|
|
147
|
+
|
|
148
|
+
if (this.failedTests > 0) {
|
|
149
|
+
console.log('\nโ Failed tests:');
|
|
150
|
+
this.results
|
|
151
|
+
.filter(r => !r.success)
|
|
152
|
+
.forEach(result => {
|
|
153
|
+
console.log(` - ${result.name}`);
|
|
154
|
+
if (result.stderr) {
|
|
155
|
+
console.log(` ${result.stderr.split('\n')[0]}`);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.log('\nโฑ๏ธ Performance statistics:');
|
|
161
|
+
const totalDuration = this.results.reduce((sum, r) => sum + r.duration, 0);
|
|
162
|
+
console.log(`Total time: ${totalDuration}ms`);
|
|
163
|
+
console.log(`Average time: ${this.totalTests > 0 ? (totalDuration / this.totalTests).toFixed(2) : 0}ms`); // Sort by duration to show slowest tests
|
|
164
|
+
const slowestTests = [...this.results]
|
|
165
|
+
.sort((a, b) => b.duration - a.duration)
|
|
166
|
+
.slice(0, 3);
|
|
167
|
+
|
|
168
|
+
if (slowestTests.length > 0) {
|
|
169
|
+
console.log('\n๐ Slowest tests:');
|
|
170
|
+
slowestTests.forEach((result, index) => {
|
|
171
|
+
console.log(` ${index + 1}. ${result.name} - ${result.duration}ms`);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
console.log('='.repeat(50));
|
|
176
|
+
|
|
177
|
+
if (this.failedTests === 0) {
|
|
178
|
+
console.log('๐ All tests passed!');
|
|
179
|
+
} else {
|
|
180
|
+
console.log(`โ ๏ธ ${this.failedTests} test(s) failed`);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
} /**
|
|
184
|
+
* Run specific test file
|
|
185
|
+
*/
|
|
186
|
+
async runSpecificTest(testName) {
|
|
187
|
+
const testFile = testName.endsWith('.test.js') ? testName : `${testName}.test.js`;
|
|
188
|
+
const testFiles = await this.getTestFiles(); if (!testFiles.includes(testFile)) {
|
|
189
|
+
console.log(`โ Test file not found: ${testFile}`);
|
|
190
|
+
console.log('Available test files:');
|
|
191
|
+
testFiles.forEach(file => console.log(` - ${file}`));
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.log(`๐งช Running specific test: ${testFile}\n`);
|
|
196
|
+
await this.runSingleTest(testFile);
|
|
197
|
+
this.printSummary();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Main function
|
|
202
|
+
async function main() {
|
|
203
|
+
const runner = new TestRunner();
|
|
204
|
+
|
|
205
|
+
// Check command line arguments
|
|
206
|
+
const args = process.argv.slice(2);
|
|
207
|
+
|
|
208
|
+
if (args.length > 0) {
|
|
209
|
+
// Run specific test
|
|
210
|
+
const testName = args[0];
|
|
211
|
+
await runner.runSpecificTest(testName);
|
|
212
|
+
} else {
|
|
213
|
+
// Run all tests
|
|
214
|
+
await runner.runAllTests();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Handle uncaught exceptions
|
|
219
|
+
process.on('uncaughtException', (error) => {
|
|
220
|
+
console.error('โ Uncaught exception:', error.message);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
225
|
+
console.error('โ Unhandled promise rejection:', reason);
|
|
226
|
+
process.exit(1);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Run main function
|
|
230
|
+
main().catch(error => {
|
|
231
|
+
console.error('โ Failed to run tests:', error.message);
|
|
232
|
+
process.exit(1);
|
|
233
|
+
});
|