/* eslint-disable */ /** * Smoke test — exercise the new /match endpoint end-to-end against * the live PB + n8n workflow. * * Run with: * env PB_USER_EMAIL=… PB_USER_PASSWORD=… NEXT_PUBLIC_POCKETBASE_URL=… * npx tsx scripts/smoke/match-endpoint.ts * * The script: * 1. Authenticates as a PB `users` (not superuser) account so the * route's `assertBrowserSession` passes. * 2. POSTs { sku, description } bodies to /api/v1/quotes//items//match * using a known item id from your live PB. * 3. Prints the candidates (sku, tier, why, cost). * * Replace QUOTE_ID and ITEM_ID with real ids from your PB before * running. The endpoint exists at /api/v1/quotes/[id]/items/[itemId]/match * — it requires the Next.js dev server to be running (or a built * server). */ import PocketBase from "pocketbase"; async function main() { const URL = process.env.NEXT_PUBLIC_POCKETBASE_URL; const USER_EMAIL = process.env.PB_USER_EMAIL; const USER_PASSWORD = process.env.PB_USER_PASSWORD; const QUOTE_ID = process.env.QUOTE_ID; const ITEM_ID = process.env.ITEM_ID; const DASHBOARD_URL = process.env.DASHBOARD_URL || "http://127.0.0.1:3000"; if (!URL || !USER_EMAIL || !USER_PASSWORD || !QUOTE_ID || !ITEM_ID) { throw new Error( "Set NEXT_PUBLIC_POCKETBASE_URL, PB_USER_EMAIL, PB_USER_PASSWORD, QUOTE_ID, ITEM_ID." ); } // Auth as a regular PB user. const pb = new PocketBase(URL); await pb.collection("users").authWithPassword(USER_EMAIL, USER_PASSWORD); const token = pb.authStore.token; if (!token) throw new Error("PB auth succeeded but no token returned."); console.log(`Authed as ${USER_EMAIL} (token length ${token.length})`); async function tryMatch(label: string, body: Record) { const url = `${DASHBOARD_URL.replace(/\/$/, "")}/api/v1/quotes/${QUOTE_ID}/items/${ITEM_ID}/match`; console.log(`\n ${label}`); console.log(` body: ${JSON.stringify(body)}`); const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", Authorization: token }, body: JSON.stringify(body), }); const data = await res.json(); if (!res.ok) { console.log(` HTTP ${res.status}: ${data.error || "(no error message)"}`); return; } const candidates = Array.isArray(data.candidates) ? data.candidates : []; console.log(` ${candidates.length} candidate(s)`); for (const c of candidates) { console.log(` tier ${c.tier} ${c.sku} R${c.cost_price} "${c.why}"`); } } console.log("=== /match endpoint smoke test ==="); await tryMatch("Tier 1 — exact SKU", { sku: "JEITD-06" }); await tryMatch("Tier 2 — SKU substring", { sku: "JEITD" }); await tryMatch("Tier 3 — fuzzy description", { description: "TURBO" }); await tryMatch("Tier 3 — fuzzy 'twist'", { description: "twist" }); await tryMatch("SKU + description combo", { sku: "JEITD", description: "turbo" }); await tryMatch("Both empty (400 expected)", { sku: "", description: "" }); await tryMatch("Garbage (no match expected)", { sku: "ZZZGARBAGE", description: "" }); } main().catch((e) => { console.error(e); process.exit(1); });