Skip to content

Commit fa11db0

Browse files
authored
Merge pull request #65 from fredeil/copilot/add-unit-tests
Add unit tests for email validation edge cases
2 parents 43b9640 + a7da4bf commit fa11db0

1 file changed

Lines changed: 227 additions & 0 deletions

File tree

test/email_validator_test.dart

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,231 @@ void main() {
136136
reason: 'E-mail: ' + actual);
137137
}
138138
});
139+
140+
test('Validate empty and whitespace-only input is invalid', () {
141+
expect(EmailValidator.validate(''), equals(false));
142+
expect(EmailValidator.validate(' '), equals(false));
143+
expect(EmailValidator.validate('\t'), equals(false));
144+
});
145+
146+
test('Validate default parameter values', () {
147+
// Default: allowTopLevelDomains = false, allowInternational = true
148+
expect(EmailValidator.validate('user@example.com'), equals(true),
149+
reason: 'Standard email with defaults should be valid');
150+
expect(EmailValidator.validate('user@example'), equals(false),
151+
reason: 'Top-level domain should be rejected by default');
152+
expect(EmailValidator.validate('伊昭傑@郵件.商務'), equals(true),
153+
reason: 'International email should be valid by default');
154+
});
155+
156+
test('Validate allowTopLevelDomains parameter', () {
157+
expect(EmailValidator.validate('admin@mailserver', false), equals(false),
158+
reason:
159+
'TLD-only address should be invalid when allowTopLevelDomains is false');
160+
expect(EmailValidator.validate('admin@mailserver', true), equals(true),
161+
reason:
162+
'TLD-only address should be valid when allowTopLevelDomains is true');
163+
expect(EmailValidator.validate('user@example', true), equals(true),
164+
reason:
165+
'Single-label domain should be valid when allowTopLevelDomains is true');
166+
});
167+
168+
test('Validate allowInternational parameter rejects non-ASCII when false',
169+
() {
170+
expect(EmailValidator.validate('伊昭傑@郵件.商務', false, false), equals(false),
171+
reason:
172+
'International email should be invalid when allowInternational is false');
173+
expect(
174+
EmailValidator.validate('user@example.com', false, false), equals(true),
175+
reason: 'ASCII email should be valid regardless of allowInternational');
176+
});
177+
178+
test('Validate local-part length boundary', () {
179+
final local64 = 'a' * 64;
180+
final local65 = 'a' * 65;
181+
expect(EmailValidator.validate('$local64@x.org'), equals(true),
182+
reason: '64-character local-part should be valid');
183+
expect(EmailValidator.validate('$local65@x.org'), equals(false),
184+
reason: '65-character local-part should be invalid');
185+
});
186+
187+
test('Validate total email length boundary', () {
188+
// The validator rejects emails with length >= 255, so 254 is max valid
189+
const valid254 =
190+
'the-total-length@of-an-entire-address.cannot-be-longer-than-two-hundred-and-fifty-four-characters.and-this-address-is-254-characters-exactly.so-it-should-be-valid.and-im-going-to-add-some-more-words-here.to-increase-the-length-blah-blah-blah-blah-bla.org';
191+
const invalid255 =
192+
'the-total-length@of-an-entire-address.cannot-be-longer-than-two-hundred-and-fifty-four-characters.and-this-address-is-255-characters-exactly.so-it-should-be-invalid.and-im-going-to-add-some-more-words-here.to-increase-the-lenght-blah-blah-blah-blah-bl.org';
193+
expect(valid254.length, equals(254));
194+
expect(EmailValidator.validate(valid254), equals(true),
195+
reason: '254-character email should be valid');
196+
expect(invalid255.length, equals(255));
197+
expect(EmailValidator.validate(invalid255), equals(false),
198+
reason: '255-character email should be invalid');
199+
});
200+
201+
test('Validate domain label length boundary', () {
202+
expect(
203+
EmailValidator.validate(
204+
'the-character-limit@for-each-part.of-the-domain.is-sixty-three-characters.this-is-exactly-sixty-three-characters-so-it-is-valid-blah-blah.com'),
205+
equals(true),
206+
reason: '63-character domain label should be valid');
207+
expect(
208+
EmailValidator.validate(
209+
'the-character-limit@for-each-part.of-the-domain.is-sixty-three-characters.this-is-exactly-sixty-four-characters-so-it-is-invalid-blah-blah.com'),
210+
equals(false),
211+
reason: '64-character domain label should be invalid');
212+
});
213+
214+
test('Validate domain starting or ending with hyphen is invalid', () {
215+
expect(EmailValidator.validate('user@-example.com'), equals(false),
216+
reason: 'Domain starting with hyphen should be invalid');
217+
expect(EmailValidator.validate('user@example-.com'), equals(false),
218+
reason: 'Domain label ending with hyphen should be invalid');
219+
});
220+
221+
test('Validate double hyphens within domain are valid', () {
222+
expect(EmailValidator.validate('user@a--b.com'), equals(true),
223+
reason: 'Double hyphens within a domain label should be valid');
224+
});
225+
226+
test('Validate numeric-only TLD is invalid', () {
227+
expect(EmailValidator.validate('user@example.123'), equals(false),
228+
reason: 'Numeric-only TLD should be invalid');
229+
expect(EmailValidator.validate('user@123', true), equals(false),
230+
reason: 'Numeric-only single-label domain should be invalid');
231+
});
232+
233+
test('Validate domain with leading dot or trailing dot is invalid', () {
234+
expect(EmailValidator.validate('user@.com'), equals(false),
235+
reason: 'Domain with leading dot should be invalid');
236+
expect(EmailValidator.validate('user@com.'), equals(false),
237+
reason: 'Domain with trailing dot should be invalid');
238+
});
239+
240+
test('Validate multiple subdomains are valid', () {
241+
expect(EmailValidator.validate('user@sub.domain.example.com'), equals(true),
242+
reason: 'Multiple subdomains should be valid');
243+
expect(EmailValidator.validate('user@a.b.c.d.e.com'), equals(true),
244+
reason: 'Many subdomain levels should be valid');
245+
expect(EmailValidator.validate('user@example.co.uk'), equals(true),
246+
reason: 'Country code TLD with SLD should be valid');
247+
});
248+
249+
test('Validate local-part with dots', () {
250+
expect(EmailValidator.validate('.user@example.com'), equals(false),
251+
reason: 'Local-part starting with dot should be invalid');
252+
expect(EmailValidator.validate('user.@example.com'), equals(false),
253+
reason: 'Local-part ending with dot should be invalid');
254+
expect(EmailValidator.validate('user..name@example.com'), equals(false),
255+
reason: 'Consecutive dots in local-part should be invalid');
256+
expect(EmailValidator.validate('user.name@example.com'), equals(true),
257+
reason: 'Single dot in local-part should be valid');
258+
});
259+
260+
test('Validate missing local-part or domain is invalid', () {
261+
expect(EmailValidator.validate('@example.com'), equals(false),
262+
reason: 'Missing local-part should be invalid');
263+
expect(EmailValidator.validate('user@'), equals(false),
264+
reason: 'Missing domain should be invalid');
265+
expect(EmailValidator.validate('@'), equals(false),
266+
reason: 'Only @ sign should be invalid');
267+
});
268+
269+
test('Validate multiple @ signs is invalid', () {
270+
expect(EmailValidator.validate('user@@example.com'), equals(false),
271+
reason: 'Double @ should be invalid');
272+
});
273+
274+
test('Validate spaces in email are invalid', () {
275+
expect(EmailValidator.validate('user name@example.com'), equals(false),
276+
reason: 'Space in local-part should be invalid');
277+
expect(EmailValidator.validate('user@exam ple.com'), equals(false),
278+
reason: 'Space in domain should be invalid');
279+
});
280+
281+
test('Validate special characters in local-part', () {
282+
expect(EmailValidator.validate('user+tag@example.com'), equals(true),
283+
reason: 'Plus sign in local-part should be valid');
284+
expect(EmailValidator.validate('user+tag+tag2@example.com'), equals(true),
285+
reason: 'Multiple plus signs in local-part should be valid');
286+
});
287+
288+
test('Validate quoted strings edge cases', () {
289+
expect(EmailValidator.validate('"test"@example.com', true), equals(true),
290+
reason: 'Quoted local-part should be valid');
291+
expect(EmailValidator.validate('""@example.com', true), equals(true),
292+
reason: 'Empty quoted local-part should be valid');
293+
expect(EmailValidator.validate('"@"@example.com', true), equals(true),
294+
reason: 'Quoted @ sign in local-part should be valid');
295+
expect(EmailValidator.validate('"unclosed@example.com', true), equals(false),
296+
reason: 'Unclosed quote should be invalid');
297+
});
298+
299+
test('Validate IPv4 literal edge cases', () {
300+
expect(EmailValidator.validate('user@[255.255.255.255]'), equals(true),
301+
reason: 'Max octets IPv4 should be valid');
302+
expect(EmailValidator.validate('user@[256.0.0.0]'), equals(false),
303+
reason: 'IPv4 octet > 255 should be invalid');
304+
expect(EmailValidator.validate('user@[1.2.3]'), equals(false),
305+
reason: 'IPv4 with only 3 octets should be invalid');
306+
expect(EmailValidator.validate('user@[1.2.3.4.5]'), equals(false),
307+
reason: 'IPv4 with 5 octets should be invalid');
308+
expect(EmailValidator.validate('user@[1.2.3.]'), equals(false),
309+
reason: 'IPv4 with trailing dot should be invalid');
310+
});
311+
312+
test('Validate IPv6 literal edge cases', () {
313+
expect(EmailValidator.validate('user@[IPv6:::1]'), equals(true),
314+
reason: 'IPv6 loopback should be valid');
315+
expect(EmailValidator.validate('user@[IPv6:1::1]'), equals(true),
316+
reason: 'IPv6 compact form should be valid');
317+
expect(EmailValidator.validate('user@[IPv6:1:2:3:4:5:6:7:8]'), equals(true),
318+
reason: 'IPv6 full form should be valid');
319+
expect(
320+
EmailValidator.validate('user@[IPv6:1:2:3:4:5:6:7:8:9]'), equals(false),
321+
reason: 'IPv6 with too many groups should be invalid');
322+
});
323+
324+
test('Validate IPv6v4 literal edge cases', () {
325+
expect(
326+
EmailValidator.validate(
327+
'user@[IPv6:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:127.0.0.1]'),
328+
equals(true),
329+
reason: 'Valid IPv6v4 address should be valid');
330+
expect(
331+
EmailValidator.validate(
332+
'user@[IPv6:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:256.0.0.0]'),
333+
equals(false),
334+
reason: 'IPv6v4 with invalid IPv4 part should be invalid');
335+
});
336+
337+
test('Validate unbracketed IP domain is invalid', () {
338+
expect(EmailValidator.validate('user@123.123.123.123'), equals(false),
339+
reason:
340+
'Unbracketed IP should be treated as numeric domain and be invalid');
341+
});
342+
343+
test('Validate underscore in domain is invalid', () {
344+
expect(EmailValidator.validate('user@exam_ple.com'), equals(false),
345+
reason: 'Underscore in domain should be invalid');
346+
});
347+
348+
test('Validate single-character TLD is invalid', () {
349+
expect(EmailValidator.validate('a@b.c'), equals(false),
350+
reason: 'Single-character TLD should be invalid');
351+
});
352+
353+
test('Validate minimal valid email addresses', () {
354+
expect(EmailValidator.validate('a@b.cc'), equals(true),
355+
reason: 'Minimal valid email should pass');
356+
expect(EmailValidator.validate('a@bb.cc'), equals(true),
357+
reason: 'Minimal email with 2-char SLD should pass');
358+
});
359+
360+
test('Validate domain with numeric subdomain and alpha TLD', () {
361+
expect(EmailValidator.validate('user@123.com'), equals(true),
362+
reason: 'Numeric subdomain with alphabetic TLD should be valid');
363+
expect(EmailValidator.validate('user@123abc.com'), equals(true),
364+
reason: 'Alphanumeric subdomain should be valid');
365+
});
139366
}

0 commit comments

Comments
 (0)