import { describe, it, expect } from 'vitest'; import { isValidEmail, validateEmailList, getEmailValidationError, isValidUnsubscribeUrl, parseUnsubscribeUrls, } from '../validation'; describe('validation', () => { describe('isValidEmail', () => { it('should accept valid basic emails', () => { expect(isValidEmail('user@example.com')).toBe(true); expect(isValidEmail('john.doe@company.co.uk')).toBe(true); expect(isValidEmail('test_user@subdomain.example.com')).toBe(true); }); it('should accept emails with plus addressing', () => { expect(isValidEmail('user+tag@example.com')).toBe(true); expect(isValidEmail('user+shopping@example.com')).toBe(true); }); it('should accept various valid formats', () => { expect(isValidEmail('a@b.co')).toBe(true); expect(isValidEmail('user123@test-domain.com')).toBe(true); expect(isValidEmail('first.last+tag@example.co.uk')).toBe(true); }); it('should reject emails without @ symbol', () => { expect(isValidEmail('userexample.com')).toBe(false); expect(isValidEmail('user')).toBe(false); }); it('should reject emails without domain', () => { expect(isValidEmail('user@')).toBe(false); expect(isValidEmail('@example.com')).toBe(false); }); it('should reject header injection attempts', () => { expect(isValidEmail('test\r\nBcc:evil@example.com')).toBe(false); expect(isValidEmail('test\rBcc:evil@example.com')).toBe(false); expect(isValidEmail('test\nBcc:evil@example.com')).toBe(false); }); it('should reject emails with dangerous characters', () => { expect(isValidEmail('test')).toBe(false); expect(isValidUnsubscribeUrl('data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==')).toBe(false); }); it('should reject file: protocol', () => { expect(isValidUnsubscribeUrl('file:///etc/passwd')).toBe(false); expect(isValidUnsubscribeUrl('file://C:/Windows/System32/config')).toBe(false); }); it('should reject vbscript: protocol', () => { expect(isValidUnsubscribeUrl('vbscript:msgbox(1)')).toBe(false); }); it('should reject about: protocol', () => { expect(isValidUnsubscribeUrl('about:blank')).toBe(false); }); it('should reject ftp: protocol', () => { expect(isValidUnsubscribeUrl('ftp://example.com/file')).toBe(false); }); }); describe('edge cases', () => { it('should reject empty or null input', () => { expect(isValidUnsubscribeUrl('')).toBe(false); expect(isValidUnsubscribeUrl(' ')).toBe(false); }); it('should reject malformed URLs', () => { expect(isValidUnsubscribeUrl('not-a-url')).toBe(false); expect(isValidUnsubscribeUrl('example.com/unsub')).toBe(false); expect(isValidUnsubscribeUrl('//example.com')).toBe(false); }); it('should reject relative URLs', () => { expect(isValidUnsubscribeUrl('/unsubscribe')).toBe(false); expect(isValidUnsubscribeUrl('../unsub')).toBe(false); }); }); }); describe('parseUnsubscribeUrls', () => { it('should parse single HTTP URL', () => { const result = parseUnsubscribeUrls(''); expect(result.http).toBe('https://example.com/unsub'); expect(result.mailto).toBeUndefined(); expect(result.preferred).toBe('http'); }); it('should parse single mailto URL', () => { const result = parseUnsubscribeUrls(''); expect(result.http).toBeUndefined(); expect(result.mailto).toBe('mailto:unsub@example.com'); expect(result.preferred).toBe('mailto'); }); it('should parse multiple URLs and prefer HTTP', () => { const result = parseUnsubscribeUrls(', '); expect(result.http).toBe('https://example.com/unsub'); expect(result.mailto).toBe('mailto:unsub@example.com'); expect(result.preferred).toBe('http'); }); it('should prefer HTTP over mailto when both present', () => { const result = parseUnsubscribeUrls(', '); expect(result.http).toBe('https://example.com/unsub'); expect(result.mailto).toBe('mailto:unsub@example.com'); expect(result.preferred).toBe('http'); }); it('should handle URLs with query parameters', () => { const result = parseUnsubscribeUrls(''); expect(result.http).toBe('https://example.com/unsub?token=abc123&id=456'); expect(result.preferred).toBe('http'); }); it('should handle mailto with query parameters', () => { const result = parseUnsubscribeUrls(''); expect(result.mailto).toBe('mailto:unsub@example.com?subject=Unsubscribe&body=Remove'); expect(result.preferred).toBe('mailto'); }); it('should filter out invalid URLs', () => { const result = parseUnsubscribeUrls(', '); expect(result.http).toBe('https://example.com/unsub'); expect(result.preferred).toBe('http'); }); it('should return empty object for all invalid URLs', () => { const result = parseUnsubscribeUrls(', >'); expect(result.http).toBeUndefined(); expect(result.mailto).toBeUndefined(); expect(result.preferred).toBeUndefined(); }); it('should handle empty or null input', () => { expect(parseUnsubscribeUrls('')).toEqual({}); expect(parseUnsubscribeUrls(' ')).toEqual({}); }); it('should handle malformed headers without angle brackets', () => { const result = parseUnsubscribeUrls('https://example.com/unsub'); expect(result).toEqual({}); }); it('should handle whitespace in headers', () => { const result = parseUnsubscribeUrls(' , '); expect(result.http).toBe('https://example.com/unsub'); expect(result.mailto).toBe('mailto:unsub@example.com'); expect(result.preferred).toBe('http'); }); it('should handle three or more URLs', () => { const result = parseUnsubscribeUrls( ', , ' ); expect(result.http).toBeDefined(); expect(result.mailto).toBe('mailto:unsub@example.com'); expect(result.preferred).toBe('http'); }); it('should validate email addresses in mailto URLs', () => { const result = parseUnsubscribeUrls(', '); expect(result.mailto).toBeUndefined(); expect(result.http).toBe('https://example.com/unsub'); expect(result.preferred).toBe('http'); }); }); });