/ tests / compliance / government-email-filtering.test.js
government-email-filtering.test.js
  1  #!/usr/bin/env node
  2  
  3  /**
  4   * Tests for government email filtering
  5   * Ensures we don't send outreach to government email addresses
  6   */
  7  
  8  import { test, mock } from 'node:test';
  9  import assert from 'node:assert/strict';
 10  import { fileURLToPath } from 'url';
 11  import { dirname } from 'path';
 12  
 13  // Mock LLM provider BEFORE importing modules that use it
 14  const callLLMMock = mock.fn();
 15  const getProviderMock = mock.fn(() => 'openrouter');
 16  const getProviderDisplayNameMock = mock.fn(() => 'OpenRouter');
 17  
 18  mock.module('../../src/utils/llm-provider.js', {
 19    namedExports: {
 20      callLLM: callLLMMock,
 21      getProvider: getProviderMock,
 22      getProviderDisplayName: getProviderDisplayNameMock,
 23    },
 24  });
 25  
 26  mock.module('../../src/utils/llm-usage-tracker.js', {
 27    namedExports: {
 28      logLLMUsage: mock.fn(async () => {}),
 29    },
 30  });
 31  
 32  // Now import modules
 33  import { getAllContacts } from '../../src/contacts/prioritize.js';
 34  import { generateProposalVariants } from '../../src/proposal-generator-v2.js';
 35  
 36  const __filename = fileURLToPath(import.meta.url);
 37  const __dirname = dirname(__filename);
 38  
 39  test('Government email detection patterns', () => {
 40    // This is testing the internal function, so we'll recreate it here
 41    function isGovernmentEmail(email) {
 42      if (!email || typeof email !== 'string') return false;
 43  
 44      const lower = email.toLowerCase().trim();
 45      const govPatterns = [
 46        /\.gov$/i,
 47        /\.gov\.[a-z]{2}$/i,
 48        /\.gc\.ca$/i,
 49        /\.govt\.nz$/i,
 50        /\.gob\.[a-z]{2}$/i,
 51        /\.gouv\.[a-z]{2}$/i,
 52        /\.go\.[a-z]{2}$/i,
 53        /\.gov\.br$/i,
 54        /\.mil$/i,
 55        /\.mil\.[a-z]{2}$/i,
 56      ];
 57  
 58      const domain = lower.split('@')[1];
 59      if (!domain) return false;
 60  
 61      return govPatterns.some(pattern => pattern.test(domain));
 62    }
 63  
 64    // Test US federal government emails
 65    assert.equal(isGovernmentEmail('john@example.gov'), true, 'Should detect .gov');
 66    assert.equal(isGovernmentEmail('jane@whitehouse.gov'), true, 'Should detect .gov');
 67  
 68    // Test country-specific government emails
 69    assert.equal(isGovernmentEmail('alice@example.gov.au'), true, 'Should detect .gov.au');
 70    assert.equal(isGovernmentEmail('bob@example.gov.uk'), true, 'Should detect .gov.uk');
 71    assert.equal(isGovernmentEmail('charlie@example.gov.in'), true, 'Should detect .gov.in');
 72  
 73    // Test Canada
 74    assert.equal(isGovernmentEmail('dave@example.gc.ca'), true, 'Should detect .gc.ca');
 75  
 76    // Test New Zealand
 77    assert.equal(isGovernmentEmail('eve@example.govt.nz'), true, 'Should detect .govt.nz');
 78  
 79    // Test Spanish-speaking countries
 80    assert.equal(isGovernmentEmail('frank@example.gob.mx'), true, 'Should detect .gob.mx');
 81    assert.equal(isGovernmentEmail('grace@example.gob.es'), true, 'Should detect .gob.es');
 82  
 83    // Test French-speaking countries
 84    assert.equal(isGovernmentEmail('henri@example.gouv.fr'), true, 'Should detect .gouv.fr');
 85  
 86    // Test Asian countries
 87    assert.equal(isGovernmentEmail('yuki@example.go.jp'), true, 'Should detect .go.jp');
 88    assert.equal(isGovernmentEmail('kim@example.go.kr'), true, 'Should detect .go.kr');
 89  
 90    // Test Brazil
 91    assert.equal(isGovernmentEmail('maria@example.gov.br'), true, 'Should detect .gov.br');
 92  
 93    // Test military domains
 94    assert.equal(isGovernmentEmail('soldier@example.mil'), true, 'Should detect .mil');
 95    assert.equal(isGovernmentEmail('officer@example.mil.au'), true, 'Should detect .mil.au');
 96  
 97    // Test non-government emails (should return false)
 98    assert.equal(isGovernmentEmail('user@example.com'), false, 'Should not detect .com');
 99    assert.equal(isGovernmentEmail('admin@company.com.au'), false, 'Should not detect .com.au');
100    assert.equal(isGovernmentEmail('info@business.co.uk'), false, 'Should not detect .co.uk');
101    assert.equal(isGovernmentEmail('contact@service.org'), false, 'Should not detect .org');
102  
103    // Test edge cases
104    assert.equal(isGovernmentEmail(''), false, 'Should handle empty string');
105    assert.equal(isGovernmentEmail(null), false, 'Should handle null');
106    assert.equal(isGovernmentEmail(undefined), false, 'Should handle undefined');
107    assert.equal(isGovernmentEmail('invalid-email'), false, 'Should handle invalid email');
108  });
109  
110  test('getAllContacts filters out government emails', () => {
111    const contactsJson = {
112      email_addresses: [
113        'john@example.com', // Normal email - should be included
114        'admin@agency.gov', // US gov - should be filtered
115        'alice@department.gov.au', // AU gov - should be filtered
116        'support@business.co.uk', // Normal email - should be included
117      ],
118    };
119  
120    const contacts = getAllContacts(contactsJson, 'AU');
121  
122    // Should only have 2 non-government emails
123    const emailContacts = contacts.filter(c => c.channel === 'email');
124    assert.equal(emailContacts.length, 2, 'Should have 2 non-government emails');
125  
126    // Verify the correct emails were kept
127    const emailAddresses = emailContacts.map(c => c.uri);
128    assert.ok(emailAddresses.includes('john@example.com'), 'Should include normal .com email');
129    assert.ok(
130      emailAddresses.includes('support@business.co.uk'),
131      'Should include normal .co.uk email'
132    );
133    assert.ok(!emailAddresses.includes('admin@agency.gov'), 'Should filter .gov email');
134    assert.ok(!emailAddresses.includes('alice@department.gov.au'), 'Should filter .gov.au email');
135  });
136  
137  test('government emails filtered from getAllContacts contact extraction', () => {
138    // Test that getAllContacts (prioritize.js) filters gov emails during extraction.
139    // This is the actual behavior - gov emails never reach proposal/outreach storage.
140    const contactData = {
141      email_addresses: [
142        'contact@test-agency.gov.au', // Government email - should be filtered
143        'info@contractor.com', // Normal contractor email - should be included
144      ],
145    };
146  
147    const contacts = getAllContacts(contactData, 'AU');
148  
149    // Government email should be filtered out
150    const govContact = contacts.find(c => c.uri === 'contact@test-agency.gov.au');
151    assert.equal(
152      govContact,
153      undefined,
154      'Government email should be filtered during contact extraction'
155    );
156  
157    // Normal contractor email should be included
158    const normalContact = contacts.find(c => c.uri === 'info@contractor.com');
159    assert.ok(normalContact, 'Normal contractor email should be included in contacts');
160    assert.equal(normalContact.channel, 'email', 'Contact channel should be email');
161  });