← back to Ventura Claw Leads

tests/compliance.test.js

110 lines

// Compliance lib unit tests — suppression + unsubscribe-token lifecycle.

require('dotenv').config({ path: require('path').resolve(__dirname, '..', '.env') });
process.env.SESSION_SECRET = process.env.SESSION_SECRET || 'test-secret';
process.env.MAILING_ADDRESS = process.env.MAILING_ADDRESS || 'Test, 1 Real St, City, ST 12345';
process.env.PUBLIC_URL = process.env.PUBLIC_URL || 'http://localhost:9789';
process.env.NODE_ENV = 'test';

const test = require('node:test');
const assert = require('node:assert/strict');
const c = require('../lib/compliance');
const db = require('../lib/db');

const TEST_EMAIL = '__compliance_test__@example.com';

test.after(async () => {
  await db.query(`DELETE FROM comms_suppression WHERE identifier = $1`, [TEST_EMAIL]);
  await db.query(`DELETE FROM unsubscribe_tokens WHERE identifier = $1`, [TEST_EMAIL]);
  await db.query(`DELETE FROM comms_send_audit WHERE recipient = $1`, [TEST_EMAIL]);
  await db.pool.end();
});

test('hasMailingAddress accepts real, rejects placeholders', () => {
  process.env.MAILING_ADDRESS = 'Real Co, 1 Real St, City, ST 12345';
  assert.equal(c.hasMailingAddress(), true);
  process.env.MAILING_ADDRESS = 'TBD';
  assert.equal(c.hasMailingAddress(), false);
  process.env.MAILING_ADDRESS = 'TODO_REPLACE';
  assert.equal(c.hasMailingAddress(), false);
  process.env.MAILING_ADDRESS = '';
  assert.equal(c.hasMailingAddress(), false);
  // Restore
  process.env.MAILING_ADDRESS = 'Real Co, 1 Real St, City, ST 12345';
});

test('publicUrlOk: accepts https public, rejects localhost', () => {
  // Note: lib/compliance.js captures IS_PROD at module-load time
  // (const IS_PROD = process.env.NODE_ENV === 'production'). Mutating
  // NODE_ENV at test-runtime won't flip IS_PROD inside the already-loaded
  // module, so we can only assert the load-time-stable conditions: empty
  // and localhost are always rejected, valid https public is always ok.
  const oldUrl = process.env.PUBLIC_URL;

  process.env.PUBLIC_URL = 'https://leads.venturaclaw.com';
  assert.equal(c.publicUrlOk(), true);

  process.env.PUBLIC_URL = 'http://localhost:9789';
  assert.equal(c.publicUrlOk(), false);

  process.env.PUBLIC_URL = 'http://127.0.0.1:9789';
  assert.equal(c.publicUrlOk(), false);

  process.env.PUBLIC_URL = '';
  assert.equal(c.publicUrlOk(), false);

  process.env.PUBLIC_URL = oldUrl;
});

test('addSuppression idempotent on (channel, identifier)', async () => {
  await c.addSuppression({ channel: 'email', identifier: TEST_EMAIL, reason: 'unsubscribe', source: 'test' });
  await c.addSuppression({ channel: 'email', identifier: TEST_EMAIL, reason: 'unsubscribe', source: 'test' });
  const r = await db.query(`SELECT COUNT(*)::int AS n FROM comms_suppression WHERE identifier = $1`, [TEST_EMAIL]);
  assert.equal(r.rows[0].n, 1, 'second add did not duplicate');
});

test('isSuppressed returns true after addSuppression, false otherwise', async () => {
  assert.equal(await c.isSuppressed({ channel: 'email', identifier: TEST_EMAIL }), true);
  assert.equal(await c.isSuppressed({ channel: 'email', identifier: 'never-seen@example.com' }), false);
  assert.equal(await c.isSuppressed({ channel: '', identifier: TEST_EMAIL }), true, 'fail-closed on bad input');
});

test('mintUnsubscribeToken → consumeUnsubscribeToken round-trip', async () => {
  const token = await c.mintUnsubscribeToken({
    channel: 'email', identifier: 'unsub-test@example.com',
    campaign: 'lead_delivery', businessId: null
  });
  assert.equal(token.length, 32);

  const consumed1 = await c.consumeUnsubscribeToken(token);
  assert.ok(consumed1);
  assert.equal(consumed1.identifier, 'unsub-test@example.com');
  assert.equal(consumed1.campaign, 'lead_delivery');

  // Idempotent — consuming twice returns the row but used_at is unchanged on
  // the second call (COALESCE preserves the original used_at).
  const consumed2 = await c.consumeUnsubscribeToken(token);
  assert.deepEqual(consumed1.used_at, consumed2.used_at);

  // Garbage token
  const consumed3 = await c.consumeUnsubscribeToken('not-a-token');
  assert.equal(consumed3, null);

  await db.query(`DELETE FROM unsubscribe_tokens WHERE identifier = 'unsub-test@example.com'`);
});

test('complianceFooter includes MAILING_ADDRESS + unsubscribe link', () => {
  const html = c.complianceFooter({ campaign: 'lead_delivery', unsubscribeUrl: 'https://x/unsub?t=abc' });
  assert.ok(html.includes('Real Co, 1 Real St'), 'address present');
  assert.ok(html.includes('https://x/unsub?t=abc'), 'unsubscribe URL embedded verbatim');
  assert.ok(html.includes('lead_delivery'), 'campaign label present');
  assert.ok(html.includes('Unsubscribe'), 'human-visible unsubscribe text');
});

test('listUnsubscribeHeader returns RFC 2369/8058 headers', () => {
  const h = c.listUnsubscribeHeader('https://x/unsub?t=abc');
  assert.equal(h['List-Unsubscribe'], '<https://x/unsub?t=abc>');
  assert.equal(h['List-Unsubscribe-Post'], 'List-Unsubscribe=One-Click');
  assert.equal(c.listUnsubscribeHeader(null), null);
});