Skip to content

Commit d3c70a5

Browse files
bugerclaude
andcommitted
fix: add missing test files for GitHub Actions
- Add test-afk-command-parsing.js - Update test-runtime.js These files were created locally but not committed. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 9ffea24 commit d3c70a5

2 files changed

Lines changed: 259 additions & 0 deletions

File tree

test/test-afk-command-parsing.js

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
#!/usr/bin/env node
2+
// Simple unit tests for AFK command parsing logic
3+
4+
const assert = require('assert');
5+
const path = require('path');
6+
const fs = require('fs');
7+
const os = require('os');
8+
9+
let testCounter = 0;
10+
let failures = [];
11+
12+
function runTest(name, testFn) {
13+
try {
14+
testCounter++;
15+
testFn();
16+
console.log(`✅ ${testCounter}. ${name}`);
17+
return true;
18+
} catch (error) {
19+
failures.push({ name, error: error.message });
20+
console.log(`❌ ${testCounter}. ${name}: ${error.message}`);
21+
return false;
22+
}
23+
}
24+
25+
// Test the command parsing logic in isolation
26+
function testCommandParsingLogic() {
27+
// This mimics the parsing logic from handleUserPromptSubmit
28+
function parseAfkCommand(prompt) {
29+
if (!prompt.startsWith('/afk')) {
30+
return null; // Not an AFK command
31+
}
32+
33+
let subcommand, args;
34+
35+
if (prompt.includes(':')) {
36+
// New format: /afk:on
37+
const colonIndex = prompt.indexOf(':');
38+
const afterColon = prompt.slice(colonIndex + 1);
39+
const parts = afterColon.split(/\s+/);
40+
subcommand = parts[0] || 'toggle';
41+
args = parts.slice(1).join(' ');
42+
} else {
43+
// Old format: /afk on
44+
const parts = prompt.slice(1).split(/\s+/); // Remove leading '/'
45+
subcommand = parts[1] || 'toggle';
46+
args = parts.slice(2).join(' ');
47+
}
48+
49+
return { subcommand, args };
50+
}
51+
52+
runTest('Parse /afk:on command', () => {
53+
const result = parseAfkCommand('/afk:on');
54+
assert.strictEqual(result.subcommand, 'on');
55+
assert.strictEqual(result.args, '');
56+
});
57+
58+
runTest('Parse /afk:off command', () => {
59+
const result = parseAfkCommand('/afk:off');
60+
assert.strictEqual(result.subcommand, 'off');
61+
assert.strictEqual(result.args, '');
62+
});
63+
64+
runTest('Parse /afk:toggle command', () => {
65+
const result = parseAfkCommand('/afk:toggle');
66+
assert.strictEqual(result.subcommand, 'toggle');
67+
assert.strictEqual(result.args, '');
68+
});
69+
70+
runTest('Parse /afk:status command', () => {
71+
const result = parseAfkCommand('/afk:status');
72+
assert.strictEqual(result.subcommand, 'status');
73+
assert.strictEqual(result.args, '');
74+
});
75+
76+
runTest('Parse /afk:global on command', () => {
77+
const result = parseAfkCommand('/afk:global on');
78+
assert.strictEqual(result.subcommand, 'global');
79+
assert.strictEqual(result.args, 'on');
80+
});
81+
82+
runTest('Parse /afk:global off command', () => {
83+
const result = parseAfkCommand('/afk:global off');
84+
assert.strictEqual(result.args, 'off');
85+
});
86+
87+
runTest('Parse /afk:project clear command', () => {
88+
const result = parseAfkCommand('/afk:project clear');
89+
assert.strictEqual(result.subcommand, 'project');
90+
assert.strictEqual(result.args, 'clear');
91+
});
92+
93+
runTest('Parse legacy /afk on command', () => {
94+
const result = parseAfkCommand('/afk on');
95+
assert.strictEqual(result.subcommand, 'on');
96+
assert.strictEqual(result.args, '');
97+
});
98+
99+
runTest('Parse legacy /afk off command', () => {
100+
const result = parseAfkCommand('/afk off');
101+
assert.strictEqual(result.subcommand, 'off');
102+
assert.strictEqual(result.args, '');
103+
});
104+
105+
runTest('Parse legacy /afk toggle command', () => {
106+
const result = parseAfkCommand('/afk toggle');
107+
assert.strictEqual(result.subcommand, 'toggle');
108+
assert.strictEqual(result.args, '');
109+
});
110+
111+
runTest('Parse legacy /afk global on command', () => {
112+
const result = parseAfkCommand('/afk global on');
113+
assert.strictEqual(result.subcommand, 'global');
114+
assert.strictEqual(result.args, 'on');
115+
});
116+
117+
runTest('Parse /afk with no subcommand (should default to toggle)', () => {
118+
const result = parseAfkCommand('/afk');
119+
assert.strictEqual(result.subcommand, 'toggle');
120+
assert.strictEqual(result.args, '');
121+
});
122+
123+
runTest('Parse /afk: with empty colon (should default to toggle)', () => {
124+
const result = parseAfkCommand('/afk:');
125+
assert.strictEqual(result.subcommand, 'toggle');
126+
assert.strictEqual(result.args, '');
127+
});
128+
129+
runTest('Non-AFK command returns null', () => {
130+
const result = parseAfkCommand('/help');
131+
assert.strictEqual(result, null);
132+
});
133+
134+
runTest('Regular text returns null', () => {
135+
const result = parseAfkCommand('hello world');
136+
assert.strictEqual(result, null);
137+
});
138+
139+
runTest('Empty command returns null', () => {
140+
const result = parseAfkCommand('');
141+
assert.strictEqual(result, null);
142+
});
143+
}
144+
145+
// Test that the subcommand structure matches the expected values
146+
function testSubcommandValidation() {
147+
const validSubcommands = ['on', 'off', 'toggle', 'status', 'clear', 'global', 'project', 'help'];
148+
const validGlobalArgs = ['on', 'off', 'toggle'];
149+
const validProjectArgs = ['on', 'off', 'clear'];
150+
151+
runTest('Valid basic subcommands', () => {
152+
['on', 'off', 'toggle', 'status', 'clear', 'help'].forEach(cmd => {
153+
assert(validSubcommands.includes(cmd), `${cmd} should be a valid subcommand`);
154+
});
155+
});
156+
157+
runTest('Valid global arguments', () => {
158+
['on', 'off', 'toggle'].forEach(arg => {
159+
assert(validGlobalArgs.includes(arg), `${arg} should be a valid global argument`);
160+
});
161+
});
162+
163+
runTest('Valid project arguments', () => {
164+
['on', 'off', 'clear'].forEach(arg => {
165+
assert(validProjectArgs.includes(arg), `${arg} should be a valid project argument`);
166+
});
167+
});
168+
}
169+
170+
// Test message formatting expectations
171+
function testMessageFormatting() {
172+
// Test that we can create expected messages for different commands
173+
function createExpectedMessage(subcommand, args) {
174+
switch (subcommand) {
175+
case 'on':
176+
return 'Remote mode enabled for this session';
177+
case 'off':
178+
return 'Remote mode disabled for this session';
179+
case 'status':
180+
return 'AFK Mode Status';
181+
case 'help':
182+
return 'AFK Commands Help';
183+
case 'clear':
184+
return 'Session AFK mode override cleared';
185+
case 'global':
186+
if (args === 'on') return 'Global AFK mode enabled';
187+
if (args === 'off') return 'Global AFK mode disabled';
188+
return 'Global AFK mode is currently';
189+
case 'project':
190+
if (args === 'on') return 'Project AFK mode enabled';
191+
if (args === 'off') return 'Project AFK mode disabled';
192+
if (args === 'clear') return 'Project AFK mode override cleared';
193+
return 'Project AFK mode';
194+
default:
195+
return 'Unknown AFK command';
196+
}
197+
}
198+
199+
runTest('Expected messages for basic commands', () => {
200+
assert(createExpectedMessage('on', '').includes('enabled'));
201+
assert(createExpectedMessage('off', '').includes('disabled'));
202+
assert(createExpectedMessage('status', '').includes('Status'));
203+
assert(createExpectedMessage('help', '').includes('Help'));
204+
});
205+
206+
runTest('Expected messages for global commands', () => {
207+
assert(createExpectedMessage('global', 'on').includes('enabled'));
208+
assert(createExpectedMessage('global', 'off').includes('disabled'));
209+
assert(createExpectedMessage('global', '').includes('currently'));
210+
});
211+
212+
runTest('Expected messages for project commands', () => {
213+
assert(createExpectedMessage('project', 'on').includes('enabled'));
214+
assert(createExpectedMessage('project', 'off').includes('disabled'));
215+
assert(createExpectedMessage('project', 'clear').includes('cleared'));
216+
});
217+
}
218+
219+
// Main test runner
220+
function runAllTests() {
221+
console.log('🧪 Running AFK Command Parsing Unit Tests\n');
222+
223+
testCommandParsingLogic();
224+
testSubcommandValidation();
225+
testMessageFormatting();
226+
227+
console.log(`\n📊 Results: ${testCounter - failures.length}/${testCounter} tests passed`);
228+
229+
if (failures.length > 0) {
230+
console.log('\n❌ Failed tests:');
231+
failures.forEach(failure => {
232+
console.log(` • ${failure.name}: ${failure.error}`);
233+
});
234+
process.exit(1);
235+
}
236+
237+
console.log('\n✅ All command parsing tests passed!');
238+
}
239+
240+
// Run tests if this file is executed directly
241+
if (require.main === module) {
242+
runAllTests();
243+
}
244+
245+
module.exports = {
246+
runAllTests
247+
};

test/test-runtime.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,18 @@ async function testFunctionDefinitions() {
139139
definedFunctions.add(name);
140140
});
141141

142+
// Add imported functions from require statements
143+
const requireImports = afkSource.match(/const\s*{\s*([^}]+)\s*}\s*=\s*require\(/g) || [];
144+
requireImports.forEach(importStmt => {
145+
const match = importStmt.match(/const\s*{\s*([^}]+)\s*}/);
146+
if (match) {
147+
const imports = match[1].split(',').map(s => s.trim());
148+
imports.forEach(importName => {
149+
definedFunctions.add(importName);
150+
});
151+
}
152+
});
153+
142154
// Add Node.js built-ins and known globals
143155
const builtins = new Set([
144156
'require', 'process', 'console', 'setTimeout', 'clearTimeout',

0 commit comments

Comments
 (0)