outreach-guard.test.js
1 import { test, describe, beforeEach } from 'node:test'; 2 import assert from 'node:assert/strict'; 3 import { 4 recordOutreachError, 5 shouldHaltChannel, 6 getHaltedChannels, 7 clearHalt, 8 getChannelErrorStats, 9 } from '../../src/utils/outreach-guard.js'; 10 11 // Reset guard state between tests by clearing all halts 12 function resetGuard() { 13 for (const ch of ['sms', 'email', 'form', 'x', 'linkedin']) { 14 clearHalt(ch); 15 } 16 } 17 18 describe('outreach-guard', () => { 19 beforeEach(resetGuard); 20 21 test('channel is not halted initially', () => { 22 assert.equal(shouldHaltChannel('sms'), false); 23 }); 24 25 test('channel not halted after fewer than 25 errors', () => { 26 for (let i = 0; i < 24; i++) { 27 recordOutreachError('sms', 'outside business hours'); 28 } 29 assert.equal(shouldHaltChannel('sms'), false); 30 }); 31 32 test('channel halted after 25 of the same error', () => { 33 for (let i = 0; i < 25; i++) { 34 recordOutreachError('sms', 'outside business hours'); 35 } 36 assert.equal(shouldHaltChannel('sms'), true); 37 }); 38 39 test('halting one channel does not halt another', () => { 40 for (let i = 0; i < 25; i++) { 41 recordOutreachError('sms', 'outside business hours'); 42 } 43 assert.equal(shouldHaltChannel('email'), false); 44 }); 45 46 test('clearHalt removes the halt', () => { 47 for (let i = 0; i < 25; i++) { 48 recordOutreachError('sms', 'outside business hours'); 49 } 50 assert.equal(shouldHaltChannel('sms'), true); 51 clearHalt('sms'); 52 assert.equal(shouldHaltChannel('sms'), false); 53 }); 54 55 test('getHaltedChannels returns halted channel info', () => { 56 for (let i = 0; i < 25; i++) { 57 recordOutreachError('email', 'status code 429'); 58 } 59 const halted = getHaltedChannels(); 60 assert.ok('email' in halted); 61 assert.ok(halted.email.count >= 25); 62 }); 63 64 test('different errors on same channel do not aggregate', () => { 65 // 13 of error A and 12 of error B — neither should trigger 66 for (let i = 0; i < 13; i++) { 67 recordOutreachError('sms', 'error A message'); 68 } 69 for (let i = 0; i < 12; i++) { 70 recordOutreachError('sms', 'error B message'); 71 } 72 assert.equal(shouldHaltChannel('sms'), false); 73 }); 74 75 test('getChannelErrorStats returns top error per channel', () => { 76 for (let i = 0; i < 10; i++) { 77 recordOutreachError('sms', 'outside business hours'); 78 } 79 const stats = getChannelErrorStats(); 80 const smsStats = stats.find(s => s.channel === 'sms'); 81 assert.ok(smsStats); 82 assert.ok(smsStats.count >= 10); 83 }); 84 85 test('error normalization groups similar errors', () => { 86 // These should all normalize to the same key 87 recordOutreachError('sms', 'Outreach #12345 failed: outside business hours'); 88 recordOutreachError('sms', 'Outreach #67890 failed: outside business hours'); 89 // Both should map to the same normalized string, so count as 2 90 const stats = getChannelErrorStats(); 91 const smsStats = stats.find(s => s.channel === 'sms'); 92 assert.ok(smsStats); 93 assert.ok(smsStats.count >= 2); 94 }); 95 });