/ tests / utils / compliance-validator-supplement.test.js
compliance-validator-supplement.test.js
  1  /**
  2   * Supplement tests for src/utils/compliance-validator.js
  3   *
  4   * Tests validateCompliance() directly without mock.module.
  5   * Covers: email channel checks, SMS channel checks, blocked channels,
  6   * unknown country fallback, CAN-SPAM physical address check.
  7   */
  8  
  9  import { test, describe } from 'node:test';
 10  import assert from 'node:assert/strict';
 11  
 12  // Set required env var before importing the module (CAN-SPAM physical address)
 13  process.env.CAN_SPAM_PHYSICAL_ADDRESS = '123 Test St, Test City, State 12345';
 14  
 15  import { validateCompliance } from '../../src/utils/compliance-validator.js';
 16  
 17  // ─── validateCompliance ───────────────────────────────────────────────────────
 18  
 19  describe('validateCompliance', () => {
 20    describe('email channel', () => {
 21      test('returns ok for basic valid email proposal (AU)', () => {
 22        const result = validateCompliance(
 23          'Hi, I noticed your website could benefit from CRO improvements.',
 24          'email',
 25          'AU'
 26        );
 27        assert.ok(typeof result === 'object');
 28        assert.ok('ok' in result);
 29        assert.ok('blocked' in result);
 30      });
 31  
 32      test('blocks email with RE: subject prefix', () => {
 33        const proposal = 'Subject: RE: Your website\n\nHi there, this is about CRO.';
 34        const result = validateCompliance(proposal, 'email', 'AU');
 35        assert.equal(result.ok, false);
 36        assert.equal(result.blocked, true);
 37        assert.ok(result.reason.includes('RE:'));
 38      });
 39  
 40      test('blocks email with FWD: subject prefix', () => {
 41        const proposal = 'Subject: FWD: Check this out\n\nForwarded message.';
 42        const result = validateCompliance(proposal, 'email', 'AU');
 43        assert.equal(result.ok, false);
 44        assert.equal(result.blocked, true);
 45      });
 46  
 47      test('returns ok for valid subject line without RE:/FWD:', () => {
 48        const result = validateCompliance(
 49          'Subject: Website Opportunity for Your Business\n\nHi there.',
 50          'email',
 51          'NZ'
 52        );
 53        // Should not block on subject alone (no CAN-SPAM addr required for NZ)
 54        assert.ok(typeof result.ok === 'boolean');
 55      });
 56  
 57      test('handles null country code by defaulting to AU', () => {
 58        assert.doesNotThrow(() => {
 59          validateCompliance('Simple proposal text.', 'email', null);
 60        });
 61      });
 62  
 63      test('handles undefined country code by defaulting to AU', () => {
 64        assert.doesNotThrow(() => {
 65          validateCompliance('Simple proposal text.', 'email', undefined);
 66        });
 67      });
 68    });
 69  
 70    describe('sms channel', () => {
 71      test('returns ok for short SMS proposal (AU)', () => {
 72        const result = validateCompliance('Hi! Quick question about your website.', 'sms', 'AU');
 73        assert.ok(typeof result === 'object');
 74        assert.ok('ok' in result);
 75      });
 76  
 77      test('modifiedText is null when no sender ID appended (AU does not require body sender ID)', () => {
 78        const result = validateCompliance('Short SMS text here.', 'sms', 'AU');
 79        // Whether modifiedText is null depends on AU requirements — just check it doesn't throw
 80        assert.ok(result.modifiedText === null || typeof result.modifiedText === 'string');
 81      });
 82  
 83      test('returns ok for US SMS with short text', () => {
 84        const result = validateCompliance(
 85          'Hi! I can help optimize your website. Reply STOP to opt out.',
 86          'sms',
 87          'US'
 88        );
 89        assert.ok(typeof result.ok === 'boolean');
 90      });
 91  
 92      test('handles lowercase country code', () => {
 93        assert.doesNotThrow(() => {
 94          validateCompliance('Test SMS text.', 'sms', 'au');
 95        });
 96      });
 97    });
 98  
 99    describe('unknown channel', () => {
100      test('returns ok for unknown channel (no specific checks apply)', () => {
101        const result = validateCompliance('Some text.', 'form', 'AU');
102        assert.equal(result.ok, true);
103        assert.equal(result.blocked, false);
104      });
105    });
106  
107    describe('return shape', () => {
108      test('always returns ok, blocked, reason, modifiedText fields', () => {
109        const result = validateCompliance('Test text.', 'sms', 'AU');
110        assert.ok('ok' in result, 'should have ok');
111        assert.ok('blocked' in result, 'should have blocked');
112        assert.ok('reason' in result, 'should have reason');
113        assert.ok('modifiedText' in result, 'should have modifiedText');
114      });
115  
116      test('reason is null when ok', () => {
117        const result = validateCompliance('Valid SMS text.', 'sms', 'AU');
118        if (result.ok) {
119          assert.equal(result.reason, null);
120        }
121      });
122  
123      test('ok is false when blocked is true', () => {
124        const result = validateCompliance('Subject: RE: Test\n\nBody text.', 'email', 'AU');
125        if (result.blocked) {
126          assert.equal(result.ok, false);
127        }
128      });
129    });
130  });