UNPKG

@sun-asterisk/sunlint

Version:

☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards

140 lines (122 loc) 6.1 kB
/** * C002_no_duplicate_code - Rule Tests * Tests for heuristic rule analyzer */ const path = require('path'); const fs = require('fs'); const C002_no_duplicate_codeAnalyzer = require('../common/C002_no_duplicate_code/analyzer'); describe('C002_no_duplicate_code Heuristic Rule', () => { let analyzer; const fixturesPath = path.join(__dirname, '../../examples/rule-test-fixtures/rules/C002_no_duplicate_code'); beforeEach(() => { analyzer = new C002_no_duplicate_codeAnalyzer({ minLines: 10, similarityThreshold: 0.85 }); }); describe('Configuration', () => { test('should have correct default configuration', () => { const config = analyzer.getConfig(); expect(config.minLines).toBe(10); expect(config.similarityThreshold).toBe(0.85); expect(config.ignoreComments).toBe(true); expect(config.ignoreWhitespace).toBe(true); }); }); describe('Valid Code - No Duplicates', () => { test('should not report violations for clean code with no duplicates', () => { const testFile = path.join(fixturesPath, 'clean/no-duplicates.ts'); if (fs.existsSync(testFile)) { const violations = analyzer.analyze([testFile], 'typescript'); expect(violations).toHaveLength(0); } }); test('should not report violations for properly extracted utilities', () => { const testFile = path.join(fixturesPath, 'clean/extracted-utilities.ts'); if (fs.existsSync(testFile)) { const violations = analyzer.analyze([testFile], 'typescript'); expect(violations).toHaveLength(0); } }); test('should not report violations for inheritance pattern', () => { const testFile = path.join(fixturesPath, 'clean/inheritance-pattern.ts'); if (fs.existsSync(testFile)) { const violations = analyzer.analyze([testFile], 'typescript'); expect(violations).toHaveLength(0); } }); test('should not report violations for composition pattern', () => { const testFile = path.join(fixturesPath, 'clean/composition-pattern.ts'); if (fs.existsSync(testFile)) { const violations = analyzer.analyze([testFile], 'typescript'); expect(violations).toHaveLength(0); } }); }); describe('Invalid Code - With Duplicates', () => { test('should report violations for duplicate validation logic', () => { const testFile = path.join(fixturesPath, 'violations/duplicate-with-comments.ts'); if (fs.existsSync(testFile)) { const violations = analyzer.analyze([testFile], 'typescript'); expect(violations.length).toBeGreaterThan(0); expect(violations[0].ruleId).toBe('C002'); expect(violations[0].message).toContain('Duplicate'); expect(violations[0].data.suggestions).toBeDefined(); } }); test('should report violations for duplicate repository logic', () => { const testFile = path.join(fixturesPath, 'violations/duplicate-repository-logic.ts'); if (fs.existsSync(testFile)) { const violations = analyzer.analyze([testFile], 'typescript'); expect(violations.length).toBeGreaterThan(0); expect(violations[0].message).toContain('Duplicate'); } }); test('should report violations for duplicate error handling', () => { const testFile = path.join(fixturesPath, 'violations/duplicate-error-handling.ts'); if (fs.existsSync(testFile)) { const violations = analyzer.analyze([testFile], 'typescript'); expect(violations.length).toBeGreaterThan(0); } }); }); describe('Suggestions', () => { test('should provide context-aware suggestions', () => { const testFile = path.join(fixturesPath, 'violations/duplicate-with-comments.ts'); if (fs.existsSync(testFile)) { const violations = analyzer.analyze([testFile], 'typescript'); if (violations.length > 0) { expect(violations[0].data.suggestions).toBeDefined(); expect(Array.isArray(violations[0].data.suggestions)).toBe(true); expect(violations[0].data.suggestions.length).toBeGreaterThan(0); } } }); }); describe('Edge Cases', () => { test('should handle empty file list', () => { const violations = analyzer.analyze([], 'typescript'); expect(violations).toHaveLength(0); }); test('should handle non-existent files gracefully', () => { const violations = analyzer.analyze(['/non/existent/file.ts'], 'typescript'); expect(Array.isArray(violations)).toBe(true); }); test('should handle files with only comments', () => { const tempFile = path.join(__dirname, 'temp-comments-only.ts'); fs.writeFileSync(tempFile, '// Only comments\n/* More comments */\n'); const violations = analyzer.analyze([tempFile], 'typescript'); expect(violations).toHaveLength(0); if (fs.existsSync(tempFile)) { fs.unlinkSync(tempFile); } }); }); describe('Cross-file Detection', () => { test('should detect duplicates across multiple files', () => { const file1 = path.join(fixturesPath, 'violations/duplicate-repository-logic.ts'); const file2 = path.join(fixturesPath, 'violations/duplicate-error-handling.ts'); if (fs.existsSync(file1) && fs.existsSync(file2)) { const violations = analyzer.analyze([file1, file2], 'typescript'); expect(Array.isArray(violations)).toBe(true); } }); }); });