Skip to content

Commit 18bc6bb

Browse files
Move validate phone number, safe string methods to utils, add test files for respective functions, modify incoming openedx and twilio webhook requests to validate request.body
1 parent 6ae7b0b commit 18bc6bb

File tree

10 files changed

+571
-59
lines changed

10 files changed

+571
-59
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
const validatePhoneNumber = require('../../utils/validatePhoneNumber');
2+
3+
describe('validatePhoneNumber', () => {
4+
let mockHelpers;
5+
6+
beforeEach(() => {
7+
// Clear all mocks before each test
8+
jest.clearAllMocks();
9+
10+
// Mock Joi helpers
11+
mockHelpers = {
12+
error: jest.fn((code) => ({ code, message: `Validation error: ${code}` }))
13+
};
14+
});
15+
16+
describe('Valid phone numbers', () => {
17+
it('should accept valid US/Canada phone numbers with exactly 10 digits after +1', () => {
18+
const validNumbers = [
19+
'+12345678901',
20+
'+15551234567',
21+
'+19876543210',
22+
'+11234567890'
23+
];
24+
25+
validNumbers.forEach(number => {
26+
const result = validatePhoneNumber(number, mockHelpers);
27+
28+
expect(result).toBe(number);
29+
expect(mockHelpers.error).not.toHaveBeenCalled();
30+
});
31+
});
32+
33+
it('should reject phone numbers with less than or more than 10 digits after +1', () => {
34+
const invalidNumbers = [
35+
'+123456789', // 9 digits
36+
'+123456789012', // 12 digits
37+
'+1', // only +1
38+
'+12', // only +1 and 1 digit
39+
'+123', // only +1 and 2 digits
40+
'+1234', // only +1 and 3 digits
41+
'+12345', // only +1 and 4 digits
42+
'+123456', // only +1 and 5 digits
43+
'+1234567', // only +1 and 6 digits
44+
'+12345678', // only +1 and 7 digits
45+
'+123456789' // only +1 and 9 digits
46+
];
47+
48+
invalidNumbers.forEach(number => {
49+
const result = validatePhoneNumber(number, mockHelpers);
50+
51+
expect(result).toEqual({ code: 'any.invalid', message: 'Validation error: any.invalid' });
52+
expect(mockHelpers.error).toHaveBeenCalledWith('any.invalid');
53+
});
54+
});
55+
});
56+
57+
describe('Invalid phone numbers', () => {
58+
it('should reject phone numbers that do not start with +', () => {
59+
const invalidNumbers = [
60+
'1234567890',
61+
'1555123456',
62+
'1987654321',
63+
'1123456789'
64+
];
65+
66+
invalidNumbers.forEach(number => {
67+
const result = validatePhoneNumber(number, mockHelpers);
68+
69+
expect(result).toEqual({ code: 'any.invalid', message: 'Validation error: any.invalid' });
70+
expect(mockHelpers.error).toHaveBeenCalledWith('any.invalid');
71+
});
72+
});
73+
74+
it('should reject phone numbers that do not start with +1', () => {
75+
const invalidNumbers = [
76+
'+21234567890', // +2
77+
'+31234567890', // +3
78+
'+41234567890', // +4
79+
'+51234567890', // +5
80+
'+61234567890', // +6
81+
'+71234567890', // +7
82+
'+81234567890', // +8
83+
'+91234567890' // +9
84+
];
85+
86+
invalidNumbers.forEach(number => {
87+
const result = validatePhoneNumber(number, mockHelpers);
88+
89+
expect(result).toEqual({ code: 'any.invalid', message: 'Validation error: any.invalid' });
90+
expect(mockHelpers.error).toHaveBeenCalledWith('any.invalid');
91+
});
92+
});
93+
94+
it('should reject phone numbers with non-digit characters after +1', () => {
95+
const invalidNumbers = [
96+
'+1a23456789',
97+
'+1-234-567-8900',
98+
'+1 (234) 567-8900',
99+
'+1.234.567.8900',
100+
'+1_234_567_8900',
101+
'+1 234 567 8900'
102+
];
103+
104+
invalidNumbers.forEach(number => {
105+
const result = validatePhoneNumber(number, mockHelpers);
106+
107+
expect(result).toEqual({ code: 'any.invalid', message: 'Validation error: any.invalid' });
108+
expect(mockHelpers.error).toHaveBeenCalledWith('any.invalid');
109+
});
110+
});
111+
112+
it('should reject phone numbers that are too short', () => {
113+
const invalidNumbers = [
114+
'+1', // Only +1, no digits
115+
'+', // Only +
116+
'' // Empty string
117+
];
118+
119+
invalidNumbers.forEach(number => {
120+
const result = validatePhoneNumber(number, mockHelpers);
121+
122+
expect(result).toEqual({ code: 'any.invalid', message: 'Validation error: any.invalid' });
123+
expect(mockHelpers.error).toHaveBeenCalledWith('any.invalid');
124+
});
125+
});
126+
});
127+
128+
describe('Edge cases', () => {
129+
it('should handle null and undefined values', () => {
130+
const invalidValues = [null, undefined];
131+
132+
invalidValues.forEach(value => {
133+
const result = validatePhoneNumber(value, mockHelpers);
134+
135+
expect(result).toEqual({ code: 'any.invalid', message: 'Validation error: any.invalid' });
136+
expect(mockHelpers.error).toHaveBeenCalledWith('any.invalid');
137+
});
138+
});
139+
140+
it('should handle non-string values', () => {
141+
const invalidValues = [123, {}, [], true, false];
142+
143+
invalidValues.forEach(value => {
144+
const result = validatePhoneNumber(value, mockHelpers);
145+
146+
expect(result).toEqual({ code: 'any.invalid', message: 'Validation error: any.invalid' });
147+
expect(mockHelpers.error).toHaveBeenCalledWith('any.invalid');
148+
});
149+
});
150+
151+
it('should handle strings with leading/trailing whitespace', () => {
152+
const invalidNumbers = [
153+
' +1234567890',
154+
'+1234567890 ',
155+
' +1234567890 ',
156+
'\t+1234567890',
157+
'+1234567890\n'
158+
];
159+
160+
invalidNumbers.forEach(number => {
161+
const result = validatePhoneNumber(number, mockHelpers);
162+
163+
expect(result).toEqual({ code: 'any.invalid', message: 'Validation error: any.invalid' });
164+
expect(mockHelpers.error).toHaveBeenCalledWith('any.invalid');
165+
});
166+
});
167+
});
168+
169+
describe('Function behavior', () => {
170+
it('should return the original value for valid phone numbers', () => {
171+
const validNumber = '+12345678901';
172+
const result = validatePhoneNumber(validNumber, mockHelpers);
173+
174+
expect(result).toBe(validNumber);
175+
expect(typeof result).toBe('string');
176+
});
177+
178+
it('should call helpers.error with correct error code for invalid numbers', () => {
179+
const invalidNumber = '1234567890';
180+
181+
validatePhoneNumber(invalidNumber, mockHelpers);
182+
183+
expect(mockHelpers.error).toHaveBeenCalledTimes(1);
184+
expect(mockHelpers.error).toHaveBeenCalledWith('any.invalid');
185+
});
186+
187+
it('should return error object from helpers.error for invalid numbers', () => {
188+
const invalidNumber = '1234567890';
189+
const result = validatePhoneNumber(invalidNumber, mockHelpers);
190+
191+
expect(result).toEqual({ code: 'any.invalid', message: 'Validation error: any.invalid' });
192+
});
193+
});
194+
});

0 commit comments

Comments
 (0)