152 lines
4.1 KiB
JavaScript
152 lines
4.1 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Tests for UserPromptSubmit hook
|
|
* Uses only Node.js built-in modules
|
|
*/
|
|
|
|
const path = require('path');
|
|
const {
|
|
mockClaudeCodeEnv,
|
|
createTestFile,
|
|
cleanup,
|
|
runHook,
|
|
assertTrue,
|
|
assertEqual
|
|
} = require('./test-helpers');
|
|
|
|
const hookPath = path.join(__dirname, '..', 'user-prompt-submit.js');
|
|
|
|
async function testContextLoading() {
|
|
console.log('Testing context loading from active story...');
|
|
|
|
const testDir = '.test-stories';
|
|
const storyPath = path.join(testDir, 'STORY-001.md');
|
|
const storyContent = `# Story 001
|
|
|
|
## Status
|
|
In Progress
|
|
|
|
## Acceptance Criteria
|
|
1. Must load context automatically
|
|
2. Must filter long content
|
|
|
|
## Dev Notes
|
|
Critical: Use only built-in modules`;
|
|
|
|
try {
|
|
await createTestFile(storyPath, storyContent);
|
|
|
|
const env = mockClaudeCodeEnv({
|
|
CLAUDE_CODE_PROMPT: 'implement the feature'
|
|
});
|
|
|
|
const result = await runHook(hookPath, env);
|
|
|
|
assertTrue(result.success, 'Hook should execute successfully');
|
|
assertTrue(result.output.approve, 'Hook should approve');
|
|
assertTrue(result.output.messages.length > 0, 'Should include context messages');
|
|
|
|
const contextMessage = result.output.messages[0].content;
|
|
assertTrue(contextMessage.includes('Acceptance Criteria'), 'Should include acceptance criteria');
|
|
assertTrue(contextMessage.includes('Dev Notes'), 'Should include dev notes');
|
|
|
|
console.log('✓ Context loading test passed');
|
|
} finally {
|
|
await cleanup([testDir]);
|
|
}
|
|
}
|
|
|
|
async function testQualityReminders() {
|
|
console.log('Testing quality reminders for implementation keywords...');
|
|
|
|
const env = mockClaudeCodeEnv({
|
|
CLAUDE_CODE_PROMPT: 'implement new feature'
|
|
});
|
|
|
|
const result = await runHook(hookPath, env);
|
|
|
|
assertTrue(result.success, 'Hook should execute successfully');
|
|
|
|
const hasQualityReminder = result.output.messages.some(msg =>
|
|
msg.content.includes('BMAD Quality Reminder')
|
|
);
|
|
|
|
assertTrue(hasQualityReminder, 'Should include quality reminder for implementation');
|
|
|
|
console.log('✓ Quality reminders test passed');
|
|
}
|
|
|
|
async function testNoActiveStory() {
|
|
console.log('Testing behavior with no active story...');
|
|
|
|
const env = mockClaudeCodeEnv({
|
|
CLAUDE_CODE_PROMPT: 'check something'
|
|
});
|
|
|
|
const result = await runHook(hookPath, env);
|
|
|
|
assertTrue(result.success, 'Hook should execute successfully');
|
|
assertTrue(result.output.approve, 'Hook should approve');
|
|
assertEqual(result.output.messages.length, 0, 'Should have no messages without active story');
|
|
|
|
console.log('✓ No active story test passed');
|
|
}
|
|
|
|
async function testPerformance() {
|
|
console.log('Testing performance with multiple story files...');
|
|
|
|
const testDir = '.test-perf-stories';
|
|
const stories = [];
|
|
|
|
try {
|
|
// Create multiple story files
|
|
for (let i = 0; i < 10; i++) {
|
|
const storyPath = path.join(testDir, `STORY-${i.toString().padStart(3, '0')}.md`);
|
|
const content = `# Story ${i}\n\n## Status\n${i === 5 ? 'In Progress' : 'Draft'}`;
|
|
await createTestFile(storyPath, content);
|
|
stories.push(storyPath);
|
|
}
|
|
|
|
const startTime = Date.now();
|
|
const result = await runHook(hookPath, mockClaudeCodeEnv());
|
|
const duration = Date.now() - startTime;
|
|
|
|
assertTrue(result.success, 'Hook should execute successfully');
|
|
assertTrue(duration < 500, `Performance should be under 500ms (was ${duration}ms)`);
|
|
|
|
console.log(`✓ Performance test passed (${duration}ms)`);
|
|
} finally {
|
|
await cleanup([testDir]);
|
|
}
|
|
}
|
|
|
|
// Run all tests
|
|
async function runTests() {
|
|
console.log('Running UserPromptSubmit hook tests...\n');
|
|
|
|
const tests = [
|
|
testContextLoading,
|
|
testQualityReminders,
|
|
testNoActiveStory,
|
|
testPerformance
|
|
];
|
|
|
|
let passed = 0;
|
|
let failed = 0;
|
|
|
|
for (const test of tests) {
|
|
try {
|
|
await test();
|
|
passed++;
|
|
} catch (error) {
|
|
failed++;
|
|
console.error(`✗ ${test.name} failed:`, error.message);
|
|
}
|
|
}
|
|
|
|
console.log(`\nTests completed: ${passed} passed, ${failed} failed`);
|
|
process.exit(failed > 0 ? 1 : 0);
|
|
}
|
|
|
|
runTests(); |