forked from github/copilot-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest-run-command.js
More file actions
225 lines (198 loc) · 7.76 KB
/
test-run-command.js
File metadata and controls
225 lines (198 loc) · 7.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#!/usr/bin/env node
/**
* Test script for v0.0.5 run_command functionality
* Tests: command execution, safety analysis, output truncation
*/
const path = require('path');
// Import the system-automation module
const systemAutomationPath = path.join(__dirname, '..', 'src', 'main', 'system-automation.js');
const systemAutomation = require(systemAutomationPath);
const {
ACTION_TYPES,
executeAction,
DANGEROUS_COMMAND_PATTERNS,
isCommandDangerous,
truncateOutput
} = systemAutomation;
// Test results tracker
const results = {
passed: 0,
failed: 0,
tests: []
};
function test(name, fn) {
try {
fn();
results.passed++;
results.tests.push({ name, status: 'PASS' });
console.log(`✅ PASS: ${name}`);
} catch (error) {
results.failed++;
results.tests.push({ name, status: 'FAIL', error: error.message });
console.log(`❌ FAIL: ${name}`);
console.log(` Error: ${error.message}`);
}
}
async function testAsync(name, fn) {
try {
await fn();
results.passed++;
results.tests.push({ name, status: 'PASS' });
console.log(`✅ PASS: ${name}`);
} catch (error) {
results.failed++;
results.tests.push({ name, status: 'FAIL', error: error.message });
console.log(`❌ FAIL: ${name}`);
console.log(` Error: ${error.message}`);
}
}
function assert(condition, message) {
if (!condition) {
throw new Error(message || 'Assertion failed');
}
}
function assertEqual(actual, expected, message) {
if (actual !== expected) {
throw new Error(`${message || 'Assertion failed'}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
}
}
function assertContains(str, substring, message) {
if (!str || !str.includes(substring)) {
throw new Error(`${message || 'Assertion failed'}: expected "${str}" to contain "${substring}"`);
}
}
function assertMatches(str, regex, message) {
if (!str || !regex.test(str)) {
throw new Error(`${message || 'Assertion failed'}: expected "${str}" to match ${regex}`);
}
}
async function runTests() {
console.log('\n========================================');
console.log(' Testing v0.0.5 run_command Feature');
console.log('========================================\n');
// Test 1: ACTION_TYPES includes RUN_COMMAND
test('ACTION_TYPES.RUN_COMMAND exists', () => {
assert(ACTION_TYPES.RUN_COMMAND !== undefined, 'RUN_COMMAND should be defined');
assertEqual(ACTION_TYPES.RUN_COMMAND, 'run_command', 'RUN_COMMAND should equal "run_command"');
});
// Test 2: isCommandDangerous function exists
test('isCommandDangerous function exists', () => {
assert(typeof isCommandDangerous === 'function', 'isCommandDangerous should be a function');
});
// Test 3: DANGEROUS_COMMAND_PATTERNS is defined
test('DANGEROUS_COMMAND_PATTERNS is defined', () => {
assert(Array.isArray(DANGEROUS_COMMAND_PATTERNS), 'DANGEROUS_COMMAND_PATTERNS should be an array');
assert(DANGEROUS_COMMAND_PATTERNS.length > 0, 'Should have dangerous patterns defined');
});
// Test 4: Safe command detection
test('Safe commands are not flagged as dangerous', () => {
const safeCommands = ['dir', 'echo hello', 'Get-Process', 'pwd', 'ls -la'];
for (const cmd of safeCommands) {
const result = isCommandDangerous(cmd);
assert(!result, `"${cmd}" should not be dangerous`);
}
});
// Test 5: Dangerous command detection
test('Dangerous commands ARE flagged', () => {
const dangerousCommands = [
'rm -rf /tmp', // Has -rf flag followed by path
'del /s /q C:\\temp', // Windows delete with flags
'format C:', // Format drive (should be caught)
'format D:', // Format another drive
'Remove-Item -Recurse -Force C:\\temp', // PowerShell destructive
'shutdown /s', // Shutdown
];
for (const cmd of dangerousCommands) {
const result = isCommandDangerous(cmd);
assert(result === true, `"${cmd}" SHOULD be flagged as dangerous (got: ${result})`);
}
});
// Test 5b: Format-Table should NOT be flagged (false positive fix)
test('Format-Table is NOT flagged as dangerous', () => {
const safeFormatCommands = [
'Get-ChildItem | Format-Table',
'Get-Process | Format-Table Name, CPU -AutoSize',
'Get-Service | Format-Table -Property Name, Status',
];
for (const cmd of safeFormatCommands) {
const result = isCommandDangerous(cmd);
assert(result === false, `"${cmd}" should NOT be flagged as dangerous (got: ${result})`);
}
});
// Test 6: truncateOutput function
test('truncateOutput function works correctly', () => {
const shortText = 'Hello World';
assertEqual(truncateOutput(shortText, 100), shortText, 'Short text should not be truncated');
const longText = 'A'.repeat(5000);
const truncated = truncateOutput(longText, 100);
assert(truncated.length < longText.length, 'Long text should be truncated');
assertMatches(truncated, /characters truncated/, 'Should contain truncation notice');
});
// Test 7: Execute simple command (PowerShell)
await testAsync('Execute simple PowerShell command', async () => {
const action = {
type: 'run_command',
command: 'Write-Output "Hello from Liku v0.0.5"'
};
const result = await executeAction(action);
assert(result.success, `Command should succeed: ${result.error || result.stderr || ''}`);
assertContains(result.stdout || '', 'Hello from Liku', 'Output should contain expected text');
});
// Test 8: Execute command with cwd
await testAsync('Execute command with custom cwd', async () => {
const action = {
type: 'run_command',
command: 'Get-Location | Select-Object -ExpandProperty Path',
cwd: process.env.USERPROFILE || 'C:\\Users'
};
const result = await executeAction(action);
assert(result.success, `Command should succeed: ${result.error || ''}`);
});
// Test 9: Execute command that produces output
await testAsync('Execute command that lists files', async () => {
const action = {
type: 'run_command',
command: 'Get-ChildItem | Select-Object -First 3 | ForEach-Object { $_.Name }'
};
const result = await executeAction(action);
assert(result.success, `Command should succeed: ${result.error || ''}`);
assert(result.stdout && result.stdout.length > 0, `Should have output, got: "${result.stdout}"`);
});
// Test 10: Execute command with cmd shell
await testAsync('Execute command with cmd shell', async () => {
const action = {
type: 'run_command',
command: 'echo Liku CMD Test',
shell: 'cmd'
};
const result = await executeAction(action);
assert(result.success, `Command should succeed: ${result.error || ''}`);
assertContains(result.stdout || '', 'Liku CMD Test', 'Output should contain expected text');
});
// Test 11: Invalid command fails gracefully
await testAsync('Invalid command fails gracefully', async () => {
const action = {
type: 'run_command',
command: 'this-command-definitely-does-not-exist-12345'
};
const result = await executeAction(action);
// Should not crash, should return error info
assert(result !== undefined, 'Should return a result object');
// It's ok if success is false, we just want no crash
});
// Print summary
console.log('\n========================================');
console.log(' Test Summary');
console.log('========================================');
console.log(` Total: ${results.passed + results.failed}`);
console.log(` Passed: ${results.passed}`);
console.log(` Failed: ${results.failed}`);
console.log('========================================\n');
// Return exit code
process.exit(results.failed > 0 ? 1 : 0);
}
// Run tests
runTests().catch(err => {
console.error('Test runner error:', err);
process.exit(1);
});