import { describe, it, expect, beforeEach } from 'vitest'; import { InputValidator, ValidationRule } from '../InputValidator'; import { DEFAULT_SECURITY_CONFIG } from '../SecurityConfig'; describe('InputValidator - Comprehensive Tests', () => { let validator: InputValidator; beforeEach(() => { validator = new InputValidator(DEFAULT_SECURITY_CONFIG.validation); }); describe('Basic Validation', () => { it('should validate input against single rule', () => { const result = validator.validate('test@example.com', ['email']); expect(result.valid).toBe(true); expect(result.errors).toHaveLength(0); expect(result.sanitized).toBe('test@example.com'); }); it('should validate input against multiple rules', () => { const result = validator.validate('testuser123', ['alphanumericOnly', 'noEmpty']); expect(result.valid).toBe(true); expect(result.errors).toHaveLength(0); }); it('should fail validation with invalid input', () => { const result = validator.validate('invalid-email', ['email']); expect(result.valid).toBe(false); expect(result.errors).toContain('Invalid email format'); expect(result.sanitized).toBeUndefined(); }); it('should handle unknown validation rules', () => { const result = validator.validate('test', ['unknownRule']); expect(result.valid).toBe(false); expect(result.errors).toContain('Unknown validation rule: unknownRule'); }); it('should enforce maximum input length', () => { const longInput = 'x'.repeat(100001); const result = validator.validate(longInput, ['noEmpty']); expect(result.valid).toBe(false); expect(result.errors[0]).toContain('Input exceeds maximum length'); }); }); describe('Built-in Validation Rules', () => { describe('Email Validation', () => { it('should validate correct email formats', () => { const validEmails = [ 'test@example.com', 'user.name@domain.co.uk', 'test+tag@example.org', 'firstname.lastname@company.com' ]; validEmails.forEach(email => { const result = validator.validate(email, ['email']); expect(result.valid).toBe(true); }); }); it('should reject invalid email formats', () => { const invalidEmails = [ 'invalid-email', '@example.com', 'test@', 'test..test@example.com', 'test@.com', 'test@com', '' ]; invalidEmails.forEach(email => { const result = validator.validate(email, ['email']); expect(result.valid).toBe(false); }); }); }); describe('URL Validation', () => { it('should validate correct URL formats', () => { const validUrls = [ 'https://example.com', 'http://localhost:3000', 'ftp://files.example.com', 'https://sub.domain.com/path?query=value' ]; validUrls.forEach(url => { const result = validator.validate(url, ['url']); expect(result.valid).toBe(true); }); }); it('should reject invalid URL formats', () => { const invalidUrls = [ 'not-a-url', 'javascript:alert(1)', 'data:text/html,', 'example.com', '' ]; invalidUrls.forEach(url => { const result = validator.validate(url, ['url']); expect(result.valid).toBe(false); }); }); }); describe('UUID Validation', () => { it('should validate correct UUID formats', () => { const validUuids = [ '550e8400-e29b-41d4-a716-446655440000', '6ba7b810-9dad-11d1-80b4-00c04fd430c8', '6ba7b811-9dad-11d1-80b4-00c04fd430c8', '00000000-0000-1000-8000-000000000000' ]; validUuids.forEach(uuid => { const result = validator.validate(uuid, ['uuid']); expect(result.valid).toBe(true); }); }); it('should reject invalid UUID formats', () => { const invalidUuids = [ 'not-a-uuid', '550e8400-e29b-41d4-a716', '550e8400-e29b-41d4-a716-446655440000-extra', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', '' ]; invalidUuids.forEach(uuid => { const result = validator.validate(uuid, ['uuid']); expect(result.valid).toBe(false); }); }); }); describe('Alphanumeric Validation', () => { it('should validate alphanumeric strings', () => { const validInputs = ['abc123', 'ABC', '123', 'TestUser123']; validInputs.forEach(input => { const result = validator.validate(input, ['alphanumericOnly']); expect(result.valid).toBe(true); }); }); it('should reject non-alphanumeric strings', () => { const invalidInputs = ['test@example', 'hello world', 'test-user', 'user_name', '']; invalidInputs.forEach(input => { const result = validator.validate(input, ['alphanumericOnly']); expect(result.valid).toBe(false); }); }); it('should sanitize non-alphanumeric characters', () => { const result = validator.validate('test@user-123', ['alphanumericOnly']); expect(result.valid).toBe(false); // In non-strict mode, sanitization would occur }); }); describe('Numeric Validation', () => { it('should validate numeric values', () => { const validNumbers = ['123', '0', '-456', '3.14', '1e10']; validNumbers.forEach(num => { const result = validator.validate(num, ['numeric']); expect(result.valid).toBe(true); expect(typeof result.sanitized).toBe('number'); }); }); it('should reject non-numeric values', () => { const invalidNumbers = ['abc', 'test123', '', 'NaN']; invalidNumbers.forEach(num => { const result = validator.validate(num, ['numeric']); expect(result.valid).toBe(false); }); }); }); describe('Empty Value Validation', () => { it('should reject empty strings', () => { const result = validator.validate('', ['noEmpty']); expect(result.valid).toBe(false); expect(result.errors).toContain('Value cannot be empty'); }); it('should reject whitespace-only strings', () => { const result = validator.validate(' \t\n ', ['noEmpty']); expect(result.valid).toBe(false); }); it('should accept non-empty strings', () => { const result = validator.validate('valid content', ['noEmpty']); expect(result.valid).toBe(true); }); }); describe('Special Characters Validation', () => { it('should validate allowed special characters', () => { const validInputs = ['test.file', 'user_name', 'file-name', 'hello world']; validInputs.forEach(input => { const result = validator.validate(input, ['noSpecialChars']); expect(result.valid).toBe(true); }); }); it('should reject disallowed special characters', () => { const invalidInputs = ['test@example', 'user#name', 'file$name', 'test%value']; invalidInputs.forEach(input => { const result = validator.validate(input, ['noSpecialChars']); expect(result.valid).toBe(false); }); }); }); }); describe('Security Validations', () => { describe('HTML Content Detection', () => { it('should detect HTML tags', () => { const htmlInputs = [ '', '
',
'Hello World'
];
htmlInputs.forEach(html => {
const result = validator.validate(html, ['noHtml']);
expect(result.valid).toBe(false);
expect(result.errors).toContain('HTML content is not allowed');
});
});
it('should strip HTML tags during sanitization', () => {
const nonStrictConfig = {
...DEFAULT_SECURITY_CONFIG.validation,
strict: false
};
const nonStrictValidator = new InputValidator(nonStrictConfig);
const result = nonStrictValidator.validate('