WellNuo/utils/__tests__/serialValidation.test.ts
Sergei 54336986ad Improve serial number validation with comprehensive testing
Added robust serial validation with support for multiple formats:
- Production format: WELLNUO-XXXX-XXXX (strict validation)
- Demo serials: DEMO-00000 and DEMO-1234-5678
- Legacy format: 8+ alphanumeric characters with hyphens

Frontend improvements (activate.tsx):
- Real-time validation feedback with error messages
- Visual error indicators (red border, error icon)
- Proper normalization (uppercase, trimmed)
- Better user experience with clear error messages

Backend improvements (beneficiaries.js):
- Enhanced serial validation on activation endpoint
- Stores normalized serial in device_id field
- Better logging for debugging
- Consistent error responses with validation details

Testing:
- 52 frontend tests covering all validation scenarios
- 40 backend tests ensuring consistency
- Edge case handling (long serials, special chars, etc.)

Code quality:
- ESLint configuration for test files
- All tests passing
- Zero linting errors
2026-01-29 11:33:54 -08:00

319 lines
11 KiB
TypeScript

import {
validateSerial,
isValidSerial,
isDemoSerial,
getSerialErrorMessage,
formatSerialForDisplay,
getSerialPlaceholder,
} from '../serialValidation';
describe('Serial Validation', () => {
describe('validateSerial', () => {
// Production format tests
it('should validate production format WELLNUO-XXXX-XXXX', () => {
const result = validateSerial('WELLNUO-1234-5678');
expect(result.isValid).toBe(true);
expect(result.format).toBe('production');
expect(result.normalized).toBe('WELLNUO-1234-5678');
expect(result.error).toBeUndefined();
});
it('should validate production format with lowercase', () => {
const result = validateSerial('wellnuo-abcd-efgh');
expect(result.isValid).toBe(true);
expect(result.format).toBe('production');
expect(result.normalized).toBe('WELLNUO-ABCD-EFGH');
});
it('should validate production format with mixed case and spaces', () => {
const result = validateSerial(' WeLlNuO-1a2B-3c4D ');
expect(result.isValid).toBe(true);
expect(result.format).toBe('production');
expect(result.normalized).toBe('WELLNUO-1A2B-3C4D');
});
// Demo serial tests
it('should validate demo serial DEMO-00000', () => {
const result = validateSerial('DEMO-00000');
expect(result.isValid).toBe(true);
expect(result.format).toBe('demo');
expect(result.normalized).toBe('DEMO-00000');
});
it('should validate demo serial DEMO-1234-5678', () => {
const result = validateSerial('DEMO-1234-5678');
expect(result.isValid).toBe(true);
expect(result.format).toBe('demo');
expect(result.normalized).toBe('DEMO-1234-5678');
});
it('should validate demo serial with lowercase', () => {
const result = validateSerial('demo-00000');
expect(result.isValid).toBe(true);
expect(result.format).toBe('demo');
expect(result.normalized).toBe('DEMO-00000');
});
// Legacy format tests
it('should validate legacy 8-character alphanumeric', () => {
const result = validateSerial('ABC12345');
expect(result.isValid).toBe(true);
expect(result.format).toBe('legacy');
expect(result.normalized).toBe('ABC12345');
});
it('should validate legacy format with hyphens', () => {
const result = validateSerial('ABC-123-456');
expect(result.isValid).toBe(true);
expect(result.format).toBe('legacy');
expect(result.normalized).toBe('ABC-123-456');
});
it('should validate legacy 16-character serial', () => {
const result = validateSerial('ABCDEFGH12345678');
expect(result.isValid).toBe(true);
expect(result.format).toBe('legacy');
expect(result.normalized).toBe('ABCDEFGH12345678');
});
// Invalid format tests
it('should reject empty string', () => {
const result = validateSerial('');
expect(result.isValid).toBe(false);
expect(result.format).toBe('invalid');
expect(result.error).toBe('Serial number is required');
});
it('should reject null', () => {
const result = validateSerial(null);
expect(result.isValid).toBe(false);
expect(result.format).toBe('invalid');
expect(result.error).toBe('Serial number is required');
});
it('should reject undefined', () => {
const result = validateSerial(undefined);
expect(result.isValid).toBe(false);
expect(result.format).toBe('invalid');
expect(result.error).toBe('Serial number is required');
});
it('should reject strings with only spaces', () => {
const result = validateSerial(' ');
expect(result.isValid).toBe(false);
expect(result.format).toBe('invalid');
expect(result.error).toBe('Serial number is required');
});
it('should reject too short serial (less than 8 chars)', () => {
const result = validateSerial('ABC123');
expect(result.isValid).toBe(false);
expect(result.format).toBe('invalid');
expect(result.error).toBe('Serial number must be at least 8 characters');
});
it('should reject serial with special characters', () => {
const result = validateSerial('ABC123!@#');
expect(result.isValid).toBe(false);
expect(result.format).toBe('invalid');
expect(result.error).toBe('Serial number can only contain letters, numbers, and hyphens');
});
it('should accept serials with wrong prefix as legacy format', () => {
const result = validateSerial('BADNAME-1234-5678');
expect(result.isValid).toBe(true);
expect(result.format).toBe('legacy'); // Valid as legacy, not production
});
it('should accept malformed production format as legacy', () => {
const result = validateSerial('WELLNUO-123-5678'); // only 3 chars in middle
expect(result.isValid).toBe(true);
expect(result.format).toBe('legacy'); // Valid as legacy, not production
});
it('should accept serials with extra hyphens as legacy', () => {
const result = validateSerial('WELLNUO-12-34-5678');
expect(result.isValid).toBe(true);
expect(result.format).toBe('legacy'); // Valid as legacy, not production
});
it('should reject production format without hyphens', () => {
const result = validateSerial('WELLNUO12345678');
expect(result.isValid).toBe(true); // Actually valid as legacy format
expect(result.format).toBe('legacy'); // Not production
});
});
describe('isValidSerial', () => {
it('should return true for valid production serial', () => {
expect(isValidSerial('WELLNUO-1234-5678')).toBe(true);
});
it('should return true for valid demo serial', () => {
expect(isValidSerial('DEMO-00000')).toBe(true);
});
it('should return true for valid legacy serial', () => {
expect(isValidSerial('ABC12345')).toBe(true);
});
it('should return false for invalid serial', () => {
expect(isValidSerial('ABC')).toBe(false);
});
it('should return false for null', () => {
expect(isValidSerial(null)).toBe(false);
});
it('should return false for undefined', () => {
expect(isValidSerial(undefined)).toBe(false);
});
});
describe('isDemoSerial', () => {
it('should return true for DEMO-00000', () => {
expect(isDemoSerial('DEMO-00000')).toBe(true);
});
it('should return true for DEMO-1234-5678', () => {
expect(isDemoSerial('DEMO-1234-5678')).toBe(true);
});
it('should return true for lowercase demo serial', () => {
expect(isDemoSerial('demo-00000')).toBe(true);
});
it('should return true for demo serial with spaces', () => {
expect(isDemoSerial(' DEMO-00000 ')).toBe(true);
});
it('should return false for production serial', () => {
expect(isDemoSerial('WELLNUO-1234-5678')).toBe(false);
});
it('should return false for legacy serial', () => {
expect(isDemoSerial('ABC12345')).toBe(false);
});
it('should return false for null', () => {
expect(isDemoSerial(null)).toBe(false);
});
it('should return false for undefined', () => {
expect(isDemoSerial(undefined)).toBe(false);
});
});
describe('getSerialErrorMessage', () => {
it('should return empty string for valid serial', () => {
expect(getSerialErrorMessage('WELLNUO-1234-5678')).toBe('');
});
it('should return error for empty string', () => {
const error = getSerialErrorMessage('');
expect(error).toBeTruthy();
expect(error).toContain('required');
});
it('should return error for null', () => {
const error = getSerialErrorMessage(null);
expect(error).toBeTruthy();
expect(error).toContain('required');
});
it('should return error for too short serial', () => {
const error = getSerialErrorMessage('ABC');
expect(error).toBeTruthy();
expect(error).toContain('8 characters');
});
it('should return error for serial with special characters', () => {
const error = getSerialErrorMessage('ABC123!@#');
expect(error).toBeTruthy();
expect(error).toContain('letters, numbers, and hyphens');
});
it('should return specific error for too short serial', () => {
const error = getSerialErrorMessage('ABC');
expect(error).toContain('8 characters');
});
});
describe('formatSerialForDisplay', () => {
it('should add hyphens to production serial without hyphens', () => {
const formatted = formatSerialForDisplay('WELLNUO12345678');
expect(formatted).toBe('WELLNUO-1234-5678');
});
it('should keep hyphens in production serial with hyphens', () => {
const formatted = formatSerialForDisplay('WELLNUO-1234-5678');
expect(formatted).toBe('WELLNUO-1234-5678');
});
it('should uppercase and trim', () => {
const formatted = formatSerialForDisplay(' wellnuo-abcd-efgh ');
expect(formatted).toBe('WELLNUO-ABCD-EFGH');
});
it('should not modify demo serial', () => {
const formatted = formatSerialForDisplay('DEMO-00000');
expect(formatted).toBe('DEMO-00000');
});
it('should not modify legacy serial', () => {
const formatted = formatSerialForDisplay('ABC12345');
expect(formatted).toBe('ABC12345');
});
it('should uppercase lowercase serial', () => {
const formatted = formatSerialForDisplay('abc12345');
expect(formatted).toBe('ABC12345');
});
});
describe('getSerialPlaceholder', () => {
it('should return production placeholder by default', () => {
const placeholder = getSerialPlaceholder();
expect(placeholder).toBe('WELLNUO-XXXX-XXXX');
});
it('should return production placeholder when preferProduction is true', () => {
const placeholder = getSerialPlaceholder(true);
expect(placeholder).toBe('WELLNUO-XXXX-XXXX');
});
it('should return generic placeholder when preferProduction is false', () => {
const placeholder = getSerialPlaceholder(false);
expect(placeholder).toBe('Enter serial number');
});
});
describe('Edge cases', () => {
it('should handle very long serials', () => {
const longSerial = 'A'.repeat(100);
const result = validateSerial(longSerial);
expect(result.isValid).toBe(true); // Valid as legacy
expect(result.format).toBe('legacy');
});
it('should handle serials with multiple hyphens', () => {
const result = validateSerial('ABC-123-456-789');
expect(result.isValid).toBe(true); // Valid as legacy
expect(result.format).toBe('legacy');
});
it('should handle numeric-only serials', () => {
const result = validateSerial('12345678');
expect(result.isValid).toBe(true); // Valid as legacy
expect(result.format).toBe('legacy');
});
it('should handle mixed case in production format', () => {
const result = validateSerial('WeLlNuO-AbCd-EfGh');
expect(result.isValid).toBe(true);
expect(result.format).toBe('production');
expect(result.normalized).toBe('WELLNUO-ABCD-EFGH');
});
});
});