Skip to content

Commit 35064bf

Browse files
committed
Add GPT-slop unit tests :)
1 parent 5798cca commit 35064bf

10 files changed

Lines changed: 867 additions & 132 deletions

File tree

Lines changed: 130 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,130 @@
1-
import { describe, expect, it } from 'vitest';
2-
import { validateEmail } from './emailValidator';
3-
4-
describe('email validator test', () => {
5-
it('empty email should return null', () => {
6-
const result = validateEmail('');
7-
expect(result).toEqual(null);
8-
});
9-
it('invalid email should return false and error', () => {
10-
const result = validateEmail('invalid');
11-
expect(result).toEqual({
12-
valid: false,
13-
message: 'Invalid email',
14-
});
15-
});
16-
it('email with alias should return false and error', () => {
17-
const result = validateEmail('john+actual.address@email.com');
18-
expect(result).toEqual({
19-
valid: false,
20-
message: 'Email cannot contain aliases',
21-
});
22-
});
23-
it('valid email should return true and no error', () => {
24-
const result = validateEmail('valid.address@email.com');
25-
expect(result).toEqual({
26-
valid: true,
27-
});
28-
});
29-
});
1+
import { describe, it, expect } from 'vitest'
2+
import { isEmailAddress, validateEmail } from './emailValidator'
3+
4+
describe('isEmailAddress', () => {
5+
it('returns false for empty string', () => {
6+
expect(isEmailAddress('')).toBe(false)
7+
})
8+
9+
it('returns false when missing "@" or has multiple "@"', () => {
10+
expect(isEmailAddress('plainaddress')).toBe(false)
11+
expect(isEmailAddress('a@b@c.com')).toBe(false)
12+
})
13+
14+
it('returns false when total length exceeds 320 characters', () => {
15+
const localPart = 'a'.repeat(64)
16+
const domainLabel = 'b'.repeat(63)
17+
const domain = Array(5).fill(domainLabel).join('.') // 5×63 + 4 dots = 319
18+
const validEmail = `${localPart}@${domain}` // 384 chars total
19+
expect(validEmail.length).toBe(64 + 1 + 319)
20+
expect(isEmailAddress(validEmail)).toBe(false)
21+
})
22+
23+
it('returns false when local part exceeds 64 characters', () => {
24+
const account = 'x'.repeat(65)
25+
const address = 'domain.com'
26+
expect(isEmailAddress(`${account}@${address}`)).toBe(false)
27+
})
28+
29+
it('returns false when domain part exceeds 255 characters', () => {
30+
const account = 'user'
31+
const longLabel = 'd'.repeat(63)
32+
// 4 labels of 63 chars + 3 dots = 4*63 + 3 = 255
33+
const domain = Array(4).fill(longLabel).join('.')
34+
expect(domain.length).toBe(255)
35+
// Prepend one character to push domain over 255
36+
const tooLongDomain = 'e' + domain
37+
expect(isEmailAddress(`${account}@${tooLongDomain}`)).toBe(false)
38+
})
39+
40+
it('returns false when any domain label exceeds 63 characters', () => {
41+
const account = 'user'
42+
const longLabel = 'd'.repeat(64)
43+
const address = `${longLabel}.com`
44+
expect(isEmailAddress(`${account}@${address}`)).toBe(false)
45+
})
46+
47+
it('returns false for invalid characters or formats not matching regex', () => {
48+
// starts with a dot
49+
expect(isEmailAddress('.user@domain.com')).toBe(false)
50+
// consecutive dots in local part
51+
expect(isEmailAddress('user..name@domain.com')).toBe(false)
52+
// hyphen at start of domain label
53+
expect(isEmailAddress('user@-domain.com')).toBe(false)
54+
// no TLD
55+
expect(isEmailAddress('user@domain')).toBe(false)
56+
})
57+
58+
it('returns true for valid standard email addresses', () => {
59+
expect(isEmailAddress('simple@example.com')).toBe(true)
60+
expect(isEmailAddress('very.common@example.co.uk')).toBe(true)
61+
expect(isEmailAddress("o'reilly@example.io")).toBe(true)
62+
expect(isEmailAddress('user_name-123@sub.domain.com')).toBe(true)
63+
})
64+
})
65+
66+
describe('validateEmail', () => {
67+
it('returns null for empty input', () => {
68+
const result = validateEmail('')
69+
expect(result).toBeNull()
70+
})
71+
72+
it('returns valid: false and message "Invalid email" for syntactically invalid addresses', () => {
73+
const invalids = [
74+
'noatsign.com',
75+
'user@.com',
76+
'user@domain..com',
77+
'user@domain',
78+
'toolong' + 'a'.repeat(320) + '@example.com',
79+
]
80+
for (const addr of invalids) {
81+
const result = validateEmail(addr)
82+
expect(result).not.toBeNull()
83+
expect(result?.valid).toBe(false)
84+
expect(result?.message).toBe('Invalid email')
85+
}
86+
})
87+
88+
it('returns valid: false and message "Email cannot contain aliases" when email contains "+"', () => {
89+
const withAlias = 'user+alias@example.com'
90+
const result = validateEmail(withAlias)
91+
expect(result).not.toBeNull()
92+
expect(result?.valid).toBe(false)
93+
expect(result?.message).toBe('Email cannot contain aliases')
94+
})
95+
96+
it('returns valid: true for valid email without "+"', () => {
97+
const valid = 'firstname.lastname@domain.com'
98+
const result = validateEmail(valid)
99+
expect(result).not.toBeNull()
100+
expect(result?.valid).toBe(true)
101+
expect(result).not.toHaveProperty('message')
102+
})
103+
104+
it('treats "+" anywhere in the local part as invalid alias', () => {
105+
const examples = [
106+
'plus+sign@domain.com',
107+
'+start@domain.com',
108+
'end+@domain.com',
109+
'middle+more@sub.domain.org',
110+
]
111+
for (const addr of examples) {
112+
const result = validateEmail(addr)
113+
expect(result).not.toBeNull()
114+
expect(result?.valid).toBe(false)
115+
expect(result?.message).toBe('Email cannot contain aliases')
116+
}
117+
})
118+
119+
it('does not reject valid emails with hyphens in domain or underscores in local part', () => {
120+
const examples = [
121+
'user_name@example-domain.com', // underscore in local, hyphen in domain
122+
'another.user-name@sub.domain.com', // hyphen in both portions
123+
]
124+
for (const addr of examples) {
125+
const result = validateEmail(addr)
126+
expect(result).not.toBeNull()
127+
expect(result?.valid).toBe(true)
128+
}
129+
})
130+
})
Lines changed: 135 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,135 @@
1-
import { describe, expect, it } from 'vitest';
2-
import { validatePassword, validatePasswordMatch } from './passwordValidator';
3-
4-
describe('email validator test', () => {
5-
it('empty password should return null', () => {
6-
const result = validatePassword('');
7-
expect(result).toEqual(null);
8-
});
9-
it('password with less than 12 characters should return false and error', () => {
10-
const result = validatePassword('obfuscation');
11-
expect(result).toEqual({
12-
valid: false,
13-
message: 'Password is too short',
14-
});
15-
});
16-
it('password with length more than 256 characters should return false and error', () => {
17-
const result = validatePassword('a'.repeat(257));
18-
expect(result).toEqual({
19-
valid: false,
20-
message: `Seriously? 257 characters? That's too much`,
21-
});
22-
});
23-
it('password with only whitespaces should return false and error', () => {
24-
const result = validatePassword(' \r \n \t \r \n ');
25-
expect(result).toEqual({
26-
valid: false,
27-
message: 'Password cannot consist of only whitespaces',
28-
});
29-
});
30-
it('password starting with whitespace should return false and error', () => {
31-
const result = validatePassword(' myinsecurepassword123');
32-
expect(result).toEqual({
33-
valid: false,
34-
message: 'Password cannot start or end with whitespace',
35-
});
36-
});
37-
it('password ending with whitespace should return false and error', () => {
38-
const result = validatePassword('myinsecurepassword123 ');
39-
expect(result).toEqual({
40-
valid: false,
41-
message: 'Password cannot start or end with whitespace',
42-
});
43-
});
44-
it('password match with different password should return false and error', () => {
45-
const result = validatePasswordMatch('password', 'differentpassword');
46-
expect(result).toEqual({
47-
valid: false,
48-
message: 'Passwords do not match',
49-
});
50-
});
51-
it('password match with invalid password should return false and error', () => {
52-
const result = validatePasswordMatch('password', 'password');
53-
expect(result).toEqual({
54-
valid: true,
55-
});
56-
});
57-
});
1+
import { describe, it, expect } from 'vitest'
2+
import {
3+
validatePassword,
4+
validatePasswordMatch,
5+
getPasswordStrength,
6+
} from './passwordValidator'
7+
8+
describe('validatePassword', () => {
9+
it('returns null for empty password', () => {
10+
const result = validatePassword('')
11+
expect(result).toBeNull()
12+
})
13+
14+
it('rejects passwords shorter than 12 characters', () => {
15+
const result = validatePassword('shortPwd1!')
16+
expect(result).not.toBeNull()
17+
expect(result?.valid).toBe(false)
18+
expect(result?.message).toBe('Password is too short')
19+
})
20+
21+
it('rejects passwords longer than 256 characters', () => {
22+
const longPwd = 'a'.repeat(257)
23+
const result = validatePassword(longPwd)
24+
expect(result).not.toBeNull()
25+
expect(result?.valid).toBe(false)
26+
expect(result?.message).toBe(`Seriously? ${longPwd.length} characters? That's too much`)
27+
})
28+
29+
it('rejects passwords consisting only of whitespaces', () => {
30+
const spaces = ' \r \n \t \r \n '
31+
const result = validatePassword(spaces)
32+
expect(result).not.toBeNull()
33+
expect(result?.valid).toBe(false)
34+
expect(result?.message).toBe('Password cannot consist of only whitespaces')
35+
})
36+
37+
it('rejects passwords that start or end with whitespace', () => {
38+
const leading = ' validPassword123'
39+
const trailing = 'validPassword123 '
40+
const both = ' validPassword123 '
41+
for (const pwd of [leading, trailing, both]) {
42+
const result = validatePassword(pwd)
43+
expect(result).not.toBeNull()
44+
expect(result?.valid).toBe(false)
45+
expect(result?.message).toBe('Password cannot start or end with whitespace')
46+
}
47+
})
48+
49+
it('accepts valid passwords of acceptable length and no leading/trailing whitespace', () => {
50+
const valid = 'ValidPassword123'
51+
const result = validatePassword(valid)
52+
expect(result).not.toBeNull()
53+
expect(result?.valid).toBe(true)
54+
expect(result).not.toHaveProperty('message')
55+
})
56+
})
57+
58+
describe('validatePasswordMatch', () => {
59+
it('returns null when original password is empty', () => {
60+
const result = validatePasswordMatch('', 'anything')
61+
expect(result).toBeNull()
62+
})
63+
64+
it('rejects non-matching passwords', () => {
65+
const result = validatePasswordMatch('password123', 'password124')
66+
expect(result).not.toBeNull()
67+
expect(result?.valid).toBe(false)
68+
expect(result?.message).toBe('Passwords do not match')
69+
})
70+
71+
it('accepts matching passwords', () => {
72+
const result = validatePasswordMatch('password123', 'password123')
73+
expect(result).not.toBeNull()
74+
expect(result?.valid).toBe(true)
75+
expect(result).not.toHaveProperty('message')
76+
})
77+
})
78+
79+
describe('getPasswordStrength', () => {
80+
it('returns None/gray-500/0% for empty string', () => {
81+
const { percent, text, color } = getPasswordStrength('')
82+
expect(percent).toBe(0)
83+
expect(text).toBe('None')
84+
expect(color).toBe('gray-500')
85+
})
86+
87+
it('rates a repeated single-character password as Very weak (red-500)', () => {
88+
// "aaaaaaaaaaaa" (12×'a'): uniqueLength=1 ⇒ entropy=0 ⇒ percent=0 ⇒ <30
89+
const pwd = 'a'.repeat(12)
90+
const { percent, text, color } = getPasswordStrength(pwd)
91+
expect(percent).toBe(0)
92+
expect(text).toBe('Very weak')
93+
expect(color).toBe('red-500')
94+
})
95+
96+
it('rates a moderately varied 12-character password as Weak (orange-500)', () => {
97+
// "abcABC123!@#": length=12, uniqueLength=12 ⇒ entropy≈43 ⇒ percent≈35.8 ⇒ <50
98+
const pwd = 'abcABC123!@#'
99+
const { percent, text, color } = getPasswordStrength(pwd)
100+
expect(percent).toBeGreaterThanOrEqual(30)
101+
expect(percent).toBeLessThan(50)
102+
expect(text).toBe('Weak')
103+
expect(color).toBe('orange-500')
104+
})
105+
106+
it('rates a 60-character password with two unique chars as Fair (yellow-500)', () => {
107+
// 60×(two-character alphabet) ⇒ entropy=60*1=60 ⇒ percent=50 ⇒ <60
108+
const pwd = 'ab'.repeat(30)
109+
const { percent, text, color } = getPasswordStrength(pwd)
110+
expect(percent).toBeCloseTo(50, 1)
111+
expect(text).toBe('Fair')
112+
expect(color).toBe('yellow-500')
113+
})
114+
115+
it('rates a 72-character password with two unique chars as Strong (green-500)', () => {
116+
// 72×(two-character alphabet) ⇒ entropy=72 ⇒ percent=60 ⇒ <99
117+
const pwd = 'ab'.repeat(36)
118+
const { percent, text, color } = getPasswordStrength(pwd)
119+
expect(percent).toBeCloseTo(60, 1)
120+
expect(text).toBe('Strong')
121+
expect(color).toBe('green-500')
122+
})
123+
124+
it('caps strength at 100% and rates as Very strong (cyan-500) for high entropy', () => {
125+
// 100 random characters
126+
const randomBytes = new Uint8Array(100)
127+
crypto.getRandomValues(randomBytes)
128+
const pwd = Array.from(randomBytes, b => String.fromCharCode(b % 94 + 33)).join('')
129+
130+
const { percent, text, color } = getPasswordStrength(pwd)
131+
expect(percent).toBe(100)
132+
expect(text).toBe('Very strong')
133+
expect(color).toBe('cyan-500')
134+
})
135+
})

0 commit comments

Comments
 (0)