UNPKG

ctrlshiftleft

Version:

AI-powered toolkit for embedding QA and security testing into development workflows

268 lines (231 loc) 8.1 kB
/** * Comprehensive integration test for ctrl.shift.left functionality * Tests all major components and their interactions */ const fs = require('fs').promises; const path = require('path'); const chalk = require('chalk'); const { execSync } = require('child_process'); // Import core components const { TestGenerator } = require('../../dist/core/testGenerator'); const { TestRunner } = require('../../dist/core/testRunner'); const { ChecklistGenerator } = require('../../dist/core/checklistGenerator'); const { diff, highlightSecurityChanges } = require('../../dist/utils/diffUtils'); const { generateSecurityRiskReport, formatSeverityBadge } = require('../../dist/utils/securityRiskUtils'); // Test configuration const TEST_FILE_PATH = path.join(__dirname, '../demo-security/LoginComponent.tsx'); const TEST_OUTPUT_DIR = path.join(__dirname, '../output'); // Test execution state const results = { pass: 0, fail: 0, tests: [] }; // Utility functions async function ensureDirectoryExists(dir) { try { await fs.mkdir(dir, { recursive: true }); } catch (err) { console.error(`Error creating directory ${dir}:`, err); } } function testCase(name, fn) { return { name, fn }; } async function runTest(test) { console.log(`\n${chalk.blue('Running test:')} ${test.name}`); try { await test.fn(); console.log(`${chalk.green('✓')} ${test.name}`); results.pass++; results.tests.push({ name: test.name, passed: true }); return true; } catch (err) { console.error(`${chalk.red('✗')} ${test.name}`); console.error(` ${chalk.red('Error:')} ${err.message}`); results.fail++; results.tests.push({ name: test.name, passed: false, error: err.message }); return false; } } // Define test suite const tests = [ testCase('Build checks - TypeScript compilation', async () => { try { execSync('npx tsc --noEmit', { cwd: path.join(__dirname, '../../') }); } catch (err) { throw new Error(`TypeScript compilation failed: ${err.message}`); } }), testCase('TestGenerator - Can instantiate', async () => { // Mock the OpenAI dependency for testing const originalEnv = process.env.OPENAI_API_KEY; process.env.OPENAI_API_KEY = 'mock-api-key-for-testing'; try { const generator = new TestGenerator({ format: 'playwright', timeout: 60 }); if (!generator) { throw new Error('Failed to create TestGenerator instance'); } } finally { // Restore original environment process.env.OPENAI_API_KEY = originalEnv; } }), testCase('TestRunner - Can instantiate', async () => { const runner = new TestRunner({ browser: 'chromium', headless: true, timeout: 30, reporter: 'list', workers: 1 }); if (!runner) { throw new Error('Failed to create TestRunner instance'); } }), testCase('ChecklistGenerator - Can instantiate', async () => { // Mock the OpenAI dependency for testing const originalEnv = process.env.OPENAI_API_KEY; process.env.OPENAI_API_KEY = 'mock-api-key-for-testing'; try { const generator = new ChecklistGenerator(); if (!generator) { throw new Error('Failed to create ChecklistGenerator instance'); } } finally { // Restore original environment process.env.OPENAI_API_KEY = originalEnv; } }), testCase('diffUtils - Generates correct diff', async () => { const oldCode = "function test() {\n return 123;\n}"; const newCode = "function test() {\n return 456;\n}"; const changes = diff(oldCode, newCode); if (!Array.isArray(changes)) { throw new Error('diff should return an array'); } if (changes.length < 1) { throw new Error('diff should detect changes between different strings'); } const hasAddedChange = changes.some(c => c.added); const hasRemovedChange = changes.some(c => c.removed); if (!hasAddedChange || !hasRemovedChange) { throw new Error('diff should identify added and removed content'); } }), testCase('securityRiskUtils - Formats severity correctly', async () => { const critical = formatSeverityBadge('critical'); const high = formatSeverityBadge('high'); const medium = formatSeverityBadge('medium'); const low = formatSeverityBadge('low'); if (!critical.includes('CRITICAL') || !high.includes('HIGH') || !medium.includes('MEDIUM') || !low.includes('LOW')) { throw new Error('formatSeverityBadge should format severity levels correctly'); } }), testCase('securityRiskUtils - Generates risk report', async () => { const mockItems = [ { id: 'SEC-001', title: 'Test Issue', description: 'Test description', category: 'Security', severity: 'high', riskScore: { score: 7.5, impact: 'high', likelihood: 'medium' } } ]; const report = generateSecurityRiskReport(mockItems); if (!report || typeof report !== 'string' || !report.includes('SECURITY RISK REPORT')) { throw new Error('generateSecurityRiskReport should return a formatted report string'); } }), testCase('End-to-end test of security checklist generation - mock mode', async () => { await ensureDirectoryExists(TEST_OUTPUT_DIR); // Instead of using real OpenAI, create a mock result for testing const mockResult = { items: [ { id: 'SEC-001', title: 'Test Security Issue', description: 'This is a mock security issue for testing', category: 'Security', severity: 'high', status: 'failed', file: TEST_FILE_PATH, riskScore: { score: 7.5, impact: 'high', likelihood: 'medium' } } ], itemCount: 1, categories: ['Security'], file: path.join(TEST_OUTPUT_DIR, 'mock-checklist.json') }; // Write the mock result to a file to simulate the full process await fs.writeFile( mockResult.file, JSON.stringify(mockResult, null, 2) ); console.log(` Generated mock checklist with ${mockResult.items.length} items`); console.log(` Output file: ${mockResult.file}`); }) ]; // Integration test for CLI commands const cliTests = [ testCase('CLI help command works', async () => { try { const output = execSync('node ../../dist/cli.js --help', { cwd: __dirname }).toString(); if (!output.includes('Usage:') || !output.includes('Commands:')) { throw new Error('CLI help output does not contain expected content'); } } catch (err) { throw new Error(`CLI help command failed: ${err.message}`); } }), testCase('CLI version command works', async () => { try { execSync('node ../../dist/cli.js --version', { cwd: __dirname }); } catch (err) { throw new Error(`CLI version command failed: ${err.message}`); } }) ]; // Run all tests async function runTests() { console.log(chalk.blue.bold('\n===== CTRL.SHIFT.LEFT INTEGRATION TESTS =====\n')); console.log(chalk.blue('Core Component Tests:')); for (const test of tests) { await runTest(test); } console.log(chalk.blue('\nCLI Command Tests:')); for (const test of cliTests) { await runTest(test); } // Print summary console.log('\n===== TEST RESULTS ====='); console.log(`${chalk.green('Passed:')} ${results.pass}/${results.pass + results.fail}`); console.log(`${chalk.red('Failed:')} ${results.fail}/${results.pass + results.fail}`); if (results.fail > 0) { console.log('\n===== FAILED TESTS ====='); results.tests .filter(t => !t.passed) .forEach(t => { console.log(`${chalk.red('✗')} ${t.name}`); console.log(` ${chalk.red('Error:')} ${t.error}`); }); } console.log('\n===== END OF TEST REPORT ====='); } runTests().catch(err => { console.error('Test runner error:', err); process.exit(1); });