agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
290 lines (243 loc) • 10.8 kB
JavaScript
/**
* @file Unit tests for issue factory utility
* @description Tests issue creation, validation, and standardization across analysis modules
*/
const {
createIssue,
createSecurityIssue,
createPerformanceIssue,
createBugIssue,
validateIssue,
normalizeIssue,
mergeIssues
} = require('./issueFactory');
const qtests = require('qtests');
/**
* Test runner for issue factory
*/
async function runTests() {
console.log('=== Testing Issue Factory Utilities ===');
const results = {
total: 0,
passed: 0
};
// Test createIssue function
results.total++;
try {
const issueData = {
type: 'test_issue',
severity: 'MEDIUM',
category: 'Testing',
file: '/path/to/file.js',
line: 42,
column: 10,
summary: 'Test issue summary',
description: 'Detailed description of the test issue',
recommendation: 'Fix the test issue'
};
const issue = createIssue(issueData);
qtests.assert(typeof issue === 'object', 'createIssue should return object');
qtests.assert(issue.type === 'test_issue', 'Issue should have correct type');
qtests.assert(issue.severity === 'MEDIUM', 'Issue should have correct severity');
qtests.assert(issue.category === 'Testing', 'Issue should have correct category');
qtests.assert(issue.file === '/path/to/file.js', 'Issue should have correct file path');
qtests.assert(issue.line === 42, 'Issue should have correct line number');
qtests.assert(typeof issue.id === 'string', 'Issue should have generated ID');
qtests.assert(typeof issue.timestamp === 'number', 'Issue should have timestamp');
console.log('✓ createIssue correctly creates standardized issue objects');
results.passed++;
} catch (error) {
console.log(`✗ createIssue test failed: ${error.message}`);
}
// Test createSecurityIssue function
results.total++;
try {
const securityData = {
type: 'xss_vulnerability',
confidence: 'HIGH',
cwe: 'CWE-79',
file: '/path/to/vulnerable.js',
line: 15,
pattern: 'innerHTML assignment',
context: 'user input directly assigned to innerHTML'
};
const securityIssue = createSecurityIssue(securityData);
qtests.assert(securityIssue.category === 'Security', 'Security issue should have Security category');
qtests.assert(securityIssue.confidence === 'HIGH', 'Security issue should preserve confidence');
qtests.assert(securityIssue.cwe === 'CWE-79', 'Security issue should preserve CWE identifier');
qtests.assert(securityIssue.pattern === 'innerHTML assignment', 'Security issue should preserve pattern');
qtests.assert(typeof securityIssue.riskLevel === 'string', 'Security issue should calculate risk level');
console.log('✓ createSecurityIssue correctly creates security-specific issues');
results.passed++;
} catch (error) {
console.log(`✗ createSecurityIssue test failed: ${error.message}`);
}
// Test createPerformanceIssue function
results.total++;
try {
const performanceData = {
type: 'o_n_squared',
impact: 8,
effort: 3,
file: '/path/to/slow.js',
line: 25,
algorithm: 'nested loop',
complexity: 'O(n²)',
suggestion: 'Use Map for O(1) lookup'
};
const performanceIssue = createPerformanceIssue(performanceData);
qtests.assert(performanceIssue.category === 'Performance', 'Performance issue should have Performance category');
qtests.assert(performanceIssue.impact === 8, 'Performance issue should preserve impact score');
qtests.assert(performanceIssue.effort === 3, 'Performance issue should preserve effort level');
qtests.assert(performanceIssue.algorithm === 'nested loop', 'Performance issue should preserve algorithm type');
qtests.assert(performanceIssue.complexity === 'O(n²)', 'Performance issue should preserve complexity notation');
console.log('✓ createPerformanceIssue correctly creates performance-specific issues');
results.passed++;
} catch (error) {
console.log(`✗ createPerformanceIssue test failed: ${error.message}`);
}
// Test createBugIssue function
results.total++;
try {
const bugData = {
type: 'null_reference',
severity: 'HIGH',
file: '/path/to/buggy.js',
line: 33,
variable: 'user',
operator: '.',
errorType: 'TypeError',
fix: 'Add null check before property access'
};
const bugIssue = createBugIssue(bugData);
qtests.assert(bugIssue.category === 'Bug', 'Bug issue should have Bug category');
qtests.assert(bugIssue.variable === 'user', 'Bug issue should preserve variable name');
qtests.assert(bugIssue.operator === '.', 'Bug issue should preserve operator');
qtests.assert(bugIssue.errorType === 'TypeError', 'Bug issue should preserve error type');
qtests.assert(bugIssue.fix === 'Add null check before property access', 'Bug issue should preserve fix suggestion');
console.log('✓ createBugIssue correctly creates bug-specific issues');
results.passed++;
} catch (error) {
console.log(`✗ createBugIssue test failed: ${error.message}`);
}
// Test validateIssue function
results.total++;
try {
const validIssue = {
type: 'test_issue',
severity: 'MEDIUM',
category: 'Testing',
file: '/path/to/file.js',
summary: 'Valid issue'
};
const invalidIssue = {
// Missing required fields
type: 'incomplete_issue'
};
const malformedIssue = {
type: 'malformed',
severity: 'INVALID_SEVERITY',
category: 'Testing',
file: '/path/to/file.js',
line: 'not_a_number'
};
const validResult = validateIssue(validIssue);
const invalidResult = validateIssue(invalidIssue);
const malformedResult = validateIssue(malformedIssue);
qtests.assert(validResult.isValid === true, 'validateIssue should accept valid issues');
qtests.assert(validResult.errors.length === 0, 'Valid issues should have no errors');
qtests.assert(invalidResult.isValid === false, 'validateIssue should reject incomplete issues');
qtests.assert(invalidResult.errors.length > 0, 'Invalid issues should have errors');
qtests.assert(malformedResult.isValid === false, 'validateIssue should reject malformed issues');
qtests.assert(malformedResult.errors.some(e => e.includes('severity')), 'Should validate severity values');
console.log('✓ validateIssue correctly validates issue structure and content');
results.passed++;
} catch (error) {
console.log(`✗ validateIssue test failed: ${error.message}`);
}
// Test normalizeIssue function
results.total++;
try {
const unnormalizedIssue = {
type: 'Test_Issue',
severity: 'medium',
category: 'TESTING',
file: '\\path\\to\\file.js',
summary: ' Extra whitespace ',
description: '\n\nMultiple newlines\n\n'
};
const normalizedIssue = normalizeIssue(unnormalizedIssue);
qtests.assert(normalizedIssue.type === 'test_issue', 'Should normalize type to lowercase with underscores');
qtests.assert(normalizedIssue.severity === 'MEDIUM', 'Should normalize severity to uppercase');
qtests.assert(normalizedIssue.category === 'Testing', 'Should normalize category to title case');
qtests.assert(normalizedIssue.file.includes('/'), 'Should normalize file path separators');
qtests.assert(normalizedIssue.summary === 'Extra whitespace', 'Should trim whitespace from summary');
qtests.assert(!normalizedIssue.description.startsWith('\n'), 'Should normalize description formatting');
console.log('✓ normalizeIssue correctly normalizes issue properties');
results.passed++;
} catch (error) {
console.log(`✗ normalizeIssue test failed: ${error.message}`);
}
// Test mergeIssues function
results.total++;
try {
const issues1 = [
{ id: '1', type: 'issue_a', severity: 'HIGH' },
{ id: '2', type: 'issue_b', severity: 'MEDIUM' }
];
const issues2 = [
{ id: '3', type: 'issue_c', severity: 'LOW' },
{ id: '4', type: 'issue_d', severity: 'HIGH' }
];
const issues3 = [
{ id: '5', type: 'issue_e', severity: 'MEDIUM' }
];
const mergedIssues = mergeIssues([issues1, issues2, issues3]);
qtests.assert(Array.isArray(mergedIssues), 'mergeIssues should return array');
qtests.assert(mergedIssues.length === 5, 'Should merge all issues from all arrays');
qtests.assert(mergedIssues.every(issue => typeof issue.id === 'string'), 'All merged issues should have IDs');
// Check that issues are sorted by severity (HIGH first)
const highSeverityIssues = mergedIssues.filter(issue => issue.severity === 'HIGH');
qtests.assert(highSeverityIssues.length === 2, 'Should preserve all HIGH severity issues');
console.log('✓ mergeIssues correctly merges and sorts issue arrays');
results.passed++;
} catch (error) {
console.log(`✗ mergeIssues test failed: ${error.message}`);
}
// Test issue ID generation uniqueness
results.total++;
try {
const issue1 = createIssue({ type: 'test', severity: 'LOW', category: 'Test' });
const issue2 = createIssue({ type: 'test', severity: 'LOW', category: 'Test' });
const issue3 = createIssue({ type: 'test', severity: 'LOW', category: 'Test' });
qtests.assert(issue1.id !== issue2.id, 'Issue IDs should be unique');
qtests.assert(issue1.id !== issue3.id, 'Issue IDs should be unique');
qtests.assert(issue2.id !== issue3.id, 'Issue IDs should be unique');
// Test that IDs are deterministic for same content
const contentBasedIssue1 = createIssue({
type: 'deterministic',
file: '/same/file.js',
line: 10,
severity: 'MEDIUM',
category: 'Test'
});
const contentBasedIssue2 = createIssue({
type: 'deterministic',
file: '/same/file.js',
line: 10,
severity: 'MEDIUM',
category: 'Test'
});
// IDs should be the same for identical content (to avoid duplicates)
qtests.assert(contentBasedIssue1.id === contentBasedIssue2.id, 'Issues with identical content should have same ID');
console.log('✓ Issue ID generation works correctly for uniqueness and deduplication');
results.passed++;
} catch (error) {
console.log(`✗ Issue ID generation test failed: ${error.message}`);
}
console.log(`=== Issue Factory Test Results ===`);
console.log(`Tests passed: ${results.passed}/${results.total}`);
console.log(`Success rate: ${((results.passed / results.total) * 100).toFixed(1)}%`);
return results;
}
module.exports = { runTests };