← back to Sample Followup Sweep

test/sent-stamp-retry.test.js

130 lines

'use strict';
// TK-12320: a sent follow-up whose stamp was skipped / zero must be retried; a stamped one must not;
// and the chase date written is the message's SEND date, never the pass date. No FM, no network.
const assert = require('node:assert/strict');
const path = require('node:path');
const { pathToFileURL } = require('node:url');

(async () => {
  const core = await import(pathToFileURL(path.join(__dirname, '../scripts/sent-stamp-core.mjs')).href);
  const NOW = new Date(2026, 8, 26, 9, 0, 0).getTime();                 // 09/26/2026 local
  const SENT_1838 = new Date(2026, 8, 16, 8, 15, 0).getTime();          // 09/16/2026 local
  const SENT_VAH = new Date(2026, 8, 13, 0, 5, 0).getTime();            // 09/13/2026 local
  const SENT_KRA = new Date(2026, 8, 24, 12, 0, 0).getTime();
  const longAgo = new Date(NOW - 86400e3).toISOString();                // past any backoff

  const emailVid = core.buildEmailVid(
    [{ slug: '1838', vid: '1838 WALLCOVERINGS' }, { slug: 'vahallan', vid: 'VAH' }, { slug: 'kravet', vid: 'KRA' }],
    { '1838': { sample_email: 'scott.myer@gmail.com' }, vahallan: { main_email: 'info@vahallan.com' }, kravet: { sample_email: 'matt.schoffman@kravet.com' } });

  const msgs = [
    { id: 'm1838', to: '"scott.myer" <scott.myer@gmail.com>', internalDate: String(SENT_1838) },
    { id: 'mvah', to: 'info <info@vahallan.com>', internalDate: String(SENT_VAH) },
    { id: 'mkra', to: 'Matt <matt.schoffman@kravet.com>', internalDate: String(SENT_KRA) },
    { id: 'mnew', to: 'nobody@unknown.example', internalDate: String(NOW) },
  ];
  const ledger = {
    m1838: { vids: ['1838 WALLCOVERINGS'], stamped: 0, patterns: [], at: longAgo },   // poisoned stamped:0
    mvah: { skip: 'no vid for recipient', to: 'info@vahallan.com' },                  // poisoned skip (legacy: no timestamps)
    mkra: { vids: ['KRA'], stamped: 2, patterns: ['A', 'B'], at: longAgo },           // terminal success
  };

  const stampCalls = [];
  const deps = {
    emailVid,
    getBody: async id => `letter ${id}`,
    stamp: async (vids, body, win, sentOn) => { stampCalls.push({ vids, body, win, sentOn }); return { stamped: ['X-1'], failed: 0 }; },
  };
  const r = await core.runPass({ msgs, ledger, now: NOW, dry: false, retrySecs: 900, minAgeDays: 10, maxAgeDays: 60, deps });

  // (a) stamped:0 and skip entries ARE re-processed
  const byBody = Object.fromEntries(stampCalls.map(c => [c.body, c]));
  assert.ok(byBody['letter m1838'], '(a) stamped:0 entry must be re-processed');
  assert.ok(byBody['letter mvah'], '(a) skip entry must be re-processed once its recipient maps');
  assert.equal(r.ledger.m1838.stamped, 1);
  assert.equal(r.ledger.m1838.recovered_from, 'stamped:0');
  assert.equal(r.ledger.mvah.recovered_from, 'skip: no vid for recipient');
  assert.equal(r.ledger.m1838.attempts, 2, 'history kept: attempts counts the prior try');
  console.log('(a) PASS — stamped:0 and skip entries re-processed; attempts/recovered_from recorded');

  // (b) stamped>=1 is NOT re-processed, and its ledger entry is untouched
  assert.ok(!byBody['letter mkra'], '(b) terminal stamped>=1 entry must NOT be re-processed');
  assert.deepEqual(r.ledger.mkra, ledger.mkra);
  console.log('(b) PASS — stamped>=1 entry skipped and left intact');

  // (c) stamp date = message send date, not today; window anchored on the send date too
  assert.equal(byBody['letter m1838'].sentOn, '09/16/2026', '(c) 1838 must stamp its send date');
  assert.equal(byBody['letter mvah'].sentOn, '09/13/2026', '(c) Vahallan must stamp its send date');
  assert.notEqual(byBody['letter m1838'].sentOn, core.fmt(new Date(NOW)));
  assert.equal(byBody['letter m1838'].win, '07/18/2026...09/06/2026');
  assert.equal(r.ledger.m1838.sent_on_source, 'internalDate');
  console.log('(c) PASS — chase date = send date (09/16, 09/13), not today (09/26)');

  // unmapped recipient: ledgered as skip, NOT terminal, reported once
  assert.deepEqual(r.unmapped, ['nobody@unknown.example']);
  assert.equal(core.isTerminal(r.ledger.mnew), false);
  assert.equal(r.ledger.mnew.attempts, 1);

  // backoff: an immediate second pass does not re-query FM for the still-unmapped skip
  stampCalls.length = 0;
  const r2 = await core.runPass({ msgs, ledger: r.ledger, now: NOW + 30e3, dry: false, retrySecs: 900, minAgeDays: 10, maxAgeDays: 60, deps });
  assert.equal(stampCalls.length, 0, 'terminal entries skipped; unmapped one in backoff');
  assert.equal(r2.changed, false, 'no ledger churn inside the backoff window');
  // …but after the backoff it is retried and picks up a newly-added mapping
  const emailVid2 = core.buildEmailVid([{ slug: 'u', vid: 'UNK' }], { u: { sample_email: 'nobody@unknown.example' } });
  const r3 = await core.runPass({ msgs, ledger: r.ledger, now: NOW + 901e3, dry: false, retrySecs: 900, minAgeDays: 10, maxAgeDays: 60, deps: { ...deps, emailVid: emailVid2 } });
  assert.equal(r3.ledger.mnew.stamped, 1);
  assert.equal(r3.ledger.mnew.attempts, 2);
  console.log('(d) PASS — backoff suppresses churn; retry after backoff picks up a new mapping');

  // oldest send first: the earlier letter owns the chase date
  const order = [];
  await core.runPass({ msgs: [msgs[2], msgs[1], msgs[0]], ledger: {}, now: NOW, dry: true, retrySecs: 900, minAgeDays: 10, maxAgeDays: 60,
    deps: { ...deps, stamp: async (v, body) => { order.push(body); return { stamped: [] }; } } });
  assert.deepEqual(order, ['letter mvah', 'letter m1838', 'letter mkra'], 'must process oldest send first');
  // covered-only is NOT finished (a sibling letter item may not be in FM yet)
  assert.equal(core.isTerminal({ vids: ['X'], stamped: 0, covered: 1, failed: 0 }), false);
  assert.equal(core.isTerminal({ vids: ['X'], stamped: 0, covered: 0, failed: 0 }), false);
  // an older send in backoff is pulled forward when a newer send to the same recipient is due,
  // so the older letter still owns the chase date
  {
    const older = { id: 'old', to: 'a@v.com', internalDate: String(SENT_VAH) };
    const newer = { id: 'new', to: 'A <a@v.com>', internalDate: String(SENT_KRA) };
    const ev = core.buildEmailVid([{ slug: 'v', vid: 'V' }], { v: { sample_email: 'a@v.com' } });
    const seenOrder = [];
    const led = { old: { vids: ['V'], stamped: 0, last_try: new Date(NOW - 60e3).toISOString() } };   // in backoff
    await core.runPass({ msgs: [newer, older], ledger: led, now: NOW, dry: false, retrySecs: 900, minAgeDays: 10, maxAgeDays: 60,
      deps: { emailVid: ev, getBody: async id => id, stamp: async (v, body, w, sentOn) => { seenOrder.push(`${body}@${sentOn}`); return { stamped: [] }; } } });
    assert.deepEqual(seenOrder, ['old@09/13/2026', 'new@09/24/2026'], 'older backoff send must run before the newer one');
  }
  // failed FM commit is NOT terminal even with stamped>=1
  assert.equal(core.isTerminal({ stamped: 2, failed: 1 }), false);
  // send-date fallbacks
  assert.equal(core.sendDateOf({ date: 'Wed, 16 Sep 2026 08:15:00 -0700' }, NOW).source, 'date-header');
  assert.equal(core.sendDateOf({}, NOW).source, 'fallback-today');
  // a fetch-failed metadata row is ignored, not ledgered as a skip
  const r4 = await core.runPass({ msgs: [{ id: 'bad', error: 'fetch-failed' }], ledger: {}, now: NOW, dry: false, retrySecs: 900, minAgeDays: 10, maxAgeDays: 60, deps });
  assert.equal(r4.ledger.bad, undefined);
  // a failed body fetch is not an outcome: nothing ledgered, nothing stamped
  const r6 = await core.runPass({ msgs: [msgs[0]], ledger: {}, now: NOW, dry: false, retrySecs: 900, minAgeDays: 10, maxAgeDays: 60, deps: { ...deps, getBody: async () => null } });
  assert.equal(r6.ledger.m1838, undefined);
  // dry run never writes the ledger
  const r5 = await core.runPass({ msgs, ledger, now: NOW, dry: true, retrySecs: 900, minAgeDays: 10, maxAgeDays: 60, deps });
  assert.equal(r5.changed, false);
  assert.deepEqual(r5.ledger, ledger);
  console.log('(e) PASS — failed commits retried, date fallbacks, fetch-failed ignored, dry never writes');

  // contact whose vendor is absent from fleet.json still maps via the slug-derived vid (Phillip Jeffries)
  const aliases = require('../lib/slug-aliases.cjs');
  const pjMap = core.buildEmailVid([], { pj: { sample_email: 'jballard@phillipjeffries.com' } }, { slugAliases: aliases.SLUG_ALIASES, slugForVid: aliases.slugForVid });
  assert.deepEqual([...pjMap['jballard@phillipjeffries.com']], ['PJ']);
  assert.deepEqual(core.buildEmailVid([], { pj: { sample_email: 'jballard@phillipjeffries.com' } })['jballard@phillipjeffries.com'], undefined);
  console.log('(f) PASS — contact-only vendor (PJ) maps via slug-derived vid');

  // NEGATIVE: the pre-fix rule ("any ledger entry suppresses") would have left 1838/Vahallan unstamped
  const oldRule = e => !!e;
  assert.equal(oldRule(ledger.m1838) && oldRule(ledger.mvah), true, 'old rule suppresses both poisoned sends');
  assert.equal(core.shouldProcess(ledger.m1838, { now: NOW, retrySecs: 900 }).go, true);
  console.log('sent-stamp-retry: ALL PASS');
})().catch(e => { console.error('FAIL:', e.message); process.exit(1); });