contact-validator.test.js
1 /** 2 * Tests for src/utils/contact-validator.js 3 * 4 * Covers pure/sync functions only: 5 * - validatePhone 6 * - isMobilePhone 7 * 8 * validateEmail and validateSocialUrl require network (DNS/HTTP) — not tested here. 9 */ 10 11 import { test, describe } from 'node:test'; 12 import assert from 'node:assert/strict'; 13 14 import { validatePhone, isMobilePhone } from '../../src/utils/contact-validator.js'; 15 16 // ─── validatePhone ──────────────────────────────────────────────────────────── 17 18 describe('validatePhone', () => { 19 test('returns invalid for null', () => { 20 const result = validatePhone(null, 'AU'); 21 assert.equal(result.valid, false); 22 assert.ok(result.reason); 23 }); 24 25 test('returns invalid for non-string', () => { 26 const result = validatePhone(12345, 'AU'); 27 assert.equal(result.valid, false); 28 }); 29 30 test('returns invalid for empty string', () => { 31 const result = validatePhone('', 'AU'); 32 assert.equal(result.valid, false); 33 }); 34 35 test('returns invalid for whitespace-only string', () => { 36 const result = validatePhone(' ', 'AU'); 37 assert.equal(result.valid, false); 38 }); 39 40 test('returns valid for a real AU mobile number in E.164', () => { 41 const result = validatePhone('+61412345678', 'AU'); 42 assert.equal(result.valid, true); 43 assert.equal(result.reason, null); 44 }); 45 46 test('returns valid for a real US mobile number in E.164', () => { 47 const result = validatePhone('+14089990000', 'US'); 48 assert.equal(result.valid, true); 49 assert.equal(result.reason, null); 50 }); 51 52 test('returns valid for a real GB mobile number', () => { 53 const result = validatePhone('+447712345678', 'GB'); 54 assert.equal(result.valid, true); 55 assert.equal(result.reason, null); 56 }); 57 58 test('maps UK alias to GB', () => { 59 // UK is a non-standard alias for GB; should still validate a real GB number 60 const result = validatePhone('+447712345678', 'UK'); 61 assert.equal(result.valid, true); 62 }); 63 64 test('returns invalid for placeholder number with 6+ repeated digits', () => { 65 const result = validatePhone('+15555555555', 'US'); 66 assert.equal(result.valid, false); 67 assert.ok(result.reason.includes('placeholder')); 68 }); 69 70 test('returns invalid for NANP 555 fictional number', () => { 71 const result = validatePhone('+12125550123', 'US'); 72 assert.equal(result.valid, false); 73 assert.ok(result.reason.includes('fictitious')); 74 }); 75 76 test('returns invalid for obviously fake number', () => { 77 const result = validatePhone('0000000000', 'AU'); 78 assert.equal(result.valid, false); 79 }); 80 81 test('returns invalid for number that is wrong region', () => { 82 // US number passed as AU — should fail region check 83 const result = validatePhone('+12025551234', 'AU'); 84 assert.equal(result.valid, false); 85 assert.ok(result.reason); 86 }); 87 88 test('returns invalid for completely unparseable string', () => { 89 const result = validatePhone('not-a-phone', 'AU'); 90 assert.equal(result.valid, false); 91 assert.ok(result.reason); 92 }); 93 }); 94 95 // ─── isMobilePhone ──────────────────────────────────────────────────────────── 96 97 describe('isMobilePhone', () => { 98 test('returns true for a real AU mobile number', () => { 99 assert.equal(isMobilePhone('+61412345678', 'AU'), true); 100 }); 101 102 test('returns false for an AU landline number', () => { 103 // +61 2 9999 9999 is a Sydney landline 104 assert.equal(isMobilePhone('+61299999999', 'AU'), false); 105 }); 106 107 test('returns false for a US landline number', () => { 108 // US toll-free 800 numbers are not mobile 109 assert.equal(isMobilePhone('+18005551234', 'US'), false); 110 }); 111 112 test('returns false for unparseable input', () => { 113 assert.equal(isMobilePhone('not-a-number', 'AU'), false); 114 }); 115 116 test('returns false for null', () => { 117 assert.equal(isMobilePhone(null, 'AU'), false); 118 }); 119 });