UNPKG

agentsqripts

Version:

Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems

270 lines (212 loc) 9.51 kB
/** * @file Unit tests for project refactoring analyzer * @description Tests project-level export promotion analysis and refactoring recommendations */ const { analyzeProjectRefactoring } = require('./projectRefactoringAnalyzer'); const qtests = require('qtests'); const fs = require('fs'); const path = require('path'); /** * Test runner for project refactoring analyzer */ async function runTests() { console.log('=== Testing Project Refactoring Analyzer ==='); const results = { total: 0, passed: 0 }; // Test analyzeProjectRefactoring with sample project structure results.total++; try { // Create temporary test directory structure const tempDir = path.join(__dirname, 'temp-refactor-test'); const utilsDir = path.join(tempDir, 'utils'); const componentsDir = path.join(tempDir, 'components'); // Create directories fs.mkdirSync(tempDir, { recursive: true }); fs.mkdirSync(utilsDir, { recursive: true }); fs.mkdirSync(componentsDir, { recursive: true }); // Create test files with unexported utilities const utilFile1 = path.join(utilsDir, 'helpers.js'); const utilContent1 = ` // Utility functions that could be exported function formatDate(date) { return date.toISOString().split('T')[0]; } function validateEmail(email) { return email.includes('@') && email.includes('.'); } // This one is already exported export function exportedUtil() { return 'already exported'; } `; const utilFile2 = path.join(utilsDir, 'calculations.js'); const utilContent2 = ` function calculatePercentage(part, whole) { return (part / whole) * 100; } function roundToDecimal(num, places) { return Math.round(num * Math.pow(10, places)) / Math.pow(10, places); } `; const componentFile = path.join(componentsDir, 'Button.js'); const componentContent = ` import React from 'react'; // This is a component, not a utility function Button({ onClick, children }) { return <button onClick={onClick}>{children}</button>; } export default Button; `; fs.writeFileSync(utilFile1, utilContent1); fs.writeFileSync(utilFile2, utilContent2); fs.writeFileSync(componentFile, componentContent); // Analyze the project const results = await analyzeProjectRefactoring(tempDir, { extensions: ['.js'], includeNonUtility: false }); qtests.assert(typeof results === 'object', 'analyzeProjectRefactoring should return object'); qtests.assert(typeof results.summary === 'object', 'Results should include summary'); qtests.assert(Array.isArray(results.opportunities), 'Results should include opportunities array'); qtests.assert(Array.isArray(results.recommendations), 'Results should include recommendations array'); // Should find unexported utility functions qtests.assert(results.summary.totalFiles >= 2, 'Should analyze utility files'); qtests.assert(results.summary.totalOpportunities >= 4, 'Should find unexported utility functions'); // Clean up fs.rmSync(tempDir, { recursive: true, force: true }); console.log('✓ analyzeProjectRefactoring correctly analyzes project structure'); results.passed++; } catch (error) { console.log(`✗ analyzeProjectRefactoring test failed: ${error.message}`); } // Test with target directories option results.total++; try { const tempDir = path.join(__dirname, 'temp-target-test'); const libDir = path.join(tempDir, 'lib'); const srcDir = path.join(tempDir, 'src'); fs.mkdirSync(tempDir, { recursive: true }); fs.mkdirSync(libDir, { recursive: true }); fs.mkdirSync(srcDir, { recursive: true }); const libFile = path.join(libDir, 'utils.js'); const srcFile = path.join(srcDir, 'main.js'); fs.writeFileSync(libFile, ` function libUtility() { return 'lib'; } `); fs.writeFileSync(srcFile, ` function srcUtility() { return 'src'; } `); const results = await analyzeProjectRefactoring(tempDir, { targetDirs: ['lib'], extensions: ['.js'] }); qtests.assert(results.summary.totalFiles >= 1, 'Should analyze files in target directories'); // Clean up fs.rmSync(tempDir, { recursive: true, force: true }); console.log('✓ analyzeProjectRefactoring correctly handles target directories'); results.passed++; } catch (error) { console.log(`✗ analyzeProjectRefactoring target directories test failed: ${error.message}`); } // Test barrel file detection results.total++; try { const tempDir = path.join(__dirname, 'temp-barrel-test'); const utilsDir = path.join(tempDir, 'utils'); fs.mkdirSync(tempDir, { recursive: true }); fs.mkdirSync(utilsDir, { recursive: true }); // Create utility files without index.js (missing barrel) const util1 = path.join(utilsDir, 'format.js'); const util2 = path.join(utilsDir, 'validate.js'); fs.writeFileSync(util1, ` export function formatText(text) { return text.trim(); } `); fs.writeFileSync(util2, ` export function validateInput(input) { return input.length > 0; } `); const results = await analyzeProjectRefactoring(tempDir, { includeBarrelFiles: true, extensions: ['.js'] }); qtests.assert(results.opportunities.some(op => op.type === 'barrel_file'), 'Should detect missing barrel files'); // Clean up fs.rmSync(tempDir, { recursive: true, force: true }); console.log('✓ analyzeProjectRefactoring correctly detects barrel file opportunities'); results.passed++; } catch (error) { console.log(`✗ analyzeProjectRefactoring barrel file test failed: ${error.message}`); } // Test organization score calculation results.total++; try { const tempDir = path.join(__dirname, 'temp-score-test'); fs.mkdirSync(tempDir, { recursive: true }); // Create well-organized project const indexFile = path.join(tempDir, 'index.js'); fs.writeFileSync(indexFile, ` export { utilityA, utilityB } from './utils'; export { ComponentA } from './components'; `); const utilsFile = path.join(tempDir, 'utils.js'); fs.writeFileSync(utilsFile, ` export function utilityA() { return 'A'; } export function utilityB() { return 'B'; } `); const results = await analyzeProjectRefactoring(tempDir, { extensions: ['.js'] }); qtests.assert(typeof results.summary.organizationScore === 'number', 'Should calculate organization score'); qtests.assert(results.summary.organizationScore >= 0 && results.summary.organizationScore <= 100, 'Score should be 0-100'); qtests.assert(typeof results.summary.organizationGrade === 'string', 'Should calculate organization grade'); // Clean up fs.rmSync(tempDir, { recursive: true, force: true }); console.log('✓ analyzeProjectRefactoring correctly calculates organization metrics'); results.passed++; } catch (error) { console.log(`✗ analyzeProjectRefactoring organization score test failed: ${error.message}`); } // Test error handling for non-existent directory results.total++; try { const results = await analyzeProjectRefactoring('/non/existent/directory', {}); qtests.assert(results.summary.totalFiles === 0, 'Should handle non-existent directory gracefully'); qtests.assert(Array.isArray(results.opportunities), 'Should return empty opportunities array'); console.log('✓ analyzeProjectRefactoring handles non-existent directories gracefully'); results.passed++; } catch (error) { console.log(`✗ analyzeProjectRefactoring error handling test failed: ${error.message}`); } // Test filtering by extension results.total++; try { const tempDir = path.join(__dirname, 'temp-extension-test'); fs.mkdirSync(tempDir, { recursive: true }); const jsFile = path.join(tempDir, 'utils.js'); const tsFile = path.join(tempDir, 'types.ts'); const txtFile = path.join(tempDir, 'readme.txt'); fs.writeFileSync(jsFile, `function jsUtil() { return 'js'; }`); fs.writeFileSync(tsFile, `function tsUtil(): string { return 'ts'; }`); fs.writeFileSync(txtFile, `This is a text file`); const jsOnlyResults = await analyzeProjectRefactoring(tempDir, { extensions: ['.js'] }); const multiExtResults = await analyzeProjectRefactoring(tempDir, { extensions: ['.js', '.ts'] }); qtests.assert(jsOnlyResults.summary.totalFiles === 1, 'Should filter by JS extension only'); qtests.assert(multiExtResults.summary.totalFiles === 2, 'Should include both JS and TS files'); // Clean up fs.rmSync(tempDir, { recursive: true, force: true }); console.log('✓ analyzeProjectRefactoring correctly filters by file extensions'); results.passed++; } catch (error) { console.log(`✗ analyzeProjectRefactoring extension filtering test failed: ${error.message}`); } console.log(`=== Project Refactoring Analyzer 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 };