wulf-pulse/lib/services/campaign-grouping-service.test.ts
lorentz ea677b7aba test(18-01): add campaign-grouping tier-key helpers + test scaffold
- normalizeSubject strips repeated Re:/Fwd:/Fw: prefixes case-insensitively, lowercases, trims (D-03)
- extractUrlDomain returns hostname or null (never throws) for malformed URLs (Pitfall 4)
- test file mocks ./postgres-client before import, mirroring phishing-eml-service.test.ts's discipline
2026-07-15 19:19:44 -04:00

50 lines
1.6 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest';
// Mock postgresClient BEFORE importing the module under test.
const queryMock = vi.fn();
const transactionMock = vi.fn();
vi.mock('./postgres-client', () => ({
postgresClient: {
query: (...args: unknown[]) => queryMock(...args),
transaction: (...args: unknown[]) => transactionMock(...args),
},
}));
// eslint-disable-next-line import/first -- imported after vi.mock hoisting
import { normalizeSubject, extractUrlDomain } from './campaign-grouping-service';
describe('normalizeSubject', () => {
it('strips a single Re: prefix, lowercases, trims', () => {
expect(normalizeSubject('Re: Your Invoice ')).toBe('your invoice');
});
it('strips repeated Re:/Fwd:/Fw: prefixes case-insensitively', () => {
expect(normalizeSubject('FW: Re: fwd: Urgent Payment')).toBe('urgent payment');
});
it('handles null', () => {
expect(normalizeSubject(null)).toBe('');
});
it('handles empty string', () => {
expect(normalizeSubject('')).toBe('');
});
it('lowercases and trims a subject with no prefix', () => {
expect(normalizeSubject(' Urgent Payment ')).toBe('urgent payment');
});
});
describe('extractUrlDomain', () => {
it('extracts hostname from a full URL', () => {
expect(extractUrlDomain('https://evil.example.com/path?x=1')).toBe('evil.example.com');
});
it('returns null for a malformed URL instead of throwing', () => {
expect(extractUrlDomain('not-a-url')).toBeNull();
});
it('extracts hostname regardless of scheme', () => {
expect(extractUrlDomain('http://another.example.net')).toBe('another.example.net');
});
});