← back to Sanderson Onboard
tk10873/test_optionA.mjs
61 lines
#!/usr/bin/env node
// test_optionA.mjs — zero-dependency unit tests for the pure pricing/classification
// functions in lib.mjs. No DB, no network. Exit nonzero on any failure.
import { retailFromTrade, classify, proposedSellablePrice, buildPlanRow, SAMPLE_PRICE } from './lib.mjs';
let pass = 0, fail = 0;
function ok(cond, msg) { if (cond) { pass++; } else { fail++; console.error(`FAIL: ${msg}`); } }
function near(a, b, eps = 0.01) { return a != null && Math.abs(a - b) <= eps; }
// --- retailFromTrade: the three verified anchors ---
ok(near(retailFromTrade(191), 345.70), `retailFromTrade(191)=${retailFromTrade(191)} expect 345.70`);
ok(near(retailFromTrade(208), 376.47), `retailFromTrade(208)=${retailFromTrade(208)} expect 376.47`);
ok(near(retailFromTrade(177), 320.36), `retailFromTrade(177)=${retailFromTrade(177)} expect 320.36`);
ok(retailFromTrade(0) === null, 'retailFromTrade(0) -> null');
ok(retailFromTrade(null) === null, 'retailFromTrade(null) -> null');
ok(retailFromTrade('abc') === null, 'retailFromTrade(non-numeric) -> null');
// --- classify: the three buckets on synthetic rows ---
// REPRICE: has sellable + staged price
ok(classify({ has_product_variant: true, joined: true, price_retail: 345.70 }) === 'REPRICE', 'REPRICE row');
// SELLABLE_ADD: missing sellable + usable staged price
ok(classify({ has_product_variant: false, joined: true, price_retail: 200 }) === 'SELLABLE_ADD', 'SELLABLE_ADD row');
// HELD: missing sellable + no usable price
ok(classify({ has_product_variant: false, joined: true, price_retail: 0 }) === 'HELD_NEEDS_PRICE', 'HELD outlier no-price');
ok(classify({ has_product_variant: false, joined: false, price_retail: null }) === 'HELD_NEEDS_PRICE', 'HELD outlier no-join');
// HELD: has sellable but joins with null/0 price (cannot reprice)
ok(classify({ has_product_variant: true, joined: true, price_retail: null }) === 'HELD_NEEDS_PRICE', 'HELD priced-null');
ok(classify({ has_product_variant: true, joined: true, price_retail: 0 }) === 'HELD_NEEDS_PRICE', 'HELD price-0');
// HELD: has sellable but NO staging join at all
ok(classify({ has_product_variant: true, joined: false, price_retail: null }) === 'HELD_NEEDS_PRICE', 'HELD no-join sellable');
// --- proposedSellablePrice ---
ok(proposedSellablePrice({ price_retail: 345.70 }, 'REPRICE') === 345.70, 'proposed REPRICE = staged retail');
ok(proposedSellablePrice({ price_retail: 200 }, 'SELLABLE_ADD') === 200, 'proposed SELLABLE_ADD = staged retail');
ok(proposedSellablePrice({ price_retail: null }, 'HELD_NEEDS_PRICE') === null, 'proposed HELD = null');
// --- buildPlanRow end-to-end + sample invariant ---
const rp = buildPlanRow({ mfr_sku: 'X1', joined: true, has_product_variant: true, has_sample_variant: true, variant_count: 2, price_trade: 208, price_retail: 376.47 });
ok(rp.action === 'REPRICE', 'buildPlanRow REPRICE action');
ok(rp.proposed_sellable_price === 376.47, 'buildPlanRow REPRICE proposed');
ok(rp.sample_price === SAMPLE_PRICE && rp.sample_price === 4.25, 'buildPlanRow sample invariant 4.25');
ok(rp.proposed_sellable_price > rp.sample_price, 'buildPlanRow proposed > sample');
const held = buildPlanRow({ mfr_sku: 'X2', joined: false, has_product_variant: true, has_sample_variant: true, variant_count: 2, price_trade: null, price_retail: null });
ok(held.action === 'HELD_NEEDS_PRICE', 'buildPlanRow HELD action');
ok(held.proposed_sellable_price === null, 'buildPlanRow HELD proposed null');
ok(held.sample_price === 4.25, 'buildPlanRow HELD sample still 4.25');
// SELLABLE_ADD outlier (DWWC-502880 shape): missing sellable, joined w/ price
const add = buildPlanRow({ mfr_sku: '313114', joined: true, has_product_variant: false, has_sample_variant: true, variant_count: 1, price_trade: 177, price_retail: 320.36 });
ok(add.action === 'SELLABLE_ADD', 'buildPlanRow SELLABLE_ADD action');
ok(add.proposed_sellable_price === 320.36, 'buildPlanRow SELLABLE_ADD proposed');
ok(add.proposed_sellable_price > add.sample_price, 'buildPlanRow SELLABLE_ADD proposed > sample');
// margin guard: proposed retail must exceed trade (never below cost)
ok(rp.proposed_sellable_price > rp.price_trade, 'buildPlanRow proposed > trade (margin)');
ok(add.proposed_sellable_price > add.price_trade, 'buildPlanRow SELLABLE_ADD proposed > trade (margin)');
console.log(`\ntest_optionA: ${pass} passed, ${fail} failed`);
process.exit(fail === 0 ? 0 : 1);