← back to Sample Followup Sweep

test/coverage-reconcile.test.cjs

109 lines

'use strict';
// TK-12117 coverage-reconciliation negative test (ship-with-a-negative-test doctrine).
// Proves the canary goes RED on an injected fault (an outstanding row in NO bucket = silent skip)
// and GREEN only when the population is fully accounted + actioned.
const assert = require('node:assert');
const { reconcileCoverage, followupWindow, windowCoversActiveBand } = require('../lib/sweep');

// A resolvable vendor (chaseable) + a no-chase vendor as the "known good" contacts.
const contacts = {
  fabr: { name: 'Fabricut', sample_email: 'customers@fabricut.com', account_number: 'On File' },
  spn: { name: 'Spoonflower', disposition: 'no-chase' },
  wat: { name: 'Watts 1874', sample_email: 'sophie@watts1874.co.uk', account_number: 'On File', needs_confirm: true },
};
const row = (vid, sku, entered, wp = '', letter = '') => ({ fieldData: {
  vid, 'combo sku': sku, 'today for client': entered, 'Date WP Sample Sent': wp, 'Date Sample Request Letter Sent': letter, 'Mfr Pattern': 'P-' + sku } });

// 1. NEGATIVE — inject an outstanding row with EMPTY vid (the sweep drops it before grouping) → LEAKED → FAIL
{
  const recs = [ row('FABR', 'DWFC1', '09/10/2026'), row('', 'DWLEAK1', '09/11/2026') ];
  const r = reconcileCoverage(recs, { contacts });
  assert.equal(r.verdict, 'FAIL', `1 injected empty-vid leak must FAIL, got ${r.verdict}`);
  assert.ok(r.leaked.length >= 1, '1 leaked row must be reported');
  assert.equal(r.population, 2, '1 population must count both outstanding rows');
  console.log(`(1) PASS — injected empty-vid leak -> FAIL (leaked=${r.leaked.length}, pop=${r.population})`);
}

// 2. CLEAN — a single resolvable, not-recently-emailed, not-needs-confirm row → PASS
{
  const recs = [ row('FABR', 'DWFC2', '09/10/2026') ];
  const r = reconcileCoverage(recs, { contacts, recentSet: new Set() });
  assert.equal(r.verdict, 'PASS', `2 clean must PASS, got ${r.verdict}`);
  assert.equal(r.counts.chaseable, 1, '2 must be chaseable');
  assert.equal(r.leaked.length, 0, '2 no leak');
  console.log(`(2) PASS — clean chaseable row -> PASS (chaseable=${r.counts.chaseable})`);
}

// 3. UNCOVERED — an outstanding row whose vid has NO contact (no-vendor-map) → accounted but not chased → WARN
{
  const recs = [ row('NOSUCHVID', 'DWX3', '09/10/2026') ];
  const r = reconcileCoverage(recs, { contacts });
  assert.equal(r.verdict, 'WARN', `3 no-vendor-map must WARN, got ${r.verdict}`);
  assert.equal(r.counts.noVendorMap, 1, '3 must be noVendorMap');
  assert.equal(r.leaked.length, 0, '3 accounted, not leaked');
  console.log(`(3) PASS — no-vendor-map row -> WARN (uncovered=${r.uncovered})`);
}

// 4. RUN-STALE — clean population but the scheduled run did not fire on a run-day → WARN
{
  const recs = [ row('FABR', 'DWFC4', '09/10/2026') ];
  const r = reconcileCoverage(recs, { contacts, runStale: true });
  assert.equal(r.verdict, 'WARN', `4 run-stale must WARN, got ${r.verdict}`);
  console.log('(4) PASS — clean population but run missed on run-day -> WARN');
}

// 5. GUARD/RECENT reconcile — arrived-dup guard + FRESH recipient-busy deferral (<=24d) both count as ACTIONED (covered) → PASS
{
  const today = new Date(2026, 8, 24);                  // 2026-09-24, deterministic (Sep = month 8)
  const recs = [
    row('FABR', 'DWFC5', '09/08/2026'),                 // outstanding, resolvable, not recently emailed -> chaseable
    row('FABR', 'DWFC5', '09/08/2026', '09/14/2026'),   // arrived same-date twin -> guard suppress
    row('WAT', 'DWW5', '09/12/2026'),                   // recently emailed, 12d old (fresh) -> recipientBusy14d (covered batching)
  ];
  const r = reconcileCoverage(recs, { contacts, recentSet: new Set(['sophie@watts1874.co.uk']), today });
  assert.equal(r.counts.guardSuppressed, 1, '5 arrived twin -> guardSuppressed');
  assert.equal(r.counts.recipientBusy14d, 1, '5 fresh recipient-busy -> recipientBusy14d (covered, not WARN)');
  assert.equal(r.verdict, 'PASS', `5 all actioned must PASS, got ${r.verdict}`);
  console.log('(5) PASS — guard-suppressed + fresh recipient-busy deferral both actioned -> PASS');
}

// 6. NEGATIVE (TK-12119) — a recipient-busy deferral aged PAST the batching window (>24d) must NOT hide
//    as covered. Before this fix it was suppressed14d=covered=PASS (a false green for a never-chased sku
//    of a perpetually-recontacted vendor); now it escalates to recipientBusyStale -> uncovered -> WARN.
{
  const today = new Date(2026, 8, 24);                  // 2026-09-24
  const recs = [ row('WAT', 'DWW6', '08/01/2026') ];    // recently-emailed vendor, but THIS sku entered 54d ago, never chased
  const r = reconcileCoverage(recs, { contacts, recentSet: new Set(['sophie@watts1874.co.uk']), today });
  assert.equal(r.counts.recipientBusy14d, 0, '6 stale deferral must NOT count as fresh/covered');
  assert.equal(r.counts.recipientBusyStale, 1, '6 stale deferral -> recipientBusyStale');
  assert.ok(r.uncovered >= 1, '6 stale deferral counts as uncovered');
  assert.equal(r.verdict, 'WARN', `6 stale deferral must WARN (was a false-green PASS), got ${r.verdict}`);
  console.log(`(6) PASS — recipient-busy aged >24d -> WARN not covered (recipientBusyStale=${r.counts.recipientBusyStale}, was suppressed14d/PASS)`);
}

// 7. WINDOW-BAND TRIPWIRE — POSITIVE: the default production window covers the full [10..60] active band.
{
  const today = new Date(2026, 8, 24);
  const band = windowCoversActiveBand(today, { minAge: 10, maxAge: 60 });
  assert.equal(band.covers, true, '7 default full window must cover the active band');
  assert.equal(band.gapDays, 0, '7 default window floor == today-60 (no gap)');
  assert.ok(/07\/26\/2026\.\.\.09\/14\/2026/.test(followupWindow(today, { minAge: 10, maxAge: 60 })), '7 window is the full [today-60..today-10] band');
  console.log('(7) PASS — default production window covers the full active band (gapDays=0)');
}

// 8. NEGATIVE (TK-12091 regression tripwire) — inject a NARROWED production window (a re-narrowing like
//    TK-12091) and prove the canary tripwire goes RED: windowCoversActiveBand must return covers=false.
{
  const today = new Date(2026, 8, 24);
  const prev = process.env.WIN;
  process.env.WIN = '09/13/2026...09/14/2026';   // a 1-day band = the TK-12091-class narrowing
  try {
    const band = windowCoversActiveBand(today, { minAge: 10, maxAge: 60 });
    assert.equal(band.covers, false, '8 a narrowed production window MUST fail the tripwire (covers=false)');
    assert.ok(band.gapDays > 0, '8 gapDays must report how far short the floor is');
    console.log(`(8) PASS — injected narrow window -> tripwire RED (covers=false, gapDays=${band.gapDays})`);
  } finally { if (prev === undefined) delete process.env.WIN; else process.env.WIN = prev; }
}

console.log('\nALL COVERAGE-RECONCILE ASSERTIONS PASSED');