Skip to content

Commit 3f64772

Browse files
waleedlatif1claude
andcommitted
refactor(validation): remove dead validateEmail and checkMXRecord
Server-side disposable email blocking is now handled by better-auth-harmony. The async validateEmail (with MX check) had no remaining callers. Only quickValidateEmail remains for client-side form feedback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 3cd1d19 commit 3f64772

File tree

2 files changed

+125
-331
lines changed

2 files changed

+125
-331
lines changed
Lines changed: 123 additions & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -1,228 +1,147 @@
1-
import { loggerMock } from '@sim/testing'
2-
import { describe, expect, it, vi } from 'vitest'
3-
import { quickValidateEmail, validateEmail } from '@/lib/messaging/email/validation'
4-
5-
vi.mock('@sim/logger', () => loggerMock)
6-
7-
vi.mock('dns', () => ({
8-
resolveMx: (
9-
_domain: string,
10-
callback: (err: Error | null, addresses: { exchange: string; priority: number }[]) => void
11-
) => {
12-
callback(null, [{ exchange: 'mail.example.com', priority: 10 }])
13-
},
14-
}))
15-
16-
describe('Email Validation', () => {
17-
describe('validateEmail', () => {
18-
it.concurrent('should validate a correct email', async () => {
19-
const result = await validateEmail('user@example.com')
20-
expect(result.isValid).toBe(true)
21-
expect(result.checks.syntax).toBe(true)
22-
expect(result.checks.disposable).toBe(true)
23-
})
1+
import { describe, expect, it } from 'vitest'
2+
import { quickValidateEmail } from '@/lib/messaging/email/validation'
3+
4+
describe('quickValidateEmail', () => {
5+
it.concurrent('should validate a correct email', () => {
6+
const result = quickValidateEmail('user@example.com')
7+
expect(result.isValid).toBe(true)
8+
expect(result.checks.syntax).toBe(true)
9+
expect(result.checks.disposable).toBe(true)
10+
expect(result.checks.mxRecord).toBe(true)
11+
expect(result.confidence).toBe('medium')
12+
})
2413

25-
it.concurrent('should reject invalid syntax', async () => {
26-
const result = await validateEmail('invalid-email')
27-
expect(result.isValid).toBe(false)
28-
expect(result.reason).toBe('Invalid email format')
29-
expect(result.checks.syntax).toBe(false)
30-
})
14+
it.concurrent('should reject invalid syntax', () => {
15+
const result = quickValidateEmail('invalid-email')
16+
expect(result.isValid).toBe(false)
17+
expect(result.reason).toBe('Invalid email format')
18+
})
3119

32-
it.concurrent('should reject disposable email addresses', async () => {
33-
const result = await validateEmail('test@10minutemail.com')
20+
it.concurrent('should reject disposable email addresses', () => {
21+
const disposableDomains = [
22+
'mailinator.com',
23+
'yopmail.com',
24+
'guerrillamail.com',
25+
'temp-mail.org',
26+
'throwaway.email',
27+
'getnada.com',
28+
'sharklasers.com',
29+
'spam4.me',
30+
'sharebot.net',
31+
'oakon.com',
32+
'catchmail.io',
33+
'salt.email',
34+
'mail.gw',
35+
'tempmail.org',
36+
]
37+
38+
for (const domain of disposableDomains) {
39+
const result = quickValidateEmail(`test@${domain}`)
3440
expect(result.isValid).toBe(false)
3541
expect(result.reason).toBe('Disposable email addresses are not allowed')
3642
expect(result.checks.disposable).toBe(false)
37-
})
43+
}
44+
})
3845

39-
it.concurrent('should reject consecutive dots (RFC violation)', async () => {
40-
const result = await validateEmail('user..name@example.com')
41-
expect(result.isValid).toBe(false)
42-
expect(result.reason).toBe('Email contains suspicious patterns')
43-
})
46+
it.concurrent('should reject consecutive dots (RFC violation)', () => {
47+
const result = quickValidateEmail('user..name@example.com')
48+
expect(result.isValid).toBe(false)
49+
expect(result.reason).toBe('Email contains suspicious patterns')
50+
expect(result.confidence).toBe('medium')
51+
})
4452

45-
it.concurrent('should reject very long local parts (RFC violation)', async () => {
46-
const longLocalPart = 'a'.repeat(65)
47-
const result = await validateEmail(`${longLocalPart}@example.com`)
48-
expect(result.isValid).toBe(false)
49-
expect(result.reason).toBe('Email contains suspicious patterns')
50-
})
53+
it.concurrent('should reject very long local parts (RFC violation)', () => {
54+
const longLocalPart = 'a'.repeat(65)
55+
const result = quickValidateEmail(`${longLocalPart}@example.com`)
56+
expect(result.isValid).toBe(false)
57+
expect(result.reason).toBe('Email contains suspicious patterns')
58+
})
5159

52-
it.concurrent('should reject email with missing domain', async () => {
53-
const result = await validateEmail('user@')
54-
expect(result.isValid).toBe(false)
55-
expect(result.reason).toBe('Invalid email format')
56-
})
60+
it.concurrent('should reject email with missing domain', () => {
61+
const result = quickValidateEmail('user@')
62+
expect(result.isValid).toBe(false)
63+
expect(result.reason).toBe('Invalid email format')
64+
})
5765

58-
it.concurrent('should reject email with domain starting with dot', async () => {
59-
const result = await validateEmail('user@.example.com')
60-
expect(result.isValid).toBe(false)
61-
expect(result.reason).toBe('Invalid email format')
62-
})
66+
it.concurrent('should reject email with domain starting with dot', () => {
67+
const result = quickValidateEmail('user@.example.com')
68+
expect(result.isValid).toBe(false)
69+
expect(result.reason).toBe('Invalid email format')
70+
})
6371

64-
it.concurrent('should reject email with domain ending with dot', async () => {
65-
const result = await validateEmail('user@example.')
66-
expect(result.isValid).toBe(false)
67-
expect(result.reason).toBe('Invalid email format')
68-
})
72+
it.concurrent('should reject email with domain ending with dot', () => {
73+
const result = quickValidateEmail('user@example.')
74+
expect(result.isValid).toBe(false)
75+
expect(result.reason).toBe('Invalid email format')
76+
})
6977

70-
it.concurrent('should reject email with domain missing TLD', async () => {
71-
const result = await validateEmail('user@localhost')
72-
expect(result.isValid).toBe(false)
73-
expect(result.reason).toBe('Invalid domain format')
74-
})
78+
it.concurrent('should reject email with domain missing TLD', () => {
79+
const result = quickValidateEmail('user@localhost')
80+
expect(result.isValid).toBe(false)
81+
expect(result.reason).toBe('Invalid domain format')
82+
})
7583

76-
it.concurrent('should reject email longer than 254 characters', async () => {
77-
const longLocal = 'a'.repeat(64)
78-
const longDomain = `${'b'.repeat(180)}.com`
79-
const result = await validateEmail(`${longLocal}@${longDomain}`)
80-
expect(result.isValid).toBe(false)
81-
})
82-
83-
it.concurrent('should validate various known disposable email domains', async () => {
84-
const disposableDomains = [
85-
'mailinator.com',
86-
'yopmail.com',
87-
'guerrillamail.com',
88-
'temp-mail.org',
89-
'throwaway.email',
90-
'getnada.com',
91-
'sharklasers.com',
92-
'spam4.me',
93-
'sharebot.net',
94-
'oakon.com',
95-
'catchmail.io',
96-
'salt.email',
97-
'mail.gw',
98-
]
99-
100-
for (const domain of disposableDomains) {
101-
const result = await validateEmail(`test@${domain}`)
102-
expect(result.isValid).toBe(false)
103-
expect(result.reason).toBe('Disposable email addresses are not allowed')
104-
expect(result.checks.disposable).toBe(false)
105-
}
106-
})
107-
108-
it.concurrent('should accept valid email formats', async () => {
109-
const validEmails = [
110-
'simple@example.com',
111-
'very.common@example.com',
112-
'disposable.style.email.with+symbol@example.com',
113-
'other.email-with-hyphen@example.com',
114-
'fully-qualified-domain@example.com',
115-
'user.name+tag+sorting@example.com',
116-
'x@example.com',
117-
'example-indeed@strange-example.com',
118-
'example@s.example',
119-
]
120-
121-
for (const email of validEmails) {
122-
const result = await validateEmail(email)
123-
expect(result.checks.syntax).toBe(true)
124-
expect(result.checks.disposable).toBe(true)
125-
}
126-
})
127-
128-
it.concurrent('should return high confidence for syntax failures', async () => {
129-
const result = await validateEmail('not-an-email')
130-
expect(result.confidence).toBe('high')
131-
})
132-
133-
it.concurrent('should handle email with special characters in local part', async () => {
134-
const result = await validateEmail("user!#$%&'*+/=?^_`{|}~@example.com")
135-
expect(result.checks.syntax).toBe(true)
136-
})
84+
it.concurrent('should reject email longer than 254 characters', () => {
85+
const longLocal = 'a'.repeat(64)
86+
const longDomain = `${'b'.repeat(180)}.com`
87+
const result = quickValidateEmail(`${longLocal}@${longDomain}`)
88+
expect(result.isValid).toBe(false)
13789
})
13890

139-
describe('quickValidateEmail', () => {
140-
it.concurrent('should validate quickly without MX check', () => {
141-
const result = quickValidateEmail('user@example.com')
91+
it.concurrent('should accept valid email formats', () => {
92+
const validEmails = [
93+
'simple@example.com',
94+
'very.common@example.com',
95+
'disposable.style.email.with+symbol@example.com',
96+
'other.email-with-hyphen@example.com',
97+
'user.name+tag+sorting@example.com',
98+
'x@example.com',
99+
'example-indeed@strange-example.com',
100+
'example@s.example',
101+
]
102+
103+
for (const email of validEmails) {
104+
const result = quickValidateEmail(email)
142105
expect(result.isValid).toBe(true)
143-
expect(result.checks.mxRecord).toBe(true)
144-
expect(result.confidence).toBe('medium')
145-
})
146-
147-
it.concurrent('should reject invalid emails quickly', () => {
148-
const result = quickValidateEmail('invalid-email')
149-
expect(result.isValid).toBe(false)
150-
expect(result.reason).toBe('Invalid email format')
151-
})
152-
153-
it.concurrent('should reject disposable emails quickly', () => {
154-
const result = quickValidateEmail('test@tempmail.org')
155-
expect(result.isValid).toBe(false)
156-
expect(result.reason).toBe('Disposable email addresses are not allowed')
157-
})
158-
159-
it.concurrent('should reject email with missing domain', () => {
160-
const result = quickValidateEmail('user@')
161-
expect(result.isValid).toBe(false)
162-
expect(result.reason).toBe('Invalid email format')
163-
})
164-
165-
it.concurrent('should reject email with invalid domain format', () => {
166-
const result = quickValidateEmail('user@.invalid')
167-
expect(result.isValid).toBe(false)
168-
expect(result.reason).toBe('Invalid email format')
169-
})
170-
171-
it.concurrent('should return medium confidence for suspicious patterns', () => {
172-
const result = quickValidateEmail('user..double@example.com')
173-
expect(result.isValid).toBe(false)
174-
expect(result.reason).toBe('Email contains suspicious patterns')
175-
expect(result.confidence).toBe('medium')
176-
})
106+
expect(result.checks.syntax).toBe(true)
107+
expect(result.checks.disposable).toBe(true)
108+
}
109+
})
177110

178-
it.concurrent('should return high confidence for syntax errors', () => {
179-
const result = quickValidateEmail('not-valid-email')
180-
expect(result.confidence).toBe('high')
181-
})
111+
it.concurrent('should return high confidence for syntax errors', () => {
112+
const result = quickValidateEmail('not-valid-email')
113+
expect(result.confidence).toBe('high')
114+
})
182115

183-
it.concurrent('should handle empty string', () => {
184-
const result = quickValidateEmail('')
185-
expect(result.isValid).toBe(false)
186-
expect(result.reason).toBe('Invalid email format')
187-
})
116+
it.concurrent('should handle special characters in local part', () => {
117+
const result = quickValidateEmail("user!#$%&'*+/=?^_`{|}~@example.com")
118+
expect(result.checks.syntax).toBe(true)
119+
})
188120

189-
it.concurrent('should handle email with only @ symbol', () => {
190-
const result = quickValidateEmail('@')
191-
expect(result.isValid).toBe(false)
192-
expect(result.reason).toBe('Invalid email format')
193-
})
121+
it.concurrent('should handle empty string', () => {
122+
const result = quickValidateEmail('')
123+
expect(result.isValid).toBe(false)
124+
expect(result.reason).toBe('Invalid email format')
125+
})
194126

195-
it.concurrent('should handle email with spaces', () => {
196-
const result = quickValidateEmail('user name@example.com')
197-
expect(result.isValid).toBe(false)
198-
expect(result.reason).toBe('Invalid email format')
199-
})
127+
it.concurrent('should handle email with only @ symbol', () => {
128+
const result = quickValidateEmail('@')
129+
expect(result.isValid).toBe(false)
130+
})
200131

201-
it.concurrent('should handle email with multiple @ symbols', () => {
202-
const result = quickValidateEmail('user@domain@example.com')
203-
expect(result.isValid).toBe(false)
204-
expect(result.reason).toBe('Invalid email format')
205-
})
132+
it.concurrent('should handle email with spaces', () => {
133+
const result = quickValidateEmail('user name@example.com')
134+
expect(result.isValid).toBe(false)
135+
})
206136

207-
it.concurrent('should validate complex but valid local parts', () => {
208-
const result = quickValidateEmail('user+tag@example.com')
209-
expect(result.isValid).toBe(true)
210-
expect(result.checks.syntax).toBe(true)
211-
})
137+
it.concurrent('should handle email with multiple @ symbols', () => {
138+
const result = quickValidateEmail('user@domain@example.com')
139+
expect(result.isValid).toBe(false)
140+
})
212141

213-
it.concurrent('should validate subdomains', () => {
214-
const result = quickValidateEmail('user@mail.subdomain.example.com')
215-
expect(result.isValid).toBe(true)
216-
expect(result.checks.domain).toBe(true)
217-
})
218-
219-
it.concurrent('should reject spam domains from the inline blocklist', () => {
220-
const spamDomains = ['sharebot.net', 'oakon.com', 'catchmail.io', 'salt.email', 'mail.gw']
221-
for (const domain of spamDomains) {
222-
const result = quickValidateEmail(`user@${domain}`)
223-
expect(result.isValid).toBe(false)
224-
expect(result.reason).toBe('Disposable email addresses are not allowed')
225-
}
226-
})
142+
it.concurrent('should validate subdomains', () => {
143+
const result = quickValidateEmail('user@mail.subdomain.example.com')
144+
expect(result.isValid).toBe(true)
145+
expect(result.checks.domain).toBe(true)
227146
})
228147
})

0 commit comments

Comments
 (0)