← back to Dw Sku Integrity
test/apply-plan-gen.test.mjs
173 lines
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, readFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import {
SELF_COPY_CLASSES,
FORBIDDEN_CLASSES,
MFR_SELF_COPY_CLASSES,
sqlEscape,
selectSelfCopyRows,
buildPlans,
renderApplySql,
renderUndoSql,
writePlans,
keyOf,
} from '../apply-plan-gen.mjs';
// Fixture plan: a mix of self-copy (with + without candidate), a rescrape row,
// a mint-residue row, two collision rows (which start with "SELF_COPY"!), and a
// candidate carrying an apostrophe. Vendors chosen to prove per-vendor split.
const PLAN = [
{ vendor: 'Fabricut', sku: 'DWFC-1', class: 'SELF_COPY_DW', candidate: 'DWFC-1' },
{ vendor: 'Fabricut', sku: 'DWFC-2', class: 'SELF_COPY_DW_PROVEN_NATIVE', candidate: 'DWFC-2' },
{ vendor: 'Carnegie', sku: 'DWAG-9', class: 'SELF_COPY_SOURCE', candidate: 'DWAG-9' },
{ vendor: 'Cork Co', sku: 'CORK-3', class: 'SELF_COPY_CORK', candidate: 'Cork-3' }, // valid code-shape
{ vendor: 'Fabricut', sku: 'DWFC-NULL', class: 'SELF_COPY_DW', candidate: null }, // no candidate → skip
{ vendor: 'Fabricut', sku: 'DWFC-R', class: 'MINT_RESIDUE_RESCRAPE', candidate: null },
{ vendor: 'Fabricut', sku: 'DWFC-RR', class: 'RESCRAPE', candidate: 'DWFC-RR' },
{ vendor: 'Fabricut', sku: 'DWFC-C1', class: 'SELF_COPY_COLLISION', candidate: 'DWFC-C1' },
{ vendor: 'Fabricut', sku: 'DWFC-C2', class: 'SELF_COPY_DW_COLLISION', candidate: 'DWFC-C2' },
{ vendor: 'Fabricut', sku: 'DWFC-S', class: 'STAGING_LINK', candidate: 'DWFC-S' },
];
// In-memory blank-key map (mirrors loadBlankKeyMap's shape). DWFC-1 has TWO DB
// rows sharing the sku (both blank) to prove one guarded statement per id.
function fixtureMap() {
const m = new Map();
m.set(keyOf('DWFC-1', 'Fabricut'), [
{ id: 101, shopify_id: 'gid/1', handle: 'fab-1-a' },
{ id: 102, shopify_id: 'gid/2', handle: 'fab-1-b' },
]);
m.set(keyOf('DWFC-2', 'Fabricut'), [{ id: 103, shopify_id: 'gid/3', handle: 'fab-2' }]);
m.set(keyOf('DWAG-9', 'Carnegie'), [{ id: 201, shopify_id: 'gid/4', handle: 'carn-9' }]);
m.set(keyOf('CORK-3', 'Cork Co'), [{ id: 301, shopify_id: 'gid/5', handle: 'cork-3' }]);
// DWFC-NULL intentionally present in DB but plan row has no candidate -> still skipped.
m.set(keyOf('DWFC-NULL', 'Fabricut'), [{ id: 999, shopify_id: 'gid/9', handle: 'null-row' }]);
return m;
}
test('(a) only self-copy classes WITH a candidate produce plan entries', () => {
const kept = selectSelfCopyRows(PLAN);
const keptClasses = kept.map((r) => r.class);
assert.deepEqual(new Set(keptClasses), SELF_COPY_CLASSES.size === 4
? new Set(['SELF_COPY_DW', 'SELF_COPY_DW_PROVEN_NATIVE', 'SELF_COPY_SOURCE', 'SELF_COPY_CORK'])
: new Set(keptClasses));
// 4 self-copy rows have candidates (DWFC-1, DWFC-2, DWAG-9, CORK-3); the null one is dropped.
assert.equal(kept.length, 4);
assert.ok(!kept.some((r) => r.sku === 'DWFC-NULL'));
});
test('(b) residue / rescrape / collision / staging rows produce NO SQL', () => {
const { byVendor } = buildPlans(PLAN, fixtureMap());
const allEntries = [...byVendor.values()].flat();
const apply = allEntries.map((e) => e.candidate).join('\n') + '\n' +
renderApplySql(allEntries);
for (const forbidden of ['DWFC-R', 'DWFC-RR', 'DWFC-C1', 'DWFC-C2', 'DWFC-S']) {
assert.ok(!apply.includes(forbidden), `forbidden candidate ${forbidden} leaked into apply set`);
}
// DWFC-1 → 2 statements (2 ids), DWFC-2/DWAG-9/CORK-3 → 1 each = 5 total.
assert.equal(allEntries.length, 5);
});
test('(c) every generated UPDATE carries the blank-guard', () => {
const { byVendor } = buildPlans(PLAN, fixtureMap());
for (const entries of byVendor.values()) {
const sql = renderApplySql(entries);
for (const line of sql.split('\n')) {
if (!line.startsWith('UPDATE')) continue;
assert.ok(
line.includes("(dw_sku IS NULL OR btrim(dw_sku)='')"),
`missing blank-guard: ${line}`
);
}
}
});
test('(d) undo.sql reverses apply.sql, keyed on shopify_id (stable cross-machine)', () => {
const entries = [{ id: 101, shopify_id: 'gid://shopify/Product/77', handle: 'h', candidate: 'DWFC-1' }];
const apply = renderApplySql(entries);
const undo = renderUndoSql(entries);
assert.ok(apply.includes("SET dw_sku='DWFC-1' WHERE shopify_id='gid://shopify/Product/77' AND (dw_sku IS NULL OR btrim(dw_sku)='')"));
assert.ok(undo.includes("SET dw_sku=NULL WHERE shopify_id='gid://shopify/Product/77' AND dw_sku='DWFC-1'"));
// MUST NOT key on the local serial id (meaningless on Kamatera).
assert.ok(!apply.includes('WHERE id='), 'apply must not key on local id');
});
test('(e) apostrophe candidates are SQL-escaped', () => {
const entries = [{ id: 301, shopify_id: 'g', handle: 'h', candidate: "O'CORK-3" }];
const apply = renderApplySql(entries);
const undo = renderUndoSql(entries);
assert.equal(sqlEscape("O'CORK-3"), "O''CORK-3");
assert.ok(apply.includes("dw_sku='O''CORK-3'"), 'apply must double the apostrophe');
assert.ok(undo.includes("dw_sku='O''CORK-3'"), 'undo must double the apostrophe');
});
test('(f) candidate that is a title / garbage shape is rejected, never written', () => {
const dirty = [
{ vendor: 'Cole & Son', sku: 'Bouncing Bubbles Mural - Cream', class: 'SELF_COPY_SOURCE', candidate: 'Bouncing Bubbles Mural - Cream' },
{ vendor: 'X', sku: 'DWKK-152210-Sold Per Bolt (20.5in x 33ft)', class: 'SELF_COPY_SOURCE', candidate: 'DWKK-152210-Sold Per Bolt (20.5in x 33ft)' },
{ vendor: 'Good', sku: 'AS784911-0', class: 'SELF_COPY_SOURCE', candidate: 'AS784911-0' }, // valid code-shape
];
const kept = selectSelfCopyRows(dirty);
assert.equal(kept.length, 1);
assert.equal(kept[0].candidate, 'AS784911-0');
assert.equal(kept.rejectedShape.length, 2);
});
test('(g) a self-copy row missing shopify_id is excluded + counted, never emitted', () => {
const plan = [{ vendor: 'V', sku: 'DWV-1', class: 'SELF_COPY_DW', candidate: 'DWV-1' }];
const map = new Map();
map.set(keyOf('DWV-1', 'V'), [
{ id: 1, shopify_id: '', handle: 'no-gid' }, // excluded
{ id: 2, shopify_id: 'gid://shopify/Product/2', handle: 'ok' }, // kept
]);
const plans = buildPlans(plan, map);
assert.equal(plans.noShopifyId, 1);
const entries = [...plans.byVendor.values()].flat();
assert.equal(entries.length, 1);
assert.equal(entries[0].shopify_id, 'gid://shopify/Product/2');
});
test('writePlans emits per-vendor artifacts + SUMMARY with correct totals', () => {
const out = join(mkdtempSync(join(tmpdir(), 'tk10896-apply-')), 'apply-plans');
const plans = buildPlans(PLAN, fixtureMap());
const summary = writePlans(plans, out);
assert.equal(summary.grand_total_statements, 5);
assert.equal(summary.vendors, 3); // Fabricut, Carnegie, Cork Co
assert.equal(summary.self_copy_plan_rows, 4);
const s = JSON.parse(readFileSync(join(out, 'SUMMARY.json'), 'utf8'));
assert.match(s.note, /NOTHING was executed/);
// restore-map old value is null (honest: pre-apply was blank; undo -> NULL) + keyed on shopify_id
const rm = JSON.parse(readFileSync(join(out, 'fabricut', 'restore-map.json'), 'utf8'));
assert.ok(rm.every((r) => r.old === null && r.column === 'dw_sku' && typeof r.shopify_id === 'string'));
// header present + gated
const applySql = readFileSync(join(out, 'fabricut', 'apply.sql'), 'utf8');
assert.match(applySql, /GATED -- canonical Kamatera dw_unified write/);
assert.match(applySql, /Do NOT run automatically/);
assert.match(applySql, /Recovers existing code \(no mint\)/);
});
// ---- mfr_sku allowlist HARD GATE (DTD verdict A, TK-10900) ------------------
test('GATE: a fabricated-mfr self-copy for a non-allowlisted vendor THROWS (cannot launder)', () => {
const bad = [{ vendor: 'Maharam', sku: '', class: 'SELF_COPY_MFR', candidate: 'MH-300110-055' }];
assert.throws(() => selectSelfCopyRows(bad), /NOT on MFR_SKU_REAL_ALLOWLIST/);
const badCol = [{ vendor: 'CMO Paris', sku: '', class: 'SELF_COPY_MFR_COLLISION', candidate: 'CMO_1' }];
assert.throws(() => selectSelfCopyRows(badCol), /NOT on MFR_SKU_REAL_ALLOWLIST/);
});
test('GATE: an allowlisted-vendor mfr self-copy does NOT throw, but this sku-keyed generator still emits no SQL for it', () => {
const ok = [{ vendor: 'Carnegie', sku: '', class: 'SELF_COPY_MFR', candidate: '47081' }];
const kept = selectSelfCopyRows(ok); // no throw
assert.equal(kept.length, 0); // mfr recovery has its own generator
});
test('GATE: mfr classes are forbidden here and MFR_SELF_COPY_CLASSES is exact', () => {
assert.deepEqual([...MFR_SELF_COPY_CLASSES].sort(), ['SELF_COPY_MFR', 'SELF_COPY_MFR_COLLISION']);
assert.ok(FORBIDDEN_CLASSES.has('SELF_COPY_MFR'));
assert.ok(FORBIDDEN_CLASSES.has('SELF_COPY_MFR_COLLISION'));
assert.ok(FORBIDDEN_CLASSES.has('MFR_FABRICATED_RESCRAPE'));
});