/** * Test script for PolicyValidator * * This script tests the policy validator against various scenarios: * 1. Good PR - should PASS * 2. Security violations - should BLOCK * 3. Missing migration rollback - should BLOCK * 4. Missing ticket - should BLOCK * 5. Poor PR structure - should WARN/BLOCK */ import * as fs from 'fs'; import * as path from 'path'; import { PolicyValidator } from '../src/policy-validator'; import { parsePolicyV2 } from '@nihal1983/core'; import { PRContext, FileChange } from '../src/types'; // Load CODE-POLICY.md const policyPath = path.join(__dirname, 'CODE-POLICY.md'); const policyContent = fs.readFileSync(policyPath, 'utf-8'); const parseResult = parsePolicyV2(policyContent); if (!parseResult.success) { console.error('โŒ Failed to parse policy:'); console.error(parseResult.errors); process.exit(1); } console.log('โœ… Policy loaded successfully\n'); const policy = parseResult.policy!; // Create validator const validator = new PolicyValidator(policy); // Helper to read file content function readTestFile(relativePath: string): string { const filePath = path.join(__dirname, relativePath); return fs.readFileSync(filePath, 'utf-8'); } // Helper to print results function printResults(scenarioName: string, result: any) { console.log(`\n${'='.repeat(60)}`); console.log(`SCENARIO: ${scenarioName}`); console.log(`${'='.repeat(60)}`); console.log(`Status: ${result.status}`); console.log(`Violations: ${result.violations.length}`); console.log(validator.getSummary(result)); if (result.violations.length > 0) { console.log('\nViolations:'); result.violations.forEach((v: any, i: number) => { console.log(`\n${i + 1}. [${v.severity.toUpperCase()}] ${v.ruleId}`); console.log(` Message: ${v.message}`); if (v.location) console.log(` Location: ${v.location}`); if (v.suggestion) console.log(` Suggestion: ${v.suggestion}`); if (v.reference) console.log(` Reference: ${v.reference}`); }); } console.log(`\n${'='.repeat(60)}\n`); } // ========================================== // SCENARIO 1: Good PR - should PASS // ========================================== async function testGoodPR() { const context: PRContext = { pr: { number: 123, title: 'feat: add user authentication', description: ` ## What Changed Added JWT-based user authentication with secure password hashing. ## Why Need secure user authentication for the API. Jira ticket: PB-456 ## Testing - Unit tests for auth middleware - Integration tests for login/logout - Tested password hashing `, branch: 'feature/add-user-auth' }, ticket: { id: '10456', key: 'PB-456', summary: 'Add user authentication', description: 'Implement JWT-based authentication', type: 'Story', status: 'In Progress', priority: 'High', fields: {} }, files: [ { path: 'migrations/001-good-migration.sql', status: 'added' as const, content: readTestFile('migrations/001-good-migration.sql'), additions: 15, deletions: 0 } ] }; const result = await validator.validate(context); printResults('Good PR (should PASS)', result); return result; } // ========================================== // SCENARIO 2: Security Violations - should BLOCK // ========================================== async function testSecurityViolations() { const fileContent = readTestFile('bad-search.ts'); const context: PRContext = { pr: { number: 124, title: 'feat: add search endpoint', description: ` ## What Changed Added user search functionality. ## Why Users need to search for other users. Jira: PB-457 ## Testing Manual testing completed. `, branch: 'feature/user-search' }, ticket: { id: '10457', key: 'PB-457', summary: 'Add user search', description: 'Add search endpoint for users', type: 'Story', status: 'In Progress', priority: 'Medium', fields: {} }, files: [ { path: 'src/routes/search.ts', status: 'added' as const, content: fileContent, additions: 25, deletions: 0 } ] }; const result = await validator.validate(context); printResults('Security Violations (should BLOCK)', result); return result; } // ========================================== // SCENARIO 3: Missing Migration Rollback - should BLOCK // ========================================== async function testMissingRollback() { const context: PRContext = { pr: { number: 125, title: 'feat: add login history tracking', description: ` ## What Changed Added login_history table to track user logins. ## Why Need audit trail for security. Jira: PB-458 ## Testing Ran migration locally, works fine. `, branch: 'feature/login-history' }, ticket: { id: '10458', key: 'PB-458', summary: 'Add login history', description: 'Track user login history for audit', type: 'Story', status: 'In Progress', priority: 'Medium', fields: {} }, files: [ { path: 'migrations/002-bad-migration.sql', status: 'added' as const, content: readTestFile('migrations/002-bad-migration.sql'), additions: 12, deletions: 0 } ] }; const result = await validator.validate(context); printResults('Missing Migration Rollback (should BLOCK)', result); return result; } // ========================================== // SCENARIO 4: Missing Ticket - should BLOCK // ========================================== async function testMissingTicket() { const context: PRContext = { pr: { number: 126, title: 'fix: typo in readme', description: ` ## What Changed Fixed a typo in the README file. ## Why The typo was confusing. ## Testing N/A - documentation change only. `, branch: 'fix/readme-typo' }, ticket: null, // No ticket! files: [ { path: 'README.md', status: 'modified' as const, content: '# My Project\n\nFixed typo here.\n', additions: 1, deletions: 1 } ] }; const result = await validator.validate(context); printResults('Missing Ticket (should BLOCK)', result); return result; } // ========================================== // SCENARIO 5: Poor PR Structure - should WARN/BLOCK // ========================================== async function testPoorStructure() { const context: PRContext = { pr: { number: 127, title: 'fix bug', // Too short, bad format description: 'Fixed it. PB-459', // Too short branch: 'my-branch' // Bad branch name }, ticket: { id: '10459', key: 'PB-459', summary: 'Fix bug', description: 'Bug fix', type: 'Bug', status: 'In Progress', priority: 'Low', fields: {} }, files: [ { path: 'src/utils.ts', status: 'modified' as const, content: 'export function foo() { return "bar"; }', additions: 1, deletions: 0 } ] }; const result = await validator.validate(context); printResults('Poor PR Structure (should WARN/BLOCK)', result); return result; } // ========================================== // SCENARIO 6: Performance Ticket Missing Metrics - should BLOCK // ========================================== async function testPerformanceTicketMissingMetrics() { const context: PRContext = { pr: { number: 128, title: 'perf: optimize database queries', description: ` ## What Changed Optimized slow database queries. ## Why Performance improvement. Jira: PB-460 ## Testing Tested locally. `, branch: 'feature/db-optimization' }, ticket: { id: '10460', key: 'PB-460', summary: 'Optimize DB queries', description: 'Improve query performance', type: 'Performance', // Performance type should trigger conditional requirement status: 'In Progress', priority: 'High', fields: {} // Missing baseline_metrics and target_sla! }, files: [ { path: 'src/db/queries.ts', status: 'modified' as const, content: 'export async function getUsers() { /* optimized */ }', additions: 10, deletions: 5 } ] }; const result = await validator.validate(context); printResults('Performance Ticket Missing Metrics (should BLOCK)', result); return result; } // ========================================== // Run all tests // ========================================== async function runAllTests() { console.log('๐Ÿงช Running PolicyValidator Test Suite\n'); try { await testGoodPR(); await testSecurityViolations(); await testMissingRollback(); await testMissingTicket(); await testPoorStructure(); await testPerformanceTicketMissingMetrics(); console.log('\nโœ… All test scenarios completed!\n'); } catch (error) { console.error('\nโŒ Test suite failed:', error); process.exit(1); } } // Run tests runAllTests();