← back to Sanderson Onboard

scripts/create_sdg_weight_heal.test.mjs

61 lines

// create_sdg_weight_heal.test.mjs — TK-11471 negative test for the VERIFIED heal in
// scripts/create_sdg.mjs goLive(). Offline: zero network, zero DB, zero Shopify.
//
// The defect being locked down: goLive() used to fire `await gql(M_WEIGHT, ...)` and DISCARD the
// result — no userErrors check, no re-query — then fall through to M_ACTIVE. A heal that silently
// failed published the product ACTIVE at zero weight anyway. The mutation's own 200 is not
// evidence the weight is set; only re-reading it is. A positive-only test would not have caught
// this, so every case below is an injected failure that must end in a HOLD.
import fs from 'node:fs';
import assert from 'node:assert';

const SRC = fs.readFileSync(new URL('./create_sdg.mjs', import.meta.url), 'utf8');
let pass = 0, fail = 0;
const t = (n, fn) => { try { fn(); console.log('PASS ' + n); pass++; } catch (e) { console.log('FAIL ' + n + ' — ' + e.message); fail++; } };

const body = SRC.slice(SRC.indexOf('async function goLive'), SRC.indexOf('async function main'));

t('heal checks userErrors (the mutation result is no longer discarded)', () => {
  assert.ok(/inventoryItemUpdate\?\.userErrors/.test(body), 'M_WEIGHT result is not inspected for userErrors');
});
t('heal RE-QUERIES the live product after mutating', () => {
  const after = body.slice(body.indexOf('M_WEIGHT'));
  assert.ok(/gql\(Q_V,\s*\{\s*id:\s*gid\s*\}\)/.test(after), 'no re-query of the live product after the heal');
});
t('a variant still zero after heal produces a HOLD, not an activation', () => {
  assert.ok(/still zero after heal/.test(body), 'no still-zero check');
  assert.ok(/return \{ gated: 'weight>0'/.test(body), 'does not return a gated hold');
});
t('an empty re-verify is a HOLD, never an implicit pass (unmeasured is never PASS)', () => {
  assert.ok(/re-verify returned no variants/.test(body), 'empty re-verify is not treated as unmeasured');
});
t('the HOLD returns BEFORE the ACTIVATE mutation', () => {
  assert.ok(body.indexOf("return { gated: 'weight>0'") < body.indexOf('gql(M_ACTIVE'),
    'the weight hold does not precede M_ACTIVE');
});
t('Q_V selects weight (else the re-verify measures nothing and always passes)', () => {
  const q = SRC.slice(SRC.indexOf('const Q_V'), SRC.indexOf('const M_WEIGHT'));
  assert.ok(/measurement\s*\{\s*weight\s*\{\s*value/.test(q), 'Q_V does not select inventoryItem measurement weight');
});
t('CREATE-LOOP caller treats a hold as held, not as created (no ✓ on a held product)', () => {
  const main = SRC.slice(SRC.indexOf('async function main'));
  const gi = main.indexOf('if (gl.gated)');
  assert.ok(gi > -1, 'caller does not branch on gl.gated');
  // Assert against the CREATE-LOOP's record specifically. An earlier `action:'CREATED'` also
  // exists in the finish-pending path above, so searching from index 0 matches the wrong one —
  // that imprecision made this assertion fail on correct code, which is itself worth locking down.
  const ci = main.indexOf("action: 'CREATED', status: gl.status", gi);
  assert.ok(ci > gi, 'the gated branch does not precede the create-loop CREATED record');
  assert.ok(/held\+\+/.test(main.slice(gi, gi + 700)), 'a hold does not increment the held counter');
  assert.ok(/continue;/.test(main.slice(gi, gi + 700)), 'a hold does not skip the rest of the iteration');
});

t('FINISH-PENDING path is hold-safe (only records on an explicit ACTIVE status)', () => {
  const fp = SRC.slice(SRC.indexOf('resumedFromDraft') - 400, SRC.indexOf('resumedFromDraft') + 200);
  assert.ok(/gl\.status === 'ACTIVE'/.test(fp),
    'finish-pending path does not gate its success record on status===ACTIVE, so a hold would be recorded as finished');
});

console.log(`\n${fail ? 'TESTS FAILED' : 'ALL TESTS PASS'} — ${pass} passed, ${fail} failed`);
process.exit(fail ? 1 : 0);