← back to Whatsmystyle
scripts/test-receipts.js
131 lines
/**
* Receipt-parser smoke test (tick 14).
*
* Sanity-checks that scripts/parse-receipts.js routes each fixture to the
* right adapter and extracts brand + title + price. No real Gmail calls,
* no DB writes — pure function-call asserts against inline fixtures.
*
* Run:
* node scripts/test-receipts.js
*
* Exit code is 0 on all-pass, 1 on any failure.
*/
const { parseEmail, pickAdapter } = require('./parse-receipts');
const FIXTURES = [
{
name: 'nordstrom',
email: {
from: 'orders@emails.nordstrom.com',
subject: 'Your order has shipped',
body: [
'Hi Steve,',
'Your order has shipped. Items:',
'',
'Vince',
'Crew Neck Cashmere Sweater',
'$295.00',
'Size: M',
'',
'AGOLDE',
'Riley High Rise Straight Jean',
'$198.00',
'Size: 30',
].join('\n'),
},
expect: {
adapter: 'nordstrom',
minItems: 2,
brands: ['Vince', 'AGOLDE'],
titles: ['Crew Neck Cashmere Sweater', 'Riley High Rise Straight Jean'],
priceCents: [29500, 19800],
},
},
{
name: 'netaporter',
email: {
from: 'orders@emails.net-a-porter.com',
subject: 'Your NET-A-PORTER order has been dispatched',
body: [
'Dear Customer,',
'',
'The Row ',
'Crewneck Cashmere Sweater ',
'USD 1,290.00',
'',
'Khaite ',
'Stretch Leather Bag ',
'USD 2,100.00',
].join('\n'),
},
expect: {
adapter: 'netaporter',
minItems: 2,
brands: ['The Row', 'Khaite'],
titles: ['Crewneck Cashmere Sweater', 'Stretch Leather Bag'],
priceCents: [129000, 210000],
},
},
{
name: 'poshmark',
email: {
from: 'no-reply@poshmark.com',
subject: 'You bought it! Order confirmation',
body: [
'You bought it!',
'',
'Reformation',
'Juliette Linen Midi Dress',
'$118.00',
'',
'Acne Studios',
'Wool Crew Sweater Black',
'$240.00',
].join('\n'),
},
expect: {
adapter: 'poshmark',
minItems: 2,
brands: ['Reformation', 'Acne Studios'],
titles: ['Juliette Linen Midi Dress', 'Wool Crew Sweater Black'],
priceCents: [11800, 24000],
},
},
];
let pass = 0, fail = 0;
const failures = [];
function check(name, condition, detail) {
if (condition) { pass++; }
else { fail++; failures.push(`[${name}] ${detail}`); }
}
for (const fx of FIXTURES) {
const picked = pickAdapter({ from: fx.email.from, subject: fx.email.subject });
check(fx.name, picked.name === fx.expect.adapter, `expected adapter=${fx.expect.adapter}, got ${picked.name}`);
const parsed = parseEmail(fx.email);
check(fx.name, parsed.adapter === fx.expect.adapter, `parsed.adapter mismatch: ${parsed.adapter}`);
check(fx.name, parsed.items.length >= fx.expect.minItems, `expected ≥${fx.expect.minItems} items, got ${parsed.items.length}`);
// Assert each expected brand/title/price appears in the parsed items.
for (let i = 0; i < fx.expect.brands.length; i++) {
const want = { brand: fx.expect.brands[i], title: fx.expect.titles[i], price_cents: fx.expect.priceCents[i] };
const hit = parsed.items.find(it =>
it.brand === want.brand &&
it.title === want.title &&
it.price_cents === want.price_cents);
check(fx.name, !!hit, `missing item: ${JSON.stringify(want)} — got ${JSON.stringify(parsed.items)}`);
}
}
const out = {
ok: fail === 0,
pass, fail,
failures: failures.slice(0, 20),
fixtures: FIXTURES.map(f => f.name),
};
console.log(JSON.stringify(out, null, 2));
process.exit(fail === 0 ? 0 : 1);