mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-26 14:54:55 +00:00
Full-featured pastebin application with: - Next.js frontend with TypeScript - Express backend with SQLite/PostgreSQL - Syntax highlighting for 100+ languages - Code quality validation system - Comprehensive accessibility (WCAG compliance) - Docker deployment configuration - Playwright E2E tests - Jest unit tests This provides a standalone web application that can be integrated as a capability module in the Universal Platform. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
996 lines
32 KiB
TypeScript
996 lines
32 KiB
TypeScript
/**
|
|
* Comprehensive Unit Tests for Validators Utility
|
|
*
|
|
* Tests all validation functions for input checking, data integrity,
|
|
* and configuration validation.
|
|
*
|
|
* Requirements Covered:
|
|
* 1. Score Validation - Range checks, type validation
|
|
* 2. File Location Validation - Path and position validation
|
|
* 3. Finding Validation - Complete structure checks
|
|
* 4. Recommendation Validation - Priority and details checks
|
|
* 5. Metrics Validation - Object structure checks
|
|
* 6. Scoring Result Validation - Complete result validation
|
|
* 7. Configuration Validation - Threshold and weight validation
|
|
* 8. Finding Sanitization - Default value application
|
|
* 9. Recommendation Sanitization - Default value application
|
|
* 10. Exclude Path Checking - Pattern matching
|
|
* 11. Score Range Validation - Min/max bounds
|
|
* 12. Complexity Validation - Threshold comparisons
|
|
* 13. Coverage Validation - Percentage checks
|
|
* 14. Security Severity Validation - Valid severity levels
|
|
* 15. Grade Validation - Valid letters
|
|
* 16. Status Validation - Valid status values
|
|
* 17. Priority Validation - Valid priorities
|
|
* 18. Effort Validation - Valid effort levels
|
|
* 19. Percentage Validation - 0-100 range
|
|
* 20. Duplication Validation - Percentage checks
|
|
* 21. Weight Validation - 0-1 range
|
|
* 22. Weight Sum Validation - Sum to 1.0
|
|
* 23. Version Validation - Semantic versioning
|
|
* 24. URL Validation - Valid URL format
|
|
*/
|
|
|
|
import {
|
|
validateScore,
|
|
validateFileLocation,
|
|
validateFinding,
|
|
validateRecommendation,
|
|
validateMetrics,
|
|
validateScoringResult,
|
|
validateConfiguration,
|
|
sanitizeFinding,
|
|
sanitizeRecommendation,
|
|
shouldExcludeFile,
|
|
validateScoreRange,
|
|
validateComplexity,
|
|
validateCoveragePercentage,
|
|
validateSecuritySeverity,
|
|
validateGrade,
|
|
validateStatus,
|
|
validatePriority,
|
|
validateEffort,
|
|
validatePercentage,
|
|
validateDuplication,
|
|
validateWeight,
|
|
validateWeightSum,
|
|
validateVersion,
|
|
validateUrl,
|
|
} from '../../../../../src/lib/quality-validator/utils/validators';
|
|
import { createMockFinding, createDefaultConfig } from '../../../../../tests/test-utils';
|
|
import {
|
|
Finding,
|
|
Recommendation,
|
|
ScoringResult,
|
|
Configuration,
|
|
FileLocation,
|
|
CodeQualityMetrics,
|
|
} from '../../../../../src/lib/quality-validator/types/index';
|
|
|
|
// ============================================================================
|
|
// SCORE VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Score Validation', () => {
|
|
it('should validate score in range', () => {
|
|
expect(validateScore(0)).toBe(true);
|
|
expect(validateScore(50)).toBe(true);
|
|
expect(validateScore(100)).toBe(true);
|
|
});
|
|
|
|
it('should reject score below 0', () => {
|
|
expect(validateScore(-1)).toBe(false);
|
|
});
|
|
|
|
it('should reject score above 100', () => {
|
|
expect(validateScore(101)).toBe(false);
|
|
});
|
|
|
|
it('should reject non-number values', () => {
|
|
expect(validateScore(NaN)).toBe(false);
|
|
});
|
|
|
|
it('should validate decimal scores', () => {
|
|
expect(validateScore(85.5)).toBe(true);
|
|
expect(validateScore(0.1)).toBe(true);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// FILE LOCATION VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - File Location Validation', () => {
|
|
it('should validate complete file location', () => {
|
|
const location: FileLocation = { file: 'src/app.ts', line: 45, column: 10 };
|
|
expect(validateFileLocation(location)).toBe(true);
|
|
});
|
|
|
|
it('should accept undefined location', () => {
|
|
expect(validateFileLocation(undefined)).toBe(true);
|
|
});
|
|
|
|
it('should reject location with empty file', () => {
|
|
const location: FileLocation = { file: '', line: 45 };
|
|
expect(validateFileLocation(location)).toBe(false);
|
|
});
|
|
|
|
it('should reject location with non-string file', () => {
|
|
const location = { file: 123, line: 45 } as any;
|
|
expect(validateFileLocation(location)).toBe(false);
|
|
});
|
|
|
|
it('should reject negative line number', () => {
|
|
const location: FileLocation = { file: 'src/app.ts', line: -1 };
|
|
expect(validateFileLocation(location)).toBe(false);
|
|
});
|
|
|
|
it('should reject negative column number', () => {
|
|
const location: FileLocation = { file: 'src/app.ts', column: -1 };
|
|
expect(validateFileLocation(location)).toBe(false);
|
|
});
|
|
|
|
it('should accept location with only file', () => {
|
|
const location: FileLocation = { file: 'src/app.ts' };
|
|
expect(validateFileLocation(location)).toBe(true);
|
|
});
|
|
|
|
it('should accept zero line numbers', () => {
|
|
const location: FileLocation = { file: 'src/app.ts', line: 0, column: 0 };
|
|
expect(validateFileLocation(location)).toBe(true);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// FINDING VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Finding Validation', () => {
|
|
it('should validate complete finding', () => {
|
|
const finding: Finding = {
|
|
id: 'FIND-001',
|
|
severity: 'high',
|
|
category: 'security',
|
|
title: 'SQL Injection',
|
|
description: 'Input not sanitized',
|
|
remediation: 'Add validation',
|
|
};
|
|
expect(validateFinding(finding)).toHaveLength(0);
|
|
});
|
|
|
|
it('should require valid id', () => {
|
|
const finding: Finding = {
|
|
id: '',
|
|
severity: 'high',
|
|
category: 'security',
|
|
title: 'Issue',
|
|
description: 'Description',
|
|
remediation: 'Fix it',
|
|
};
|
|
const errors = validateFinding(finding);
|
|
expect(errors.some((e) => e.includes('id'))).toBe(true);
|
|
});
|
|
|
|
it('should require valid severity', () => {
|
|
const finding: Finding = {
|
|
id: 'FIND-001',
|
|
severity: 'unknown' as any,
|
|
category: 'security',
|
|
title: 'Issue',
|
|
description: 'Description',
|
|
remediation: 'Fix it',
|
|
};
|
|
const errors = validateFinding(finding);
|
|
expect(errors.some((e) => e.includes('severity'))).toBe(true);
|
|
});
|
|
|
|
it('should require valid title', () => {
|
|
const finding: Finding = {
|
|
id: 'FIND-001',
|
|
severity: 'high',
|
|
category: 'security',
|
|
title: '',
|
|
description: 'Description',
|
|
remediation: 'Fix it',
|
|
};
|
|
const errors = validateFinding(finding);
|
|
expect(errors.some((e) => e.includes('title'))).toBe(true);
|
|
});
|
|
|
|
it('should require valid description', () => {
|
|
const finding = {
|
|
id: 'FIND-001',
|
|
severity: 'high',
|
|
category: 'security',
|
|
title: 'Issue',
|
|
description: '',
|
|
remediation: 'Fix it',
|
|
};
|
|
const errors = validateFinding(finding as Finding);
|
|
expect(errors.some((e) => e.includes('description'))).toBe(true);
|
|
});
|
|
|
|
it('should require valid remediation', () => {
|
|
const finding: Finding = {
|
|
id: 'FIND-001',
|
|
severity: 'high',
|
|
category: 'security',
|
|
title: 'Issue',
|
|
description: 'Description',
|
|
remediation: '',
|
|
};
|
|
const errors = validateFinding(finding);
|
|
expect(errors.some((e) => e.includes('remediation'))).toBe(true);
|
|
});
|
|
|
|
it('should validate location if provided', () => {
|
|
const finding: Finding = {
|
|
id: 'FIND-001',
|
|
severity: 'high',
|
|
category: 'security',
|
|
title: 'Issue',
|
|
description: 'Description',
|
|
remediation: 'Fix it',
|
|
location: { file: '', line: 0 },
|
|
};
|
|
const errors = validateFinding(finding);
|
|
expect(errors.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should accept optional fields', () => {
|
|
const finding: Finding = {
|
|
id: 'FIND-001',
|
|
severity: 'high',
|
|
category: 'security',
|
|
title: 'Issue',
|
|
description: 'Description',
|
|
remediation: 'Fix it',
|
|
evidence: 'Code snippet',
|
|
moreInfo: 'https://example.com',
|
|
affectedItems: 5,
|
|
};
|
|
expect(validateFinding(finding)).toHaveLength(0);
|
|
});
|
|
|
|
it('should validate all severity levels', () => {
|
|
const severities: Array<'critical' | 'high' | 'medium' | 'low' | 'info'> = ['critical', 'high', 'medium', 'low', 'info'];
|
|
for (const severity of severities) {
|
|
const finding: Finding = {
|
|
id: 'FIND-001',
|
|
severity,
|
|
category: 'security',
|
|
title: 'Issue',
|
|
description: 'Description',
|
|
remediation: 'Fix it',
|
|
};
|
|
expect(validateFinding(finding)).toHaveLength(0);
|
|
}
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// RECOMMENDATION VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Recommendation Validation', () => {
|
|
it('should validate complete recommendation', () => {
|
|
const rec: Recommendation = {
|
|
priority: 'high',
|
|
category: 'testing',
|
|
issue: 'Low coverage',
|
|
remediation: 'Add tests',
|
|
estimatedEffort: 'high',
|
|
expectedImpact: '10% improvement',
|
|
};
|
|
expect(validateRecommendation(rec)).toHaveLength(0);
|
|
});
|
|
|
|
it('should require valid priority', () => {
|
|
const rec: Recommendation = {
|
|
priority: 'urgent' as any,
|
|
category: 'testing',
|
|
issue: 'Low coverage',
|
|
remediation: 'Add tests',
|
|
estimatedEffort: 'high',
|
|
expectedImpact: '10% improvement',
|
|
};
|
|
const errors = validateRecommendation(rec);
|
|
expect(errors.some((e) => e.includes('priority'))).toBe(true);
|
|
});
|
|
|
|
it('should require valid issue', () => {
|
|
const rec: Recommendation = {
|
|
priority: 'high',
|
|
category: 'testing',
|
|
issue: '',
|
|
remediation: 'Add tests',
|
|
estimatedEffort: 'high',
|
|
expectedImpact: '10% improvement',
|
|
};
|
|
const errors = validateRecommendation(rec);
|
|
expect(errors.some((e) => e.includes('issue'))).toBe(true);
|
|
});
|
|
|
|
it('should require valid remediation', () => {
|
|
const rec: Recommendation = {
|
|
priority: 'high',
|
|
category: 'testing',
|
|
issue: 'Low coverage',
|
|
remediation: '',
|
|
estimatedEffort: 'high',
|
|
expectedImpact: '10% improvement',
|
|
};
|
|
const errors = validateRecommendation(rec);
|
|
expect(errors.some((e) => e.includes('remediation'))).toBe(true);
|
|
});
|
|
|
|
it('should require valid estimatedEffort', () => {
|
|
const rec: Recommendation = {
|
|
priority: 'high',
|
|
category: 'testing',
|
|
issue: 'Low coverage',
|
|
remediation: 'Add tests',
|
|
estimatedEffort: 'maximum' as any,
|
|
expectedImpact: '10% improvement',
|
|
};
|
|
const errors = validateRecommendation(rec);
|
|
expect(errors.some((e) => e.includes('estimatedEffort'))).toBe(true);
|
|
});
|
|
|
|
it('should require valid expectedImpact', () => {
|
|
const rec: Recommendation = {
|
|
priority: 'high',
|
|
category: 'testing',
|
|
issue: 'Low coverage',
|
|
remediation: 'Add tests',
|
|
estimatedEffort: 'high',
|
|
expectedImpact: '',
|
|
};
|
|
const errors = validateRecommendation(rec);
|
|
expect(errors.some((e) => e.includes('expectedImpact'))).toBe(true);
|
|
});
|
|
|
|
it('should validate all priority levels', () => {
|
|
const priorities: Array<'critical' | 'high' | 'medium' | 'low'> = ['critical', 'high', 'medium', 'low'];
|
|
for (const priority of priorities) {
|
|
const rec: Recommendation = {
|
|
priority,
|
|
category: 'testing',
|
|
issue: 'Low coverage',
|
|
remediation: 'Add tests',
|
|
estimatedEffort: 'high',
|
|
expectedImpact: '10% improvement',
|
|
};
|
|
expect(validateRecommendation(rec)).toHaveLength(0);
|
|
}
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// METRICS VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Metrics Validation', () => {
|
|
it('should validate valid metrics object', () => {
|
|
const metrics = {
|
|
complexity: { functions: [] },
|
|
duplication: { percent: 2 },
|
|
linting: { errors: 0 },
|
|
};
|
|
expect(validateMetrics(metrics)).toBe(true);
|
|
});
|
|
|
|
it('should reject null metrics', () => {
|
|
expect(validateMetrics(null)).toBe(false);
|
|
});
|
|
|
|
it('should reject non-object metrics', () => {
|
|
expect(validateMetrics('metrics')).toBe(false);
|
|
expect(validateMetrics(123)).toBe(false);
|
|
});
|
|
|
|
it('should reject invalid complexity metric', () => {
|
|
const metrics = {
|
|
complexity: 'not an object',
|
|
duplication: { percent: 2 },
|
|
linting: { errors: 0 },
|
|
};
|
|
expect(validateMetrics(metrics)).toBe(false);
|
|
});
|
|
|
|
it('should accept empty metrics object', () => {
|
|
expect(validateMetrics({})).toBe(true);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// SCORING RESULT VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Scoring Result Validation', () => {
|
|
it('should validate complete result', () => {
|
|
const result: ScoringResult = {
|
|
overall: {
|
|
score: 85,
|
|
grade: 'B',
|
|
status: 'pass',
|
|
summary: 'Good',
|
|
passesThresholds: true,
|
|
},
|
|
componentScores: {
|
|
codeQuality: { score: 80, weight: 0.3, weightedScore: 24 },
|
|
testCoverage: { score: 90, weight: 0.35, weightedScore: 31.5 },
|
|
architecture: { score: 75, weight: 0.2, weightedScore: 15 },
|
|
security: { score: 85, weight: 0.15, weightedScore: 12.75 },
|
|
},
|
|
findings: [],
|
|
recommendations: [],
|
|
metadata: {
|
|
timestamp: new Date().toISOString(),
|
|
toolVersion: '1.0.0',
|
|
analysisTime: 100,
|
|
projectPath: '/project',
|
|
nodeVersion: process.version,
|
|
configUsed: createDefaultConfig(),
|
|
},
|
|
};
|
|
expect(validateScoringResult(result)).toHaveLength(0);
|
|
});
|
|
|
|
it('should require overall score', () => {
|
|
const result = {
|
|
componentScores: {},
|
|
findings: [],
|
|
recommendations: [],
|
|
metadata: {},
|
|
} as any;
|
|
const errors = validateScoringResult(result);
|
|
expect(errors.some((e) => e.includes('overall'))).toBe(true);
|
|
});
|
|
|
|
it('should validate overall score range', () => {
|
|
const result: ScoringResult = {
|
|
overall: {
|
|
score: 150,
|
|
grade: 'A',
|
|
status: 'pass',
|
|
summary: 'Good',
|
|
passesThresholds: true,
|
|
},
|
|
componentScores: {
|
|
codeQuality: { score: 80, weight: 0.3, weightedScore: 24 },
|
|
testCoverage: { score: 90, weight: 0.35, weightedScore: 31.5 },
|
|
architecture: { score: 75, weight: 0.2, weightedScore: 15 },
|
|
security: { score: 85, weight: 0.15, weightedScore: 12.75 },
|
|
},
|
|
findings: [],
|
|
recommendations: [],
|
|
metadata: {} as any,
|
|
};
|
|
const errors = validateScoringResult(result);
|
|
expect(errors.some((e) => e.includes('score'))).toBe(true);
|
|
});
|
|
|
|
it('should validate grade', () => {
|
|
const result: ScoringResult = {
|
|
overall: {
|
|
score: 85,
|
|
grade: 'Z' as any,
|
|
status: 'pass',
|
|
summary: 'Good',
|
|
passesThresholds: true,
|
|
},
|
|
componentScores: {
|
|
codeQuality: { score: 80, weight: 0.3, weightedScore: 24 },
|
|
testCoverage: { score: 90, weight: 0.35, weightedScore: 31.5 },
|
|
architecture: { score: 75, weight: 0.2, weightedScore: 15 },
|
|
security: { score: 85, weight: 0.15, weightedScore: 12.75 },
|
|
},
|
|
findings: [],
|
|
recommendations: [],
|
|
metadata: {} as any,
|
|
};
|
|
const errors = validateScoringResult(result);
|
|
expect(errors.some((e) => e.includes('Grade'))).toBe(true);
|
|
});
|
|
|
|
it('should require findings array', () => {
|
|
const result: ScoringResult = {
|
|
overall: {
|
|
score: 85,
|
|
grade: 'B',
|
|
status: 'pass',
|
|
summary: 'Good',
|
|
passesThresholds: true,
|
|
},
|
|
componentScores: {
|
|
codeQuality: { score: 80, weight: 0.3, weightedScore: 24 },
|
|
testCoverage: { score: 90, weight: 0.35, weightedScore: 31.5 },
|
|
architecture: { score: 75, weight: 0.2, weightedScore: 15 },
|
|
security: { score: 85, weight: 0.15, weightedScore: 12.75 },
|
|
},
|
|
findings: 'not an array' as any,
|
|
recommendations: [],
|
|
metadata: {} as any,
|
|
};
|
|
const errors = validateScoringResult(result);
|
|
expect(errors.some((e) => e.includes('Findings'))).toBe(true);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// CONFIGURATION VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Configuration Validation', () => {
|
|
it('should validate configuration with correct thresholds', () => {
|
|
const config = createDefaultConfig();
|
|
// Fix the coverage thresholds to be valid
|
|
config.testCoverage.warningPercent = 85;
|
|
config.testCoverage.minimumPercent = 80;
|
|
const errors = validateConfiguration(config);
|
|
expect(errors).toHaveLength(0);
|
|
});
|
|
|
|
it('should require configuration object', () => {
|
|
const errors = validateConfiguration(null as any);
|
|
expect(errors.some((e) => e.includes('required'))).toBe(true);
|
|
});
|
|
|
|
it('should validate scoring weights sum to 1.0', () => {
|
|
const config = createDefaultConfig();
|
|
config.scoring.weights = {
|
|
codeQuality: 0.3,
|
|
testCoverage: 0.35,
|
|
architecture: 0.2,
|
|
security: 0.1,
|
|
};
|
|
const errors = validateConfiguration(config);
|
|
expect(errors.some((e) => e.includes('weights'))).toBe(true);
|
|
});
|
|
|
|
it('should validate individual weight ranges', () => {
|
|
const config = createDefaultConfig();
|
|
config.scoring.weights.codeQuality = 1.5;
|
|
const errors = validateConfiguration(config);
|
|
expect(errors.some((e) => e.includes('codeQuality'))).toBe(true);
|
|
});
|
|
|
|
it('should validate complexity thresholds', () => {
|
|
const config = createDefaultConfig();
|
|
config.codeQuality.complexity.warning = 25;
|
|
config.codeQuality.complexity.max = 20;
|
|
const errors = validateConfiguration(config);
|
|
expect(errors.some((e) => e.includes('warning'))).toBe(true);
|
|
});
|
|
|
|
it('should validate coverage thresholds', () => {
|
|
const config = createDefaultConfig();
|
|
config.testCoverage.warningPercent = 70; // warning below minimum
|
|
config.testCoverage.minimumPercent = 80;
|
|
const errors = validateConfiguration(config);
|
|
expect(errors.some((e) => e.includes('warning'))).toBe(true);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// FINDING SANITIZATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Finding Sanitization', () => {
|
|
it('should sanitize finding with trimmed strings', () => {
|
|
const finding: Finding = {
|
|
id: ' FIND-001 ',
|
|
severity: 'high',
|
|
category: 'security',
|
|
title: ' Issue ',
|
|
description: ' Description ',
|
|
remediation: ' Fix it ',
|
|
};
|
|
const sanitized = sanitizeFinding(finding);
|
|
expect(sanitized.title).toBe('Issue');
|
|
expect(sanitized.description).toBe('Description');
|
|
});
|
|
|
|
it('should provide default title for empty', () => {
|
|
const finding: Finding = {
|
|
id: 'FIND-001',
|
|
severity: 'high',
|
|
category: 'security',
|
|
title: '',
|
|
description: 'Description',
|
|
remediation: 'Fix it',
|
|
};
|
|
const sanitized = sanitizeFinding(finding);
|
|
expect(sanitized.title).toBe('Unknown Issue');
|
|
});
|
|
|
|
it('should validate and remove invalid location', () => {
|
|
const finding: Finding = {
|
|
id: 'FIND-001',
|
|
severity: 'high',
|
|
category: 'security',
|
|
title: 'Issue',
|
|
description: 'Description',
|
|
remediation: 'Fix it',
|
|
location: { file: '', line: 0 },
|
|
};
|
|
const sanitized = sanitizeFinding(finding);
|
|
expect(sanitized.location).toBeUndefined();
|
|
});
|
|
|
|
it('should fix invalid severity', () => {
|
|
const finding: Finding = {
|
|
id: 'FIND-001',
|
|
severity: 'invalid' as any,
|
|
category: 'security',
|
|
title: 'Issue',
|
|
description: 'Description',
|
|
remediation: 'Fix it',
|
|
};
|
|
const sanitized = sanitizeFinding(finding);
|
|
expect(sanitized.severity).toBe('medium');
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// RECOMMENDATION SANITIZATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Recommendation Sanitization', () => {
|
|
it('should sanitize recommendation with defaults', () => {
|
|
const rec: Recommendation = {
|
|
priority: 'high',
|
|
category: 'testing',
|
|
issue: ' Low coverage ',
|
|
remediation: ' Add tests ',
|
|
estimatedEffort: 'high',
|
|
expectedImpact: ' 10% improvement ',
|
|
};
|
|
const sanitized = sanitizeRecommendation(rec);
|
|
expect(sanitized.issue).toBe('Low coverage');
|
|
expect(sanitized.remediation).toBe('Add tests');
|
|
});
|
|
|
|
it('should fix invalid priority', () => {
|
|
const rec: Recommendation = {
|
|
priority: 'urgent' as any,
|
|
category: 'testing',
|
|
issue: 'Issue',
|
|
remediation: 'Fix it',
|
|
estimatedEffort: 'high',
|
|
expectedImpact: 'Impact',
|
|
};
|
|
const sanitized = sanitizeRecommendation(rec);
|
|
expect(sanitized.priority).toBe('medium');
|
|
});
|
|
|
|
it('should provide default empty remediation', () => {
|
|
const rec: Recommendation = {
|
|
priority: 'high',
|
|
category: 'testing',
|
|
issue: 'Issue',
|
|
remediation: '',
|
|
estimatedEffort: 'high',
|
|
expectedImpact: 'Impact',
|
|
};
|
|
const sanitized = sanitizeRecommendation(rec);
|
|
expect(sanitized.remediation).toBe('');
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// EXCLUDE PATH VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Exclude Path Validation', () => {
|
|
it('should identify excluded file', () => {
|
|
expect(shouldExcludeFile('node_modules/pkg/index.js', ['node_modules'])).toBe(true);
|
|
});
|
|
|
|
it('should identify non-excluded file', () => {
|
|
expect(shouldExcludeFile('src/app.ts', ['node_modules', '.git'])).toBe(false);
|
|
});
|
|
|
|
it('should handle multiple patterns', () => {
|
|
const patterns = ['node_modules', '.git', 'dist', '.nyc_output'];
|
|
expect(shouldExcludeFile('dist/index.js', patterns)).toBe(true);
|
|
expect(shouldExcludeFile('.git/config', patterns)).toBe(true);
|
|
});
|
|
|
|
it('should handle empty pattern list', () => {
|
|
expect(shouldExcludeFile('any/file.ts', [])).toBe(false);
|
|
});
|
|
|
|
it('should match partial paths', () => {
|
|
expect(shouldExcludeFile('src/.git/something', ['.git'])).toBe(true);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// SCORE RANGE VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Score Range Validation', () => {
|
|
it('should validate score in default range', () => {
|
|
expect(validateScoreRange(50)).toBe(true);
|
|
expect(validateScoreRange(0)).toBe(true);
|
|
expect(validateScoreRange(100)).toBe(true);
|
|
});
|
|
|
|
it('should validate custom range', () => {
|
|
expect(validateScoreRange(25, 0, 50)).toBe(true);
|
|
expect(validateScoreRange(60, 0, 50)).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// COMPLEXITY VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Complexity Validation', () => {
|
|
it('should validate acceptable complexity', () => {
|
|
expect(validateComplexity(10, 20, 15)).toBe(true);
|
|
});
|
|
|
|
it('should reject complexity above max', () => {
|
|
expect(validateComplexity(25, 20, 15)).toBe(false);
|
|
});
|
|
|
|
it('should reject invalid thresholds', () => {
|
|
expect(validateComplexity(10, 15, 20)).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// COVERAGE PERCENTAGE VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Coverage Percentage Validation', () => {
|
|
it('should validate adequate coverage', () => {
|
|
expect(validateCoveragePercentage(85, 80)).toBe(true);
|
|
});
|
|
|
|
it('should reject inadequate coverage', () => {
|
|
expect(validateCoveragePercentage(75, 80)).toBe(false);
|
|
});
|
|
|
|
it('should reject coverage above 100', () => {
|
|
expect(validateCoveragePercentage(105, 80)).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// SECURITY SEVERITY VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Security Severity Validation', () => {
|
|
it('should validate all severity levels', () => {
|
|
expect(validateSecuritySeverity('critical')).toBe(true);
|
|
expect(validateSecuritySeverity('high')).toBe(true);
|
|
expect(validateSecuritySeverity('medium')).toBe(true);
|
|
expect(validateSecuritySeverity('low')).toBe(true);
|
|
expect(validateSecuritySeverity('info')).toBe(true);
|
|
});
|
|
|
|
it('should reject invalid severity', () => {
|
|
expect(validateSecuritySeverity('unknown')).toBe(false);
|
|
});
|
|
|
|
it('should handle case insensitivity', () => {
|
|
expect(validateSecuritySeverity('HIGH')).toBe(true);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// GRADE VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Grade Validation', () => {
|
|
it('should validate all letter grades', () => {
|
|
expect(validateGrade('A')).toBe(true);
|
|
expect(validateGrade('B')).toBe(true);
|
|
expect(validateGrade('C')).toBe(true);
|
|
expect(validateGrade('D')).toBe(true);
|
|
expect(validateGrade('F')).toBe(true);
|
|
});
|
|
|
|
it('should reject invalid grades', () => {
|
|
expect(validateGrade('G')).toBe(false);
|
|
expect(validateGrade('E')).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// STATUS VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Status Validation', () => {
|
|
it('should validate valid statuses', () => {
|
|
expect(validateStatus('pass')).toBe(true);
|
|
expect(validateStatus('fail')).toBe(true);
|
|
expect(validateStatus('warning')).toBe(true);
|
|
});
|
|
|
|
it('should handle case insensitivity', () => {
|
|
expect(validateStatus('PASS')).toBe(true);
|
|
});
|
|
|
|
it('should reject invalid status', () => {
|
|
expect(validateStatus('unknown')).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// PRIORITY VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Priority Validation', () => {
|
|
it('should validate valid priorities', () => {
|
|
expect(validatePriority('critical')).toBe(true);
|
|
expect(validatePriority('high')).toBe(true);
|
|
expect(validatePriority('medium')).toBe(true);
|
|
expect(validatePriority('low')).toBe(true);
|
|
});
|
|
|
|
it('should handle case insensitivity', () => {
|
|
expect(validatePriority('HIGH')).toBe(true);
|
|
});
|
|
|
|
it('should reject invalid priority', () => {
|
|
expect(validatePriority('urgent')).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// EFFORT VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Effort Validation', () => {
|
|
it('should validate valid effort levels', () => {
|
|
expect(validateEffort('high')).toBe(true);
|
|
expect(validateEffort('medium')).toBe(true);
|
|
expect(validateEffort('low')).toBe(true);
|
|
});
|
|
|
|
it('should reject invalid effort', () => {
|
|
expect(validateEffort('maximum')).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// PERCENTAGE VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Percentage Validation', () => {
|
|
it('should validate percentage values', () => {
|
|
expect(validatePercentage(0)).toBe(true);
|
|
expect(validatePercentage(50)).toBe(true);
|
|
expect(validatePercentage(100)).toBe(true);
|
|
});
|
|
|
|
it('should reject values outside range', () => {
|
|
expect(validatePercentage(-1)).toBe(false);
|
|
expect(validatePercentage(101)).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// DUPLICATION VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Duplication Validation', () => {
|
|
it('should validate acceptable duplication percentages', () => {
|
|
expect(validateDuplication(5, 10)).toBe(true);
|
|
expect(validateDuplication(0, 0)).toBe(true);
|
|
expect(validateDuplication(100, 100)).toBe(true);
|
|
});
|
|
|
|
it('should reject invalid duplication values', () => {
|
|
expect(validateDuplication(105, 10)).toBe(false); // Above 100
|
|
expect(validateDuplication(-5, 10)).toBe(false); // Below 0
|
|
});
|
|
|
|
it('should reject invalid max allowed values', () => {
|
|
expect(validateDuplication(5, 105)).toBe(false); // maxAllowed above 100
|
|
expect(validateDuplication(5, -10)).toBe(false); // maxAllowed below 0
|
|
});
|
|
|
|
it('should require both parameters to be numbers', () => {
|
|
expect(validateDuplication('5' as any, 10)).toBe(false);
|
|
expect(validateDuplication(5, 'ten' as any)).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// WEIGHT VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Weight Validation', () => {
|
|
it('should validate weight in 0-1 range', () => {
|
|
expect(validateWeight(0)).toBe(true);
|
|
expect(validateWeight(0.5)).toBe(true);
|
|
expect(validateWeight(1)).toBe(true);
|
|
});
|
|
|
|
it('should reject weight outside range', () => {
|
|
expect(validateWeight(-0.1)).toBe(false);
|
|
expect(validateWeight(1.1)).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// WEIGHT SUM VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Weight Sum Validation', () => {
|
|
it('should validate weights sum to 1.0', () => {
|
|
expect(validateWeightSum([0.25, 0.25, 0.25, 0.25])).toBe(true);
|
|
expect(validateWeightSum([0.3, 0.35, 0.2, 0.15])).toBe(true);
|
|
});
|
|
|
|
it('should reject weights not summing to 1.0', () => {
|
|
expect(validateWeightSum([0.25, 0.25, 0.25])).toBe(false);
|
|
});
|
|
|
|
it('should allow tolerance', () => {
|
|
expect(validateWeightSum([0.25, 0.25, 0.25, 0.2501], 0.01)).toBe(true);
|
|
});
|
|
|
|
it('should reject non-array input', () => {
|
|
expect(validateWeightSum('not an array' as any)).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// VERSION VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - Version Validation', () => {
|
|
it('should validate semantic versioning', () => {
|
|
expect(validateVersion('1.0.0')).toBe(true);
|
|
expect(validateVersion('2.5.3')).toBe(true);
|
|
expect(validateVersion('0.0.1')).toBe(true);
|
|
});
|
|
|
|
it('should allow extended version strings', () => {
|
|
expect(validateVersion('1.0.0-beta')).toBe(true);
|
|
expect(validateVersion('2.0.0+build')).toBe(true);
|
|
});
|
|
|
|
it('should reject invalid format', () => {
|
|
expect(validateVersion('1.0')).toBe(false);
|
|
expect(validateVersion('invalid')).toBe(false);
|
|
});
|
|
|
|
it('should require string type', () => {
|
|
expect(validateVersion(100 as any)).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// URL VALIDATION
|
|
// ============================================================================
|
|
|
|
describe('Validators - URL Validation', () => {
|
|
it('should validate valid URLs', () => {
|
|
expect(validateUrl('https://example.com')).toBe(true);
|
|
expect(validateUrl('http://localhost:3000')).toBe(true);
|
|
expect(validateUrl('https://example.com/path?query=value')).toBe(true);
|
|
});
|
|
|
|
it('should reject invalid URLs', () => {
|
|
expect(validateUrl('not-a-url')).toBe(false);
|
|
expect(validateUrl('example.com')).toBe(false);
|
|
});
|
|
|
|
it('should require string type', () => {
|
|
expect(validateUrl(123 as any)).toBe(false);
|
|
});
|
|
|
|
it('should handle edge cases', () => {
|
|
expect(validateUrl('ftp://files.example.com')).toBe(true);
|
|
expect(validateUrl('file:///path/to/file')).toBe(true);
|
|
});
|
|
});
|