@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
JavaScript
/**
* 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);
}
});
});
});