← back to Estimate Instant
Harden roll specs and SKU resolution
062bfadea250b40aa20be14e8178151c8cebd4f8 · 2026-08-28 22:14:29 -0700 · Steve Abrams
Files touched
M server.jsM test/calculate-coverage.test.jsM verification/e2e-proof.json
Diff
commit 062bfadea250b40aa20be14e8178151c8cebd4f8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Aug 28 22:14:29 2026 -0700
Harden roll specs and SKU resolution
---
server.js | 28 ++++++++++++++++++++++++----
test/calculate-coverage.test.js | 39 ++++++++++++++++++++++++++++++++++++++-
verification/e2e-proof.json | 4 +++-
3 files changed, 65 insertions(+), 6 deletions(-)
diff --git a/server.js b/server.js
index b4c9e61..774f1da 100644
--- a/server.js
+++ b/server.js
@@ -165,12 +165,28 @@ function calculateCoverage(input, rolls = loadRolls(), checkout_domain) {
if (errors.length) return { ok: false, errors };
const key = sku.toUpperCase();
- const roll = rolls.find((item) =>
- String(item.sku || '').toUpperCase() === key ||
- (item.shopify_match === true && String(item.shopify_sku || '').toUpperCase() === key)
- );
+ const canonicalMatches = rolls.filter((item) => String(item.sku || '').toUpperCase() === key);
+ if (canonicalMatches.length > 1) {
+ return { ok: false, errors: [`Ambiguous local roll specification for SKU ${sku}.`] };
+ }
+ const aliasMatches = canonicalMatches.length === 0
+ ? rolls.filter((item) => item.shopify_match === true && String(item.shopify_sku || '').toUpperCase() === key)
+ : [];
+ if (aliasMatches.length > 1) {
+ return { ok: false, errors: [`Ambiguous matched Shopify SKU ${sku}.`] };
+ }
+ const roll = canonicalMatches[0] || aliasMatches[0];
if (!roll) return { ok: false, errors: [`No local roll specification found for SKU ${sku}.`] };
+ const validPositiveSpec = (value) => typeof value === 'number' && Number.isFinite(value) && value > 0;
+ const validRepeat = typeof roll.pattern_repeat_in === 'number' &&
+ Number.isFinite(roll.pattern_repeat_in) && roll.pattern_repeat_in >= 0;
+ const match = String(roll.match || '').trim().toLowerCase();
+ if (!validPositiveSpec(roll.roll_width_in) || !validPositiveSpec(roll.roll_length_ft) ||
+ !validRepeat || !['random', 'straight', 'half-drop'].includes(match)) {
+ return { ok: false, errors: ['Local roll specification is invalid; verify width, length, repeat, and match.'] };
+ }
+
const totalWidthIn = roomWidthFt * 12 * numWalls;
const wallHeightIn = roomHeightFt * 12;
if (!Number.isFinite(totalWidthIn) || Math.abs(totalWidthIn) > Number.MAX_SAFE_INTEGER || !Number.isFinite(wallHeightIn)) {
@@ -194,6 +210,10 @@ function calculateCoverage(input, rolls = loadRolls(), checkout_domain) {
if (numericOutputs.some((value) => typeof value !== 'number' || !Number.isFinite(value))) {
return { ok: false, errors: ['Roll specifications produced a non-finite calculation; verify the product data.'] };
}
+ if (![result.rollsNeeded, result.totalStrips, result.stripsPerRoll].every(Number.isSafeInteger) ||
+ result.rollsNeeded < 1 || result.totalStrips < 1 || result.stripsPerRoll < 1 || result.cutLengthIn <= 0) {
+ return { ok: false, errors: ['Roll specifications produced an invalid quantity; verify the product data.'] };
+ }
const notes = [
`Assumes ${numWalls} wall${numWalls === 1 ? '' : 's'} at ${roomWidthFt} ft wide by ${roomHeightFt} ft high.`,
diff --git a/test/calculate-coverage.test.js b/test/calculate-coverage.test.js
index 25937ba..a4ed230 100644
--- a/test/calculate-coverage.test.js
+++ b/test/calculate-coverage.test.js
@@ -78,7 +78,44 @@ test('fails closed when malformed roll data produces non-finite output', () => {
const broken = { ...roll, roll_width_in: 0 };
const result = calculateCoverage({ sku: roll.sku, room_width_ft: 10, room_height_ft: 8, num_walls: 1 }, [broken]);
assert.equal(result.ok, false);
- assert.match(result.errors.join(' '), /non-finite calculation/i);
+ assert.match(result.errors.join(' '), /roll specification is invalid/i);
+});
+
+test('rejects invalid finite roll specs and unsupported match values', () => {
+ for (const patch of [
+ { roll_width_in: -24 },
+ { roll_length_ft: -30 },
+ { pattern_repeat_in: -1 },
+ { match: 'mystery' },
+ ]) {
+ const result = calculateCoverage(
+ { sku: roll.sku, room_width_ft: 10, room_height_ft: 8, num_walls: 1 },
+ [{ ...roll, ...patch }]
+ );
+ assert.equal(result.ok, false);
+ assert.match(result.errors.join(' '), /roll specification is invalid/i);
+ }
+});
+
+test('prefers an exact canonical SKU and rejects ambiguous aliases', () => {
+ const alias = { ...roll, sku: 'PROTO', shopify_sku: 'COLLIDE', shopify_match: true };
+ const canonical = { ...roll, sku: 'COLLIDE', shopify_sku: 'OTHER', shopify_match: true };
+ const exact = calculateCoverage(
+ { sku: 'COLLIDE', room_width_ft: 10, room_height_ft: 8, num_walls: 1 },
+ [alias, canonical]
+ );
+ assert.equal(exact.ok, true);
+ assert.equal(exact.sku, 'COLLIDE');
+
+ const ambiguous = calculateCoverage(
+ { sku: 'SAME-ALIAS', room_width_ft: 10, room_height_ft: 8, num_walls: 1 },
+ [
+ { ...roll, sku: 'ONE', shopify_sku: 'SAME-ALIAS' },
+ { ...roll, sku: 'TWO', shopify_sku: 'SAME-ALIAS' },
+ ]
+ );
+ assert.equal(ambiguous.ok, false);
+ assert.match(ambiguous.errors.join(' '), /ambiguous matched Shopify SKU/i);
});
test('rejects missing SKU, invalid dimensions, fractional walls, and unknown SKU', () => {
diff --git a/verification/e2e-proof.json b/verification/e2e-proof.json
index 909c364..fa1437b 100644
--- a/verification/e2e-proof.json
+++ b/verification/e2e-proof.json
@@ -11,7 +11,7 @@
"verdict": "PASS",
"boundary": "calculation module",
"command": "node --test test/calculate-coverage.test.js",
- "assertions": "7/7 pass: room dimensions map to rolls; exact matched alias works; stand-in aliases fail; prototype pricing is withheld; coercible/oversized inputs and non-finite outputs fail closed"
+ "assertions": "9/9 pass: room dimensions map to rolls; exact canonical SKU outranks aliases; ambiguous and stand-in aliases fail; prototype pricing is withheld; coercible/oversized inputs and invalid roll specs fail closed"
},
{
"verdict": "PASS",
@@ -35,6 +35,8 @@
"boolean, string, and array dimensions",
"dimensions beyond documented bounds",
"false-match Shopify alias",
+ "alias collision with exact canonical SKU and ambiguous aliases",
+ "negative roll width/length/repeat and unsupported match",
"non-finite estimator output from malformed roll data",
"empty HTTP payload"
],
← 8e0c9d3 Add fail-closed room coverage API
·
back to Estimate Instant
·
auto-data-snapshot: 2026-08-28T22:35:10 (2 data files) — REA ef09b11 →