stigmergy 1.0.88 → 1.0.92
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/README.md +202 -189
- package/bin/stigmergy.cmd +5 -0
- package/docs/CONFLICT_PREVENTION.md +150 -0
- package/package.json +12 -6
- package/scripts/post-deployment-config.js +289 -0
- package/scripts/preinstall-check.js +111 -0
- package/scripts/safe-install.js +139 -0
- package/src/main.js +963 -892
- package/src/main_english.js +184 -36
- package/test/comprehensive-execution-test.js +428 -0
- package/test/conflict-prevention-test.js +95 -0
- package/test/deploy-hooks-test.js +250 -0
- package/test/error-handling-test.js +341 -0
- package/test/final-deploy-test.js +221 -0
- package/test/final-install-test.js +226 -0
- package/test/iflow-integration-test.js +292 -0
- package/test/improved-install-test.js +362 -0
- package/test/install-command-test.js +370 -0
- package/test/plugin-deployment-test.js +316 -0
- package/test/postinstall-test.js +269 -0
- package/test/simple-iflow-hook-test.js +137 -0
- package/test/tdd-deploy-fix-test.js +324 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* iFlow Stigmergy Integration Test
|
|
5
|
+
* This script tests if iFlow can properly detect and use Stigmergy hooks
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs').promises;
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const { spawn } = require('child_process');
|
|
12
|
+
|
|
13
|
+
class IFlowIntegrationTester {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.iflowConfigDir = path.join(os.homedir(), '.iflow');
|
|
16
|
+
this.iflowHooksDir = path.join(this.iflowConfigDir, 'hooks');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Check if iFlow config directory exists
|
|
20
|
+
async checkIFlowConfig() {
|
|
21
|
+
try {
|
|
22
|
+
await fs.access(this.iflowConfigDir);
|
|
23
|
+
return true;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check if iFlow hooks directory exists and has Stigmergy files
|
|
30
|
+
async checkIFlowHooks() {
|
|
31
|
+
try {
|
|
32
|
+
await fs.access(this.iflowHooksDir);
|
|
33
|
+
const files = await fs.readdir(this.iflowHooksDir);
|
|
34
|
+
const stigmergyFiles = files.filter(file =>
|
|
35
|
+
file.includes('stigmergy') ||
|
|
36
|
+
file.includes('Stigmergy') ||
|
|
37
|
+
file.includes('hook') ||
|
|
38
|
+
file.includes('adapter')
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
exists: true,
|
|
43
|
+
files: files,
|
|
44
|
+
stigmergyFiles: stigmergyFiles,
|
|
45
|
+
path: this.iflowHooksDir
|
|
46
|
+
};
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return {
|
|
49
|
+
exists: false,
|
|
50
|
+
files: [],
|
|
51
|
+
stigmergyFiles: [],
|
|
52
|
+
path: this.iflowHooksDir
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check if iFlow has hooks configuration
|
|
58
|
+
async checkIFlowHooksConfig() {
|
|
59
|
+
// Check multiple possible config locations
|
|
60
|
+
const configLocations = [
|
|
61
|
+
path.join(os.homedir(), '.config', 'iflow', 'hooks.yml'),
|
|
62
|
+
path.join(os.homedir(), '.config', 'iflow', 'hooks.yaml'),
|
|
63
|
+
path.join(os.homedir(), '.iflow', 'hooks.yml'),
|
|
64
|
+
path.join(os.homedir(), '.iflow', 'hooks.yaml'),
|
|
65
|
+
path.join(os.homedir(), '.iflow', 'config.yml'),
|
|
66
|
+
path.join(os.homedir(), '.iflow', 'settings.json')
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
for (const configFile of configLocations) {
|
|
70
|
+
try {
|
|
71
|
+
await fs.access(configFile);
|
|
72
|
+
const content = await fs.readFile(configFile, 'utf8');
|
|
73
|
+
if (content.includes('hooks') || content.includes('plugins') || content.includes('CrossCLIHookAdapter')) {
|
|
74
|
+
return {
|
|
75
|
+
exists: true,
|
|
76
|
+
path: configFile,
|
|
77
|
+
hasHooksSection: true,
|
|
78
|
+
contentPreview: content.substring(0, 200)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
// File doesn't exist, continue to next
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
exists: false,
|
|
88
|
+
path: null,
|
|
89
|
+
hasHooksSection: false,
|
|
90
|
+
contentPreview: ''
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Test iFlow hooks command
|
|
95
|
+
async testIFlowHooksCommand() {
|
|
96
|
+
return new Promise((resolve) => {
|
|
97
|
+
const child = spawn('iflow', ['hooks'], {
|
|
98
|
+
stdio: 'pipe',
|
|
99
|
+
timeout: 10000
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
let stdout = '';
|
|
103
|
+
let stderr = '';
|
|
104
|
+
|
|
105
|
+
child.stdout.on('data', (data) => {
|
|
106
|
+
stdout += data.toString();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
child.stderr.on('data', (data) => {
|
|
110
|
+
stderr += data.toString();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
child.on('close', (code) => {
|
|
114
|
+
resolve({
|
|
115
|
+
success: code === 0,
|
|
116
|
+
code: code,
|
|
117
|
+
stdout: stdout,
|
|
118
|
+
stderr: stderr,
|
|
119
|
+
output: stdout + stderr
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
child.on('error', (error) => {
|
|
124
|
+
resolve({
|
|
125
|
+
success: false,
|
|
126
|
+
error: error.message,
|
|
127
|
+
stdout: '',
|
|
128
|
+
stderr: '',
|
|
129
|
+
output: ''
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Test iFlow plugin command
|
|
136
|
+
async testIFlowPluginCommand() {
|
|
137
|
+
return new Promise((resolve) => {
|
|
138
|
+
const child = spawn('iflow', ['plugins'], {
|
|
139
|
+
stdio: 'pipe',
|
|
140
|
+
timeout: 10000
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
let stdout = '';
|
|
144
|
+
let stderr = '';
|
|
145
|
+
|
|
146
|
+
child.stdout.on('data', (data) => {
|
|
147
|
+
stdout += data.toString();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
child.stderr.on('data', (data) => {
|
|
151
|
+
stderr += data.toString();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
child.on('close', (code) => {
|
|
155
|
+
resolve({
|
|
156
|
+
success: code === 0,
|
|
157
|
+
code: code,
|
|
158
|
+
stdout: stdout,
|
|
159
|
+
stderr: stderr,
|
|
160
|
+
output: stdout + stderr
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
child.on('error', (error) => {
|
|
165
|
+
resolve({
|
|
166
|
+
success: false,
|
|
167
|
+
error: error.message,
|
|
168
|
+
stdout: '',
|
|
169
|
+
stderr: '',
|
|
170
|
+
output: ''
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Run comprehensive iFlow integration test
|
|
177
|
+
async runIntegrationTest() {
|
|
178
|
+
console.log('iFlow Stigmergy Integration Test');
|
|
179
|
+
console.log('='.repeat(40));
|
|
180
|
+
|
|
181
|
+
// Test 1: Check iFlow configuration
|
|
182
|
+
console.log('\n[Test 1] Checking iFlow configuration...');
|
|
183
|
+
const configExists = await this.checkIFlowConfig();
|
|
184
|
+
console.log(` Config directory exists: ${configExists ? '✓' : '✗'}`);
|
|
185
|
+
|
|
186
|
+
// Test 2: Check iFlow hooks directory
|
|
187
|
+
console.log('\n[Test 2] Checking iFlow hooks directory...');
|
|
188
|
+
const hooksInfo = await this.checkIFlowHooks();
|
|
189
|
+
console.log(` Hooks directory exists: ${hooksInfo.exists ? '✓' : '✗'}`);
|
|
190
|
+
console.log(` Total files: ${hooksInfo.files.length}`);
|
|
191
|
+
console.log(` Stigmergy-related files: ${hooksInfo.stigmergyFiles.length}`);
|
|
192
|
+
if (hooksInfo.stigmergyFiles.length > 0) {
|
|
193
|
+
console.log(' Stigmergy files found:');
|
|
194
|
+
hooksInfo.stigmergyFiles.forEach(file => console.log(` - ${file}`));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Test 3: Check hooks configuration
|
|
198
|
+
console.log('\n[Test 3] Checking hooks configuration...');
|
|
199
|
+
const hooksConfig = await this.checkIFlowHooksConfig();
|
|
200
|
+
console.log(` Config file exists: ${hooksConfig.exists ? '✓' : '✗'}`);
|
|
201
|
+
console.log(` Has hooks section: ${hooksConfig.hasHooksSection ? '✓' : '✗'}`);
|
|
202
|
+
|
|
203
|
+
// Test 4: Test iFlow hooks command
|
|
204
|
+
console.log('\n[Test 4] Testing iFlow hooks command...');
|
|
205
|
+
const hooksCommandResult = await this.testIFlowHooksCommand();
|
|
206
|
+
console.log(` Command success: ${hooksCommandResult.success ? '✓' : '✗'}`);
|
|
207
|
+
console.log(` Exit code: ${hooksCommandResult.code}`);
|
|
208
|
+
if (hooksCommandResult.output) {
|
|
209
|
+
const preview = hooksCommandResult.output.substring(0, 200);
|
|
210
|
+
console.log(` Output preview: ${preview}${hooksCommandResult.output.length > 200 ? '...' : ''}`);
|
|
211
|
+
|
|
212
|
+
// Check if Stigmergy is mentioned in output
|
|
213
|
+
if (hooksCommandResult.output.toLowerCase().includes('stigmergy')) {
|
|
214
|
+
console.log(' ✓ Stigmergy detected in hooks output');
|
|
215
|
+
} else {
|
|
216
|
+
console.log(' ✗ Stigmergy not detected in hooks output');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Test 5: Test iFlow plugin command
|
|
221
|
+
console.log('\n[Test 5] Testing iFlow plugin command...');
|
|
222
|
+
const pluginCommandResult = await this.testIFlowPluginCommand();
|
|
223
|
+
console.log(` Command success: ${pluginCommandResult.success ? '✓' : '✗'}`);
|
|
224
|
+
console.log(` Exit code: ${pluginCommandResult.code}`);
|
|
225
|
+
if (pluginCommandResult.output) {
|
|
226
|
+
const preview = pluginCommandResult.output.substring(0, 200);
|
|
227
|
+
console.log(` Output preview: ${preview}${pluginCommandResult.output.length > 200 ? '...' : ''}`);
|
|
228
|
+
|
|
229
|
+
// Check if Stigmergy is mentioned in output
|
|
230
|
+
if (pluginCommandResult.output.toLowerCase().includes('stigmergy')) {
|
|
231
|
+
console.log(' ✓ Stigmergy detected in plugins output');
|
|
232
|
+
} else {
|
|
233
|
+
console.log(' ✗ Stigmergy not detected in plugins output');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Summary
|
|
238
|
+
console.log('\n' + '='.repeat(40));
|
|
239
|
+
console.log('Integration Test Summary:');
|
|
240
|
+
console.log('='.repeat(40));
|
|
241
|
+
|
|
242
|
+
const results = {
|
|
243
|
+
configExists,
|
|
244
|
+
hooksDirectoryExists: hooksInfo.exists,
|
|
245
|
+
stigmergyHooksPresent: hooksInfo.stigmergyFiles.length > 0,
|
|
246
|
+
hooksCommandWorks: hooksCommandResult.success,
|
|
247
|
+
pluginsCommandWorks: pluginCommandResult.success,
|
|
248
|
+
stigmergyDetectedInHooks: hooksCommandResult.output.toLowerCase().includes('stigmergy'),
|
|
249
|
+
stigmergyDetectedInPlugins: pluginCommandResult.output.toLowerCase().includes('stigmergy')
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
Object.entries(results).forEach(([test, result]) => {
|
|
253
|
+
console.log(`${test}: ${result ? '✓ PASS' : '✗ FAIL'}`);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const passedTests = Object.values(results).filter(Boolean).length;
|
|
257
|
+
const totalTests = Object.values(results).length;
|
|
258
|
+
|
|
259
|
+
console.log(`\nOverall Result: ${passedTests}/${totalTests} tests passed`);
|
|
260
|
+
|
|
261
|
+
if (passedTests === totalTests) {
|
|
262
|
+
console.log('✓ iFlow Stigmergy integration is working correctly!');
|
|
263
|
+
} else if (passedTests > 0) {
|
|
264
|
+
console.log('⚠ iFlow Stigmergy integration is partially working.');
|
|
265
|
+
} else {
|
|
266
|
+
console.log('✗ iFlow Stigmergy integration is not working.');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return results;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Run the test
|
|
274
|
+
async function runIFlowIntegrationTest() {
|
|
275
|
+
const tester = new IFlowIntegrationTester();
|
|
276
|
+
const results = await tester.runIntegrationTest();
|
|
277
|
+
|
|
278
|
+
return results;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Export for use in other modules
|
|
282
|
+
module.exports = { IFlowIntegrationTester };
|
|
283
|
+
|
|
284
|
+
// Run if called directly
|
|
285
|
+
if (require.main === module) {
|
|
286
|
+
runIFlowIntegrationTest().then(results => {
|
|
287
|
+
process.exit(0);
|
|
288
|
+
}).catch(error => {
|
|
289
|
+
console.error('[Test Failed]:', error.message);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Improved Test for CLI Installation with Better Error Handling
|
|
5
|
+
* This test verifies installation commands with improved error handling
|
|
6
|
+
* and Windows compatibility
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { spawnSync, spawn } = require('child_process');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
|
|
13
|
+
class ImprovedInstallTester {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.testResults = [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Test 1: Verify npm availability with multiple methods
|
|
19
|
+
async testNpmAvailability() {
|
|
20
|
+
console.log('[TEST 1] Verifying npm availability with multiple methods...');
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
// Method 1: Direct npm command
|
|
24
|
+
console.log(' Testing direct npm command...');
|
|
25
|
+
const npmDirect = spawnSync('npm', ['--version'], {
|
|
26
|
+
encoding: 'utf8',
|
|
27
|
+
timeout: 10000
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
console.log(` Direct npm - Status: ${npmDirect.status}`);
|
|
31
|
+
if (npmDirect.stdout) {
|
|
32
|
+
console.log(` Direct npm - Version: ${npmDirect.stdout.trim()}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Method 2: npm through cmd.exe on Windows
|
|
36
|
+
const isWindows = process.platform === 'win32';
|
|
37
|
+
let npmThroughCmd = { status: null };
|
|
38
|
+
|
|
39
|
+
if (isWindows) {
|
|
40
|
+
console.log(' Testing npm through cmd.exe...');
|
|
41
|
+
npmThroughCmd = spawnSync('cmd.exe', ['/c', 'npm', '--version'], {
|
|
42
|
+
encoding: 'utf8',
|
|
43
|
+
timeout: 10000
|
|
44
|
+
});
|
|
45
|
+
console.log(` npm through cmd.exe - Status: ${npmThroughCmd.status}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Method 3: Check PATH for npm
|
|
49
|
+
console.log(' Checking PATH for npm...');
|
|
50
|
+
const pathEnv = process.env.PATH || '';
|
|
51
|
+
const hasNpmInPath = pathEnv.toLowerCase().includes('npm');
|
|
52
|
+
console.log(` npm in PATH: ${hasNpmInPath}`);
|
|
53
|
+
|
|
54
|
+
const npmAvailable = npmDirect.status === 0 || npmThroughCmd.status === 0;
|
|
55
|
+
|
|
56
|
+
this.testResults.push({
|
|
57
|
+
name: 'npm Availability',
|
|
58
|
+
passed: npmAvailable,
|
|
59
|
+
details: `Direct: ${npmDirect.status === 0}, Cmd: ${npmThroughCmd.status === 0}, PATH: ${hasNpmInPath}`
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return npmAvailable;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.log(` ✗ Failed to check npm availability: ${error.message}`);
|
|
65
|
+
this.testResults.push({
|
|
66
|
+
name: 'npm Availability',
|
|
67
|
+
passed: false,
|
|
68
|
+
details: `Failed to check: ${error.message}`
|
|
69
|
+
});
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Test 2: Test Qoder CLI installation with shell options
|
|
75
|
+
async testQoderInstallationWithShellOptions() {
|
|
76
|
+
console.log('\n[TEST 2] Testing Qoder CLI installation with shell options...');
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const installCommand = 'npm install -g @qoder-ai/qodercli';
|
|
80
|
+
const commandParts = installCommand.split(' ');
|
|
81
|
+
const command = commandParts[0];
|
|
82
|
+
const args = commandParts.slice(1);
|
|
83
|
+
|
|
84
|
+
console.log(` Installation command: ${installCommand}`);
|
|
85
|
+
|
|
86
|
+
// Test 1: Without shell option
|
|
87
|
+
console.log(' Testing without shell option...');
|
|
88
|
+
const test1 = spawnSync(command, args, {
|
|
89
|
+
encoding: 'utf8',
|
|
90
|
+
timeout: 15000,
|
|
91
|
+
stdio: 'pipe'
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
console.log(` Without shell - Status: ${test1.status}`);
|
|
95
|
+
|
|
96
|
+
// Test 2: With shell=true
|
|
97
|
+
console.log(' Testing with shell=true...');
|
|
98
|
+
const test2 = spawnSync(command, args, {
|
|
99
|
+
encoding: 'utf8',
|
|
100
|
+
timeout: 15000,
|
|
101
|
+
stdio: 'pipe',
|
|
102
|
+
shell: true
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
console.log(` With shell - Status: ${test2.status}`);
|
|
106
|
+
|
|
107
|
+
// Test 3: On Windows, try cmd.exe explicitly
|
|
108
|
+
let test3 = { status: null };
|
|
109
|
+
if (process.platform === 'win32') {
|
|
110
|
+
console.log(' Testing with explicit cmd.exe...');
|
|
111
|
+
const fullCommand = [command, ...args].join(' ');
|
|
112
|
+
test3 = spawnSync('cmd.exe', ['/c', fullCommand], {
|
|
113
|
+
encoding: 'utf8',
|
|
114
|
+
timeout: 15000,
|
|
115
|
+
stdio: 'pipe'
|
|
116
|
+
});
|
|
117
|
+
console.log(` Explicit cmd.exe - Status: ${test3.status}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Determine which method works
|
|
121
|
+
const worksWithoutShell = test1.status === 0;
|
|
122
|
+
const worksWithShell = test2.status === 0;
|
|
123
|
+
const worksWithCmd = test3.status === 0;
|
|
124
|
+
|
|
125
|
+
console.log(` Results - Without shell: ${worksWithoutShell}, With shell: ${worksWithShell}, With cmd: ${worksWithCmd}`);
|
|
126
|
+
|
|
127
|
+
this.testResults.push({
|
|
128
|
+
name: 'Qoder Installation Shell Options',
|
|
129
|
+
passed: worksWithoutShell || worksWithShell || worksWithCmd,
|
|
130
|
+
details: `Without shell: ${worksWithoutShell}, With shell: ${worksWithShell}, With cmd: ${worksWithCmd}`
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return worksWithoutShell || worksWithShell || worksWithCmd;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.log(` ✗ Failed to test installation with shell options: ${error.message}`);
|
|
136
|
+
this.testResults.push({
|
|
137
|
+
name: 'Qoder Installation Shell Options',
|
|
138
|
+
passed: false,
|
|
139
|
+
details: `Failed to test: ${error.message}`
|
|
140
|
+
});
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Test 3: Test spawn vs spawnSync behavior
|
|
146
|
+
async testSpawnVsSpawnSync() {
|
|
147
|
+
console.log('\n[TEST 3] Testing spawn vs spawnSync behavior...');
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
const command = 'npm';
|
|
151
|
+
const args = ['--version'];
|
|
152
|
+
|
|
153
|
+
// Test spawnSync
|
|
154
|
+
console.log(' Testing spawnSync...');
|
|
155
|
+
const syncResult = spawnSync(command, args, {
|
|
156
|
+
encoding: 'utf8',
|
|
157
|
+
timeout: 10000,
|
|
158
|
+
shell: true
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
console.log(` spawnSync - Status: ${syncResult.status}`);
|
|
162
|
+
|
|
163
|
+
// Test spawn
|
|
164
|
+
console.log(' Testing spawn...');
|
|
165
|
+
const spawnResult = await new Promise((resolve) => {
|
|
166
|
+
const child = spawn(command, args, {
|
|
167
|
+
encoding: 'utf8',
|
|
168
|
+
timeout: 10000,
|
|
169
|
+
shell: true
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
let stdout = '';
|
|
173
|
+
let stderr = '';
|
|
174
|
+
|
|
175
|
+
child.stdout.on('data', (data) => {
|
|
176
|
+
stdout += data.toString();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
child.stderr.on('data', (data) => {
|
|
180
|
+
stderr += data.toString();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
child.on('close', (code) => {
|
|
184
|
+
resolve({
|
|
185
|
+
status: code,
|
|
186
|
+
stdout: stdout,
|
|
187
|
+
stderr: stderr
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
child.on('error', (error) => {
|
|
192
|
+
resolve({
|
|
193
|
+
status: null,
|
|
194
|
+
error: error.message
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Timeout
|
|
199
|
+
setTimeout(() => {
|
|
200
|
+
child.kill();
|
|
201
|
+
resolve({
|
|
202
|
+
status: null,
|
|
203
|
+
error: 'Timeout'
|
|
204
|
+
});
|
|
205
|
+
}, 11000);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
console.log(` spawn - Status: ${spawnResult.status}`);
|
|
209
|
+
if (spawnResult.error) {
|
|
210
|
+
console.log(` spawn - Error: ${spawnResult.error}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const bothWork = syncResult.status === 0 && spawnResult.status === 0;
|
|
214
|
+
|
|
215
|
+
this.testResults.push({
|
|
216
|
+
name: 'Spawn vs SpawnSync',
|
|
217
|
+
passed: bothWork,
|
|
218
|
+
details: `spawnSync: ${syncResult.status}, spawn: ${spawnResult.status}`
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return bothWork;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.log(` ✗ Failed to test spawn behavior: ${error.message}`);
|
|
224
|
+
this.testResults.push({
|
|
225
|
+
name: 'Spawn vs SpawnSync',
|
|
226
|
+
passed: false,
|
|
227
|
+
details: `Failed to test: ${error.message}`
|
|
228
|
+
});
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Test 4: Verify current installation implementation issues
|
|
234
|
+
async testCurrentInstallationImplementation() {
|
|
235
|
+
console.log('\n[TEST 4] Verifying current installation implementation...');
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
// Simulate the current implementation from main_english.js
|
|
239
|
+
const toolInfo = {
|
|
240
|
+
name: 'Qoder CLI',
|
|
241
|
+
install: 'npm install -g @qoder-ai/qodercli'
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
console.log(` Simulating current implementation for: ${toolInfo.name}`);
|
|
245
|
+
console.log(` Install command: ${toolInfo.install}`);
|
|
246
|
+
|
|
247
|
+
const installCmd = toolInfo.install.split(' ');
|
|
248
|
+
console.log(` Command parts: ${JSON.stringify(installCmd)}`);
|
|
249
|
+
|
|
250
|
+
// Current implementation (similar to lines 512-518 in main_english.js)
|
|
251
|
+
console.log(' Testing current implementation approach...');
|
|
252
|
+
const result = spawnSync(installCmd[0], installCmd.slice(1), {
|
|
253
|
+
encoding: 'utf8',
|
|
254
|
+
timeout: 300000, // 5 minutes
|
|
255
|
+
stdio: 'pipe', // Changed from 'inherit' to 'pipe' for testing
|
|
256
|
+
env: process.env
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
console.log(` Current implementation - Status: ${result.status}`);
|
|
260
|
+
if (result.error) {
|
|
261
|
+
console.log(` Current implementation - Error: ${result.error.message}`);
|
|
262
|
+
}
|
|
263
|
+
if (result.stderr && result.stderr.length > 0) {
|
|
264
|
+
const stderrPreview = result.stderr.substring(0, 200);
|
|
265
|
+
console.log(` Current implementation - Stderr preview: ${stderrPreview}${result.stderr.length > 200 ? '...' : ''}`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Test with shell=true
|
|
269
|
+
console.log(' Testing current implementation with shell=true...');
|
|
270
|
+
const resultWithShell = spawnSync(installCmd[0], installCmd.slice(1), {
|
|
271
|
+
encoding: 'utf8',
|
|
272
|
+
timeout: 300000,
|
|
273
|
+
stdio: 'pipe',
|
|
274
|
+
env: process.env,
|
|
275
|
+
shell: true
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
console.log(` Current implementation with shell - Status: ${resultWithShell.status}`);
|
|
279
|
+
if (resultWithShell.error) {
|
|
280
|
+
console.log(` Current implementation with shell - Error: ${resultWithShell.error.message}`);
|
|
281
|
+
}
|
|
282
|
+
if (resultWithShell.stderr && resultWithShell.stderr.length > 0) {
|
|
283
|
+
const stderrPreview = resultWithShell.stderr.substring(0, 200);
|
|
284
|
+
console.log(` Current implementation with shell - Stderr preview: ${stderrPreview}${resultWithShell.stderr.length > 200 ? '...' : ''}`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
this.testResults.push({
|
|
288
|
+
name: 'Current Installation Implementation',
|
|
289
|
+
passed: result.status === 0 || resultWithShell.status === 0,
|
|
290
|
+
details: `Without shell: ${result.status}, With shell: ${resultWithShell.status}`
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return result.status === 0 || resultWithShell.status === 0;
|
|
294
|
+
} catch (error) {
|
|
295
|
+
console.log(` ✗ Failed to test current implementation: ${error.message}`);
|
|
296
|
+
this.testResults.push({
|
|
297
|
+
name: 'Current Installation Implementation',
|
|
298
|
+
passed: false,
|
|
299
|
+
details: `Failed to test: ${error.message}`
|
|
300
|
+
});
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Run all tests
|
|
306
|
+
async runAllTests() {
|
|
307
|
+
console.log('Improved CLI Installation Test');
|
|
308
|
+
console.log('='.repeat(40));
|
|
309
|
+
|
|
310
|
+
await this.testNpmAvailability();
|
|
311
|
+
await this.testQoderInstallationWithShellOptions();
|
|
312
|
+
await this.testSpawnVsSpawnSync();
|
|
313
|
+
await this.testCurrentInstallationImplementation();
|
|
314
|
+
|
|
315
|
+
// Summary
|
|
316
|
+
console.log('\n' + '='.repeat(40));
|
|
317
|
+
console.log('Improved Installation Test Summary:');
|
|
318
|
+
console.log('='.repeat(40));
|
|
319
|
+
|
|
320
|
+
let passedTests = 0;
|
|
321
|
+
this.testResults.forEach(result => {
|
|
322
|
+
console.log(`${result.name}: ${result.passed ? '✓ PASS' : '✗ FAIL'} - ${result.details}`);
|
|
323
|
+
if (result.passed) passedTests++;
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
console.log(`\nOverall Result: ${passedTests}/${this.testResults.length} tests passed`);
|
|
327
|
+
|
|
328
|
+
if (passedTests === this.testResults.length) {
|
|
329
|
+
console.log('✓ All improved installation tests passed!');
|
|
330
|
+
} else if (passedTests > 0) {
|
|
331
|
+
console.log('⚠ Some improved installation tests passed.');
|
|
332
|
+
} else {
|
|
333
|
+
console.log('✗ All improved installation tests failed.');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
totalTests: this.testResults.length,
|
|
338
|
+
passedTests: passedTests,
|
|
339
|
+
results: this.testResults
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Run the tests
|
|
345
|
+
async function runImprovedInstallTests() {
|
|
346
|
+
const tester = new ImprovedInstallTester();
|
|
347
|
+
const results = await tester.runAllTests();
|
|
348
|
+
return results;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Export for use in other modules
|
|
352
|
+
module.exports = { ImprovedInstallTester };
|
|
353
|
+
|
|
354
|
+
// Run if called directly
|
|
355
|
+
if (require.main === module) {
|
|
356
|
+
runImprovedInstallTests().then(results => {
|
|
357
|
+
process.exit(results.passedTests === results.totalTests ? 0 : 1);
|
|
358
|
+
}).catch(error => {
|
|
359
|
+
console.error('[Test Failed]:', error.message);
|
|
360
|
+
process.exit(1);
|
|
361
|
+
});
|
|
362
|
+
}
|