/** * Tests for WiFi Credentials Validation */ import { validateSSID, validatePassword, validateWiFiCredentials, isStrongPassword, sanitizeWiFiCredentials, getValidationErrorMessage, getValidationMessage, validateRealTime, parseWiFiErrorResponse, prepareCredentialsForDevice, } from '../wifiValidation'; describe('WiFi Validation', () => { describe('validateSSID', () => { it('should accept valid SSIDs', () => { const validSSIDs = [ 'MyHomeWiFi', 'Guest Network', 'Office-5G', 'Café WiFi', 'Network_2.4GHz', 'a', '1234567890123456789012345678901', // 31 chars 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456', // 32 chars ]; validSSIDs.forEach((ssid) => { const result = validateSSID(ssid); expect(result.valid).toBe(true); expect(result.errors).toHaveLength(0); }); }); it('should reject empty SSID', () => { const result = validateSSID(''); expect(result.valid).toBe(false); expect(result.errors).toContain('Network name (SSID) cannot be empty'); }); it('should reject whitespace-only SSID', () => { const result = validateSSID(' '); expect(result.valid).toBe(false); expect(result.errors).toContain('Network name (SSID) cannot be empty'); }); it('should reject SSID longer than 32 bytes', () => { const longSSID = 'A'.repeat(33); const result = validateSSID(longSSID); expect(result.valid).toBe(false); expect(result.errors).toContain('Network name is too long (max 32 bytes)'); }); it('should reject SSID with control characters', () => { const result = validateSSID('Network\x00Name'); expect(result.valid).toBe(false); expect(result.errors).toContain('Network name contains invalid control characters'); }); it('should warn about leading/trailing spaces', () => { const result1 = validateSSID(' MyWiFi'); expect(result1.valid).toBe(true); expect(result1.warnings).toContain( 'Network name has leading or trailing spaces - this may cause connection issues' ); const result2 = validateSSID('MyWiFi '); expect(result2.valid).toBe(true); expect(result2.warnings).toContain( 'Network name has leading or trailing spaces - this may cause connection issues' ); }); it('should warn about multiple consecutive spaces', () => { const result = validateSSID('My WiFi'); expect(result.valid).toBe(true); expect(result.warnings).toContain('Network name has multiple consecutive spaces'); }); }); describe('validatePassword', () => { describe('WPA/WPA2/WPA3 networks', () => { it('should accept valid WPA passwords', () => { const validPasswords = [ 'MySecurePass123', 'abcdefgh', // 8 chars minimum 'A'.repeat(63), // 63 chars maximum 'Pass with spaces!@#', 'ComplexP@ssw0rd!', ]; validPasswords.forEach((password) => { const result = validatePassword(password, 'WPA2 PSK'); expect(result.valid).toBe(true); expect(result.errors).toHaveLength(0); }); }); it('should reject passwords shorter than 8 characters', () => { const result = validatePassword('1234567', 'WPA2 PSK'); expect(result.valid).toBe(false); expect(result.errors).toContain( 'Password must be at least 8 characters for WPA/WPA2/WPA3 networks' ); }); it('should reject passwords longer than 63 characters', () => { const longPassword = 'A'.repeat(64); const result = validatePassword(longPassword, 'WPA2 PSK'); expect(result.valid).toBe(false); expect(result.errors).toContain( 'Password cannot exceed 63 characters for WPA/WPA2/WPA3 networks' ); }); it('should warn about weak passwords', () => { const result = validatePassword('12345678', 'WPA2 PSK'); expect(result.valid).toBe(true); expect(result.warnings).toContain('Password is weak - consider using at least 12 characters'); }); it('should warn about commonly used weak passwords', () => { const weakPasswords = ['password', '12345678', 'qwerty123']; weakPasswords.forEach((password) => { const result = validatePassword(password, 'WPA2 PSK'); expect(result.warnings).toContain('This is a commonly used weak password'); }); }); it('should warn about numeric-only passwords', () => { const result = validatePassword('12345678', 'WPA2 PSK'); expect(result.warnings).toContain( 'Password contains only numbers - consider adding letters and symbols' ); }); it('should warn about non-ASCII characters', () => { const result = validatePassword('Password™123', 'WPA2 PSK'); expect(result.valid).toBe(true); expect(result.warnings).toContain( 'Password contains non-ASCII characters - this may cause issues on some devices' ); }); }); describe('Open networks', () => { it('should accept empty password for open networks', () => { const result = validatePassword('', 'Open'); expect(result.valid).toBe(true); expect(result.errors).toHaveLength(0); }); it('should accept any password for open networks (will be ignored)', () => { const result = validatePassword('any password', 'Open'); expect(result.valid).toBe(true); }); }); describe('WEP networks', () => { it('should accept valid 5-character WEP password', () => { const result = validatePassword('abcde', 'WEP'); expect(result.valid).toBe(true); expect(result.warnings).toContain( 'WEP is deprecated and insecure - consider upgrading your router to WPA2/WPA3' ); }); it('should accept valid 13-character WEP password', () => { const result = validatePassword('abcdefghijklm', 'WEP'); expect(result.valid).toBe(true); }); it('should accept valid 10-digit hex WEP password', () => { const result = validatePassword('1234567890', 'WEP'); expect(result.valid).toBe(true); }); it('should accept valid 26-digit hex WEP password', () => { const result = validatePassword('12345678901234567890123456', 'WEP'); expect(result.valid).toBe(true); }); it('should reject invalid WEP password length', () => { const result = validatePassword('abc', 'WEP'); expect(result.valid).toBe(false); expect(result.errors).toContain( 'WEP password must be 5 or 13 ASCII characters, or 10/26 hexadecimal digits' ); }); }); describe('Secured networks without auth type', () => { it('should require password', () => { const result = validatePassword(''); expect(result.valid).toBe(false); expect(result.errors).toContain('Password is required for secured networks'); }); it('should apply WPA rules by default', () => { const result = validatePassword('1234567'); // 7 chars expect(result.valid).toBe(false); expect(result.errors).toContain( 'Password must be at least 8 characters for WPA/WPA2/WPA3 networks' ); }); }); it('should reject passwords with control characters', () => { const result = validatePassword('Pass\x00word123', 'WPA2 PSK'); expect(result.valid).toBe(false); expect(result.errors).toContain('Password contains invalid control characters'); }); }); describe('validateWiFiCredentials', () => { it('should validate both SSID and password', () => { const result = validateWiFiCredentials('MyWiFi', 'SecurePass123', 'WPA2 PSK'); expect(result.valid).toBe(true); expect(result.errors).toHaveLength(0); }); it('should combine errors from both validations', () => { const result = validateWiFiCredentials('', '123', 'WPA2 PSK'); expect(result.valid).toBe(false); expect(result.errors).toContain('Network name (SSID) cannot be empty'); expect(result.errors).toContain( 'Password must be at least 8 characters for WPA/WPA2/WPA3 networks' ); }); it('should combine warnings from both validations', () => { const result = validateWiFiCredentials(' MyWiFi ', 'password123', 'WPA2 PSK'); expect(result.valid).toBe(true); expect(result.warnings.length).toBeGreaterThan(0); }); it('should validate open network correctly', () => { const result = validateWiFiCredentials('GuestNetwork', '', 'Open'); expect(result.valid).toBe(true); }); }); describe('isStrongPassword', () => { it('should accept strong passwords', () => { const strongPasswords = [ 'MyP@ssw0rd123!', 'Secure_Password_2024', 'Compl3x!P@ssw0rd', 'abCD1234!@#$', ]; strongPasswords.forEach((password) => { expect(isStrongPassword(password)).toBe(true); }); }); it('should reject passwords shorter than 12 characters', () => { expect(isStrongPassword('P@ssw0rd')).toBe(false); }); it('should reject passwords with less than 3 character types', () => { expect(isStrongPassword('onlylowercase')).toBe(false); expect(isStrongPassword('12345678901234')).toBe(false); expect(isStrongPassword('ONLYUPPERCASE')).toBe(false); }); it('should accept passwords with 3 or 4 character types', () => { // 3 types: lowercase, uppercase, numbers expect(isStrongPassword('Password1234')).toBe(true); // 4 types: lowercase, uppercase, numbers, special expect(isStrongPassword('P@ssword1234')).toBe(true); }); }); describe('sanitizeWiFiCredentials', () => { it('should trim SSID whitespace', () => { const result = sanitizeWiFiCredentials({ ssid: ' MyWiFi ', password: 'password', }); expect(result.ssid).toBe('MyWiFi'); }); it('should preserve password whitespace', () => { const result = sanitizeWiFiCredentials({ ssid: 'MyWiFi', password: ' password with spaces ', }); expect(result.password).toBe(' password with spaces '); }); it('should not modify valid credentials', () => { const result = sanitizeWiFiCredentials({ ssid: 'MyWiFi', password: 'SecurePass123', }); expect(result.ssid).toBe('MyWiFi'); expect(result.password).toBe('SecurePass123'); }); }); describe('getValidationErrorMessage', () => { it('should return empty string for valid credentials', () => { const result = validateWiFiCredentials('MyWiFi', 'SecurePass123', 'WPA2 PSK'); expect(getValidationErrorMessage(result)).toBe(''); }); it('should return first error message', () => { const result = validateWiFiCredentials('', '123', 'WPA2 PSK'); expect(getValidationErrorMessage(result)).toBe('Network name (SSID) cannot be empty'); }); it('should return default message if no specific error', () => { const result = { valid: false, errors: [], warnings: [] }; expect(getValidationErrorMessage(result)).toBe('Invalid WiFi credentials'); }); }); describe('getValidationMessage', () => { it('should return empty string for valid credentials with no warnings', () => { const result = { valid: true, errors: [], warnings: [] }; expect(getValidationMessage(result)).toBe(''); }); it('should return errors only', () => { const result = { valid: false, errors: ['Error 1', 'Error 2'], warnings: [], }; expect(getValidationMessage(result)).toBe('Error 1\nError 2'); }); it('should return warnings only', () => { const result = { valid: true, errors: [], warnings: ['Warning 1', 'Warning 2'], }; expect(getValidationMessage(result)).toBe('Warning 1\nWarning 2'); }); it('should return both errors and warnings', () => { const result = { valid: false, errors: ['Error 1'], warnings: ['Warning 1'], }; expect(getValidationMessage(result)).toBe('Error 1\nWarning 1'); }); }); describe('validateRealTime', () => { it('should return valid state for valid credentials', () => { const result = validateRealTime('MyWiFi', 'SecurePass123', 'WPA2 PSK'); expect(result.ssidValid).toBe(true); expect(result.passwordValid).toBe(true); expect(result.canSubmit).toBe(true); expect(result.ssidError).toBeNull(); expect(result.passwordError).toBeNull(); }); it('should indicate password strength', () => { // Weak password (8-9 chars) const weak = validateRealTime('MyWiFi', 'password', 'WPA2 PSK'); expect(weak.passwordStrength).toBe('weak'); // Medium password (10+ chars but not complex) const medium = validateRealTime('MyWiFi', 'mediumpassword', 'WPA2 PSK'); expect(medium.passwordStrength).toBe('medium'); // Strong password (12+ chars with variety) const strong = validateRealTime('MyWiFi', 'Str0ng!Pass123', 'WPA2 PSK'); expect(strong.passwordStrength).toBe('strong'); }); it('should handle open networks', () => { const result = validateRealTime('GuestNetwork', '', 'Open'); expect(result.passwordValid).toBe(true); expect(result.passwordStrength).toBeNull(); expect(result.canSubmit).toBe(true); }); it('should not allow submit with short password', () => { const result = validateRealTime('MyWiFi', '1234567', 'WPA2 PSK'); expect(result.canSubmit).toBe(false); expect(result.passwordValid).toBe(false); }); it('should not allow submit with empty SSID', () => { const result = validateRealTime('', 'password123', 'WPA2 PSK'); expect(result.canSubmit).toBe(false); }); it('should collect warnings', () => { const result = validateRealTime(' MyWiFi', 'password', 'WPA2 PSK'); expect(result.warnings.length).toBeGreaterThan(0); }); it('should return null strength for empty password', () => { const result = validateRealTime('MyWiFi', '', 'WPA2 PSK'); expect(result.passwordStrength).toBeNull(); }); }); describe('parseWiFiErrorResponse', () => { it('should detect wrong password errors', () => { const errors = [ 'WiFi credentials rejected by sensor', 'wrong password', 'check password', 'W|fail response', ]; errors.forEach((error) => { const result = parseWiFiErrorResponse(error); expect(result.type).toBe('password_wrong'); expect(result.suggestion).toContain('check'); }); }); it('should detect network not found errors', () => { const errors = [ 'Network not found', 'no network available', 'SSID not available', ]; errors.forEach((error) => { const result = parseWiFiErrorResponse(error); expect(result.type).toBe('network_not_found'); expect(result.suggestion).toContain('range'); }); }); it('should detect timeout errors', () => { const errors = [ 'Command timeout', 'Request timed out', 'Sensor did not respond in time', ]; errors.forEach((error) => { const result = parseWiFiErrorResponse(error); expect(result.type).toBe('timeout'); expect(result.suggestion).toContain('closer'); }); }); it('should detect connection errors', () => { const errors = [ 'BLE connection failed', 'Bluetooth error', 'Could not connect to device', ]; errors.forEach((error) => { const result = parseWiFiErrorResponse(error); expect(result.type).toBe('connection_failed'); expect(result.suggestion).toContain('try again'); }); }); it('should return unknown for unrecognized errors', () => { const result = parseWiFiErrorResponse('Some random error message'); expect(result.type).toBe('unknown'); expect(result.message).toBe('WiFi configuration failed'); }); }); describe('prepareCredentialsForDevice', () => { it('should return sanitized credentials for valid input', () => { const result = prepareCredentialsForDevice(' MyWiFi ', 'password123', 'WPA2 PSK'); expect(result.ssid).toBe('MyWiFi'); expect(result.password).toBe('password123'); }); it('should throw for invalid SSID with pipe character', () => { expect(() => { prepareCredentialsForDevice('My|WiFi', 'password123', 'WPA2 PSK'); }).toThrow('Network name contains invalid characters'); }); it('should throw for invalid SSID with comma character', () => { expect(() => { prepareCredentialsForDevice('My,WiFi', 'password123', 'WPA2 PSK'); }).toThrow('Network name contains invalid characters'); }); it('should throw for password with pipe character', () => { expect(() => { prepareCredentialsForDevice('MyWiFi', 'pass|word', 'WPA2 PSK'); }).toThrow('Password contains invalid character'); }); it('should allow password with comma character', () => { // Comma is allowed in password (only pipe is delimiter between command and payload) const result = prepareCredentialsForDevice('MyWiFi', 'pass,word123', 'WPA2 PSK'); expect(result.password).toBe('pass,word123'); }); it('should throw for empty SSID', () => { expect(() => { prepareCredentialsForDevice('', 'password123', 'WPA2 PSK'); }).toThrow('Network name (SSID) cannot be empty'); }); it('should throw for short password', () => { expect(() => { prepareCredentialsForDevice('MyWiFi', 'short', 'WPA2 PSK'); }).toThrow('at least 8 characters'); }); it('should allow empty password for open networks', () => { const result = prepareCredentialsForDevice('GuestNetwork', '', 'Open'); expect(result.ssid).toBe('GuestNetwork'); expect(result.password).toBe(''); }); }); });