@stackmemoryai/stackmemory 0.3.22 โ 0.3.24
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/dist/cli/commands/ralph.js +294 -0
- package/dist/cli/commands/ralph.js.map +7 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +2 -2
- package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js +586 -0
- package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js.map +7 -0
- package/dist/integrations/ralph/context/context-budget-manager.js +297 -0
- package/dist/integrations/ralph/context/context-budget-manager.js.map +7 -0
- package/dist/integrations/ralph/context/stackmemory-context-loader.js +356 -0
- package/dist/integrations/ralph/context/stackmemory-context-loader.js.map +7 -0
- package/dist/integrations/ralph/index.js +14 -0
- package/dist/integrations/ralph/index.js.map +7 -0
- package/dist/integrations/ralph/learning/pattern-learner.js +397 -0
- package/dist/integrations/ralph/learning/pattern-learner.js.map +7 -0
- package/dist/integrations/ralph/lifecycle/iteration-lifecycle.js +444 -0
- package/dist/integrations/ralph/lifecycle/iteration-lifecycle.js.map +7 -0
- package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js +459 -0
- package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js.map +7 -0
- package/dist/integrations/ralph/performance/performance-optimizer.js +354 -0
- package/dist/integrations/ralph/performance/performance-optimizer.js.map +7 -0
- package/dist/integrations/ralph/ralph-integration-demo.js +178 -0
- package/dist/integrations/ralph/ralph-integration-demo.js.map +7 -0
- package/dist/integrations/ralph/state/state-reconciler.js +400 -0
- package/dist/integrations/ralph/state/state-reconciler.js.map +7 -0
- package/dist/integrations/ralph/swarm/swarm-coordinator.js +487 -0
- package/dist/integrations/ralph/swarm/swarm-coordinator.js.map +7 -0
- package/dist/integrations/ralph/types.js +1 -0
- package/dist/integrations/ralph/types.js.map +7 -0
- package/dist/integrations/ralph/visualization/ralph-debugger.js +581 -0
- package/dist/integrations/ralph/visualization/ralph-debugger.js.map +7 -0
- package/package.json +1 -1
- package/scripts/deploy-ralph-swarm.sh +365 -0
- package/scripts/ralph-integration-test.js +274 -0
- package/scripts/ralph-loop-implementation.js +404 -0
- package/scripts/swarm-monitor.js +509 -0
- package/scripts/test-parallel-swarms.js +443 -0
- package/scripts/testing/ralph-cli-test.js +88 -0
- package/scripts/testing/ralph-integration-validation.js +727 -0
- package/scripts/testing/ralph-swarm-test-scenarios.js +613 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test Parallel Swarm Execution
|
|
5
|
+
* Validates that multiple swarms can run concurrently without conflicts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
import * as fs from 'fs/promises';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
12
|
+
|
|
13
|
+
class ParallelSwarmTester {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.swarmDir = '.swarm';
|
|
16
|
+
this.testResults = {
|
|
17
|
+
swarms: [],
|
|
18
|
+
conflicts: [],
|
|
19
|
+
performance: {},
|
|
20
|
+
success: true
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async runParallelTests() {
|
|
25
|
+
console.log('๐ Starting Parallel Swarm Tests');
|
|
26
|
+
console.log('=' .repeat(50));
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Ensure swarm directory exists
|
|
30
|
+
await fs.mkdir(this.swarmDir, { recursive: true });
|
|
31
|
+
|
|
32
|
+
// Test 1: Launch multiple swarms simultaneously
|
|
33
|
+
await this.testSimultaneousLaunch();
|
|
34
|
+
|
|
35
|
+
// Test 2: Test resource isolation
|
|
36
|
+
await this.testResourceIsolation();
|
|
37
|
+
|
|
38
|
+
// Test 3: Test coordination between swarms
|
|
39
|
+
await this.testInterSwarmCoordination();
|
|
40
|
+
|
|
41
|
+
// Test 4: Test performance under load
|
|
42
|
+
await this.testPerformanceUnderLoad();
|
|
43
|
+
|
|
44
|
+
// Test 5: Test failure handling
|
|
45
|
+
await this.testFailureHandling();
|
|
46
|
+
|
|
47
|
+
// Generate report
|
|
48
|
+
await this.generateReport();
|
|
49
|
+
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error('Test suite failed:', error);
|
|
52
|
+
this.testResults.success = false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async testSimultaneousLaunch() {
|
|
57
|
+
console.log('\n๐ฆ Test 1: Simultaneous Swarm Launch');
|
|
58
|
+
|
|
59
|
+
const swarmConfigs = [
|
|
60
|
+
{
|
|
61
|
+
id: 'swarm-frontend',
|
|
62
|
+
project: 'Build user interface components',
|
|
63
|
+
agents: ['developer', 'tester'],
|
|
64
|
+
maxAgents: 3
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 'swarm-backend',
|
|
68
|
+
project: 'Create API endpoints',
|
|
69
|
+
agents: ['architect', 'developer'],
|
|
70
|
+
maxAgents: 3
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'swarm-database',
|
|
74
|
+
project: 'Design database schema',
|
|
75
|
+
agents: ['architect', 'optimizer'],
|
|
76
|
+
maxAgents: 2
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'swarm-testing',
|
|
80
|
+
project: 'Write comprehensive tests',
|
|
81
|
+
agents: ['tester', 'reviewer'],
|
|
82
|
+
maxAgents: 2
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
const launchPromises = swarmConfigs.map(config =>
|
|
87
|
+
this.launchSwarm(config)
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const startTime = Date.now();
|
|
91
|
+
const results = await Promise.allSettled(launchPromises);
|
|
92
|
+
const duration = Date.now() - startTime;
|
|
93
|
+
|
|
94
|
+
let successCount = 0;
|
|
95
|
+
results.forEach((result, index) => {
|
|
96
|
+
if (result.status === 'fulfilled') {
|
|
97
|
+
successCount++;
|
|
98
|
+
console.log(` โ
${swarmConfigs[index].id} launched successfully`);
|
|
99
|
+
this.testResults.swarms.push({
|
|
100
|
+
...swarmConfigs[index],
|
|
101
|
+
status: 'launched',
|
|
102
|
+
pid: result.value
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
console.log(` โ ${swarmConfigs[index].id} failed: ${result.reason}`);
|
|
106
|
+
this.testResults.swarms.push({
|
|
107
|
+
...swarmConfigs[index],
|
|
108
|
+
status: 'failed',
|
|
109
|
+
error: result.reason.message
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
console.log(` Launched ${successCount}/${swarmConfigs.length} swarms in ${duration}ms`);
|
|
115
|
+
|
|
116
|
+
// Wait a bit for swarms to initialize
|
|
117
|
+
await this.sleep(3000);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async testResourceIsolation() {
|
|
121
|
+
console.log('\n๐ Test 2: Resource Isolation');
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
// Check that each swarm has its own working directory
|
|
125
|
+
const swarmDirs = await fs.readdir(this.swarmDir);
|
|
126
|
+
const isolationChecks = [];
|
|
127
|
+
|
|
128
|
+
for (const dir of swarmDirs) {
|
|
129
|
+
if (dir.startsWith('developer-') || dir.startsWith('architect-') ||
|
|
130
|
+
dir.startsWith('tester-') || dir.startsWith('reviewer-')) {
|
|
131
|
+
const dirPath = path.join(this.swarmDir, dir);
|
|
132
|
+
const stat = await fs.stat(dirPath);
|
|
133
|
+
|
|
134
|
+
if (stat.isDirectory()) {
|
|
135
|
+
isolationChecks.push({
|
|
136
|
+
directory: dir,
|
|
137
|
+
isolated: true,
|
|
138
|
+
created: stat.birthtime
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
console.log(` โ
Found ${isolationChecks.length} isolated agent directories`);
|
|
145
|
+
|
|
146
|
+
// Check for potential conflicts
|
|
147
|
+
const conflicts = await this.checkForConflicts();
|
|
148
|
+
if (conflicts.length > 0) {
|
|
149
|
+
console.log(` โ ๏ธ Found ${conflicts.length} potential conflicts`);
|
|
150
|
+
this.testResults.conflicts = conflicts;
|
|
151
|
+
} else {
|
|
152
|
+
console.log(' โ
No resource conflicts detected');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.log(` โ Resource isolation check failed: ${error.message}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async testInterSwarmCoordination() {
|
|
161
|
+
console.log('\n๐ค Test 3: Inter-Swarm Coordination');
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
// Create shared context for coordination test
|
|
165
|
+
const sharedContext = {
|
|
166
|
+
id: uuidv4(),
|
|
167
|
+
timestamp: Date.now(),
|
|
168
|
+
message: 'Test coordination message',
|
|
169
|
+
swarms: this.testResults.swarms.filter(s => s.status === 'launched')
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Write to shared context
|
|
173
|
+
const contextPath = path.join(this.swarmDir, 'shared-context.json');
|
|
174
|
+
await fs.writeFile(contextPath, JSON.stringify(sharedContext, null, 2));
|
|
175
|
+
|
|
176
|
+
console.log(' โ
Shared context created');
|
|
177
|
+
|
|
178
|
+
// Simulate coordination event
|
|
179
|
+
const coordinationEvent = {
|
|
180
|
+
type: 'task_completion',
|
|
181
|
+
source: 'swarm-frontend',
|
|
182
|
+
target: 'swarm-backend',
|
|
183
|
+
data: {
|
|
184
|
+
task: 'UI components ready',
|
|
185
|
+
timestamp: Date.now()
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const eventPath = path.join(this.swarmDir, 'coordination-events.jsonl');
|
|
190
|
+
await fs.appendFile(eventPath, JSON.stringify(coordinationEvent) + '\n');
|
|
191
|
+
|
|
192
|
+
console.log(' โ
Coordination event logged');
|
|
193
|
+
|
|
194
|
+
// Check if swarms can read shared context
|
|
195
|
+
const contextExists = await fs.access(contextPath).then(() => true).catch(() => false);
|
|
196
|
+
if (contextExists) {
|
|
197
|
+
console.log(' โ
Swarms can access shared context');
|
|
198
|
+
} else {
|
|
199
|
+
console.log(' โ Shared context not accessible');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.log(` โ Coordination test failed: ${error.message}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async testPerformanceUnderLoad() {
|
|
208
|
+
console.log('\nโก Test 4: Performance Under Load');
|
|
209
|
+
|
|
210
|
+
const metrics = {
|
|
211
|
+
memoryUsage: process.memoryUsage(),
|
|
212
|
+
cpuUsage: process.cpuUsage(),
|
|
213
|
+
activeSwarms: this.testResults.swarms.filter(s => s.status === 'launched').length,
|
|
214
|
+
timestamp: Date.now()
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Simulate heavy load by launching additional tasks
|
|
218
|
+
const loadTasks = [];
|
|
219
|
+
for (let i = 0; i < 5; i++) {
|
|
220
|
+
loadTasks.push(this.simulateHeavyTask(i));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const startTime = Date.now();
|
|
224
|
+
await Promise.allSettled(loadTasks);
|
|
225
|
+
const duration = Date.now() - startTime;
|
|
226
|
+
|
|
227
|
+
const afterMetrics = {
|
|
228
|
+
memoryUsage: process.memoryUsage(),
|
|
229
|
+
cpuUsage: process.cpuUsage(),
|
|
230
|
+
duration: duration
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
this.testResults.performance = {
|
|
234
|
+
before: metrics,
|
|
235
|
+
after: afterMetrics,
|
|
236
|
+
memoryIncrease: afterMetrics.memoryUsage.heapUsed - metrics.memoryUsage.heapUsed,
|
|
237
|
+
taskDuration: duration,
|
|
238
|
+
tasksPerSecond: (5 / (duration / 1000)).toFixed(2)
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
console.log(` โ
Processed 5 heavy tasks in ${duration}ms`);
|
|
242
|
+
console.log(` ๐ Memory increase: ${(this.testResults.performance.memoryIncrease / 1024 / 1024).toFixed(2)}MB`);
|
|
243
|
+
console.log(` ๐ Tasks per second: ${this.testResults.performance.tasksPerSecond}`);
|
|
244
|
+
|
|
245
|
+
if (duration < 10000) {
|
|
246
|
+
console.log(' โ
Performance acceptable under load');
|
|
247
|
+
} else {
|
|
248
|
+
console.log(' โ ๏ธ Performance degradation detected');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async testFailureHandling() {
|
|
253
|
+
console.log('\n๐จ Test 5: Failure Handling');
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
// Simulate a swarm failure
|
|
257
|
+
const failingSwarm = {
|
|
258
|
+
id: 'swarm-failing',
|
|
259
|
+
project: 'This will fail',
|
|
260
|
+
agents: ['invalid_agent'],
|
|
261
|
+
maxAgents: 1
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
console.log(' Testing graceful failure handling...');
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
await this.launchSwarm(failingSwarm);
|
|
268
|
+
console.log(' โ Expected failure did not occur');
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.log(' โ
Failure handled gracefully');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Test recovery mechanism
|
|
274
|
+
console.log(' Testing recovery mechanism...');
|
|
275
|
+
|
|
276
|
+
const recoverySwarm = {
|
|
277
|
+
id: 'swarm-recovery',
|
|
278
|
+
project: 'Recovery test',
|
|
279
|
+
agents: ['developer'],
|
|
280
|
+
maxAgents: 1
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
const pid = await this.launchSwarm(recoverySwarm);
|
|
285
|
+
if (pid) {
|
|
286
|
+
console.log(' โ
Recovery successful');
|
|
287
|
+
}
|
|
288
|
+
} catch (error) {
|
|
289
|
+
console.log(' โ Recovery failed');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.log(` โ Failure handling test error: ${error.message}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Helper methods
|
|
298
|
+
async launchSwarm(config) {
|
|
299
|
+
return new Promise((resolve, reject) => {
|
|
300
|
+
const args = [
|
|
301
|
+
'dist/cli/index.js',
|
|
302
|
+
'ralph',
|
|
303
|
+
'swarm',
|
|
304
|
+
config.project,
|
|
305
|
+
'--agents',
|
|
306
|
+
config.agents.join(','),
|
|
307
|
+
'--max-agents',
|
|
308
|
+
config.maxAgents.toString()
|
|
309
|
+
];
|
|
310
|
+
|
|
311
|
+
const child = spawn('node', args, {
|
|
312
|
+
detached: true,
|
|
313
|
+
stdio: 'ignore'
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
child.on('error', (error) => {
|
|
317
|
+
reject(error);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Give it a moment to start
|
|
321
|
+
setTimeout(() => {
|
|
322
|
+
if (child.pid) {
|
|
323
|
+
resolve(child.pid);
|
|
324
|
+
} else {
|
|
325
|
+
reject(new Error('Failed to get PID'));
|
|
326
|
+
}
|
|
327
|
+
}, 1000);
|
|
328
|
+
|
|
329
|
+
child.unref();
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async checkForConflicts() {
|
|
334
|
+
const conflicts = [];
|
|
335
|
+
|
|
336
|
+
// Check for port conflicts
|
|
337
|
+
const usedPorts = new Set();
|
|
338
|
+
for (const swarm of this.testResults.swarms) {
|
|
339
|
+
const port = 3456 + this.testResults.swarms.indexOf(swarm);
|
|
340
|
+
if (usedPorts.has(port)) {
|
|
341
|
+
conflicts.push({
|
|
342
|
+
type: 'port_conflict',
|
|
343
|
+
port: port,
|
|
344
|
+
swarms: [swarm.id]
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
usedPorts.add(port);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Check for file lock conflicts
|
|
351
|
+
try {
|
|
352
|
+
const lockFiles = await fs.readdir(this.swarmDir);
|
|
353
|
+
const lockConflicts = lockFiles.filter(f => f.endsWith('.lock'));
|
|
354
|
+
if (lockConflicts.length > 0) {
|
|
355
|
+
conflicts.push({
|
|
356
|
+
type: 'file_lock',
|
|
357
|
+
files: lockConflicts
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
} catch (error) {
|
|
361
|
+
// Directory might not exist yet
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return conflicts;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async simulateHeavyTask(index) {
|
|
368
|
+
return new Promise((resolve) => {
|
|
369
|
+
// Simulate CPU-intensive work
|
|
370
|
+
let result = 0;
|
|
371
|
+
for (let i = 0; i < 1000000; i++) {
|
|
372
|
+
result += Math.sqrt(i);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
setTimeout(() => {
|
|
376
|
+
resolve({ index, result });
|
|
377
|
+
}, Math.random() * 1000);
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async sleep(ms) {
|
|
382
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async generateReport() {
|
|
386
|
+
console.log('\n๐ Generating Test Report');
|
|
387
|
+
console.log('=' .repeat(50));
|
|
388
|
+
|
|
389
|
+
const report = {
|
|
390
|
+
timestamp: new Date().toISOString(),
|
|
391
|
+
success: this.testResults.success,
|
|
392
|
+
summary: {
|
|
393
|
+
totalSwarms: this.testResults.swarms.length,
|
|
394
|
+
launched: this.testResults.swarms.filter(s => s.status === 'launched').length,
|
|
395
|
+
failed: this.testResults.swarms.filter(s => s.status === 'failed').length,
|
|
396
|
+
conflicts: this.testResults.conflicts.length,
|
|
397
|
+
performance: this.testResults.performance
|
|
398
|
+
},
|
|
399
|
+
details: this.testResults
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
const reportPath = path.join(this.swarmDir, 'parallel-test-report.json');
|
|
403
|
+
await fs.writeFile(reportPath, JSON.stringify(report, null, 2));
|
|
404
|
+
|
|
405
|
+
console.log('๐ Test Summary:');
|
|
406
|
+
console.log(` Total Swarms: ${report.summary.totalSwarms}`);
|
|
407
|
+
console.log(` Successfully Launched: ${report.summary.launched}`);
|
|
408
|
+
console.log(` Failed: ${report.summary.failed}`);
|
|
409
|
+
console.log(` Conflicts Detected: ${report.summary.conflicts}`);
|
|
410
|
+
|
|
411
|
+
if (this.testResults.performance.tasksPerSecond) {
|
|
412
|
+
console.log(` Performance: ${this.testResults.performance.tasksPerSecond} tasks/sec`);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
console.log(`\n๐ Report saved to: ${reportPath}`);
|
|
416
|
+
|
|
417
|
+
if (this.testResults.success && report.summary.launched > 0) {
|
|
418
|
+
console.log('\nโ
Parallel swarm execution validated successfully!');
|
|
419
|
+
} else {
|
|
420
|
+
console.log('\nโ ๏ธ Some issues detected. Review the report for details.');
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Cleanup: Stop launched swarms
|
|
424
|
+
console.log('\n๐งน Cleaning up test swarms...');
|
|
425
|
+
for (const swarm of this.testResults.swarms) {
|
|
426
|
+
if (swarm.pid) {
|
|
427
|
+
try {
|
|
428
|
+
process.kill(swarm.pid, 'SIGTERM');
|
|
429
|
+
} catch (error) {
|
|
430
|
+
// Process might have already ended
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Run tests if executed directly
|
|
438
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
439
|
+
const tester = new ParallelSwarmTester();
|
|
440
|
+
tester.runParallelTests().catch(console.error);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export { ParallelSwarmTester };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simple Ralph CLI Functionality Test
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
|
|
10
|
+
console.log('๐ญ Testing Ralph CLI Commands');
|
|
11
|
+
console.log('=' .repeat(40));
|
|
12
|
+
|
|
13
|
+
const tests = [
|
|
14
|
+
{
|
|
15
|
+
name: 'Ralph Help Command',
|
|
16
|
+
command: 'node dist/cli/index.js ralph --help',
|
|
17
|
+
expectOutput: 'Ralph Wiggum Loop integration'
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'Ralph Init Command',
|
|
21
|
+
command: 'node dist/cli/index.js ralph init "Test task" --criteria "Tests pass,Code works"',
|
|
22
|
+
expectFiles: ['.ralph/task.md']
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'Ralph Status Command',
|
|
26
|
+
command: 'node dist/cli/index.js ralph status',
|
|
27
|
+
expectOutput: 'Ralph Loop Status'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Ralph Debug Command',
|
|
31
|
+
command: 'node dist/cli/index.js ralph debug',
|
|
32
|
+
expectOutput: 'Ralph Loop Debug'
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
let passed = 0;
|
|
37
|
+
let failed = 0;
|
|
38
|
+
|
|
39
|
+
for (const test of tests) {
|
|
40
|
+
try {
|
|
41
|
+
console.log(`Testing: ${test.name}...`);
|
|
42
|
+
|
|
43
|
+
const result = execSync(test.command, {
|
|
44
|
+
encoding: 'utf8',
|
|
45
|
+
stdio: 'pipe',
|
|
46
|
+
timeout: 30000
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
let testPassed = true;
|
|
50
|
+
|
|
51
|
+
if (test.expectOutput && !result.includes(test.expectOutput)) {
|
|
52
|
+
console.log(` โ Expected output "${test.expectOutput}" not found`);
|
|
53
|
+
testPassed = false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (test.expectFiles) {
|
|
57
|
+
for (const file of test.expectFiles) {
|
|
58
|
+
if (!fs.existsSync(file)) {
|
|
59
|
+
console.log(` โ Expected file "${file}" not created`);
|
|
60
|
+
testPassed = false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (testPassed) {
|
|
66
|
+
console.log(` โ
${test.name} passed`);
|
|
67
|
+
passed++;
|
|
68
|
+
} else {
|
|
69
|
+
failed++;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(` Output: ${result.substring(0, 150)}...`);
|
|
73
|
+
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.log(` โ ${test.name} failed: ${error.message}`);
|
|
76
|
+
failed++;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log('');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
83
|
+
console.log(`Success rate: ${Math.round((passed / (passed + failed)) * 100)}%`);
|
|
84
|
+
|
|
85
|
+
// Clean up
|
|
86
|
+
if (fs.existsSync('.ralph')) {
|
|
87
|
+
fs.rmSync('.ralph', { recursive: true, force: true });
|
|
88
|
+
}
|