Files
metabuilder/pastebin/tests/unit/lib/quality-validator/utils/validators.test.ts
johndoe6345789 73c8e3d4dc feat: Add snippet-pastebin application
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>
2026-01-21 16:54:54 +00:00

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);
});
});