Rebasely Beta: data may be purged

examples/storefront Public

TypeScript · 101 lines · 5.1 KiB · at 75e565d

import assert from "node:assert/strict";import { test } from "node:test";import { toMatch } from "../src/search.ts";import { ADMIN_TOKEN, setup } from "./helpers/app.ts";const ids = (res: { body: { products: { id: string }[] } }) => res.body.products.map((p) => p.id);async function shop() {  const s = setup();  await s.addProduct("linen-apron", { name: "Linen work apron", description: "Heavy linen with two front pockets and cross-back straps.", price_cents: 5200 });  await s.addProduct("canvas-tote", { name: "Waxed canvas tote", description: "A 20 litre tote with leather handles. Pairs well with the linen apron.", price_cents: 4800 });  await s.addProduct("enamel-mug", { name: "Enamel camp mug", description: "A 350 ml steel mug with a rolled rim.", price_cents: 1600 });  await s.addProduct("creme-soap", { name: "Crème fraîche soap", description: "Cold process, unscented.", price_cents: 900 });  return s;}test("toMatch quotes every word and matches the last one as a prefix", () => {  assert.equal(toMatch("linen apr"), '"linen" "apr"*');  assert.equal(toMatch("  Mug!  "), '"Mug"*');});test("toMatch turns FTS5 syntax into plain words", () => {  assert.equal(toMatch('apron" OR name:mug NEAR(x'), '"apron" "OR" "name" "mug" "NEAR" "x"*');  assert.equal(toMatch("*** --- ()"), null);});test("finds products by name and description, name matches first", async () => {  const s = await shop();  const res = await s.call("GET", "/search?q=linen");  assert.equal(res.status, 200);  assert.equal(res.body.query, "linen");  assert.deepEqual(ids(res), ["linen-apron", "canvas-tote"]);});test("matches while the shopper is still typing, across word forms and accents", async () => {  const s = await shop();  assert.deepEqual(ids(await s.call("GET", "/search?q=enam")), ["enamel-mug"]);  assert.deepEqual(ids(await s.call("GET", "/search?q=mugs")), ["enamel-mug"]);  assert.deepEqual(ids(await s.call("GET", "/search?q=creme")), ["creme-soap"]);});test("results carry the catalog's current price and stock", async () => {  const s = await shop();  const cartId = (await s.call("POST", "/carts")).body.cart.id;  await s.call("PUT", `/carts/${cartId}/items/enamel-mug`, { quantity: 2 });  await s.call("POST", `/carts/${cartId}/checkout`, {    email: "ada@park.test",    ship_to: { name: "Ada Park", line1: "12 Alder St", city: "Portland", postal_code: "97205", country: "US" },  });  const [mug] = (await s.call("GET", "/search?q=mug")).body.products;  assert.equal(mug.stock, 3);  assert.equal(mug.price_cents, 1600);});test("an edited product is found by its new text, a retired one not at all", async () => {  const s = await shop();  await s.addProduct("enamel-mug", { name: "Enamel camp cup", description: "A 350 ml steel cup." });  assert.deepEqual(ids(await s.call("GET", "/search?q=mug")), []);  assert.deepEqual(ids(await s.call("GET", "/search?q=cup")), ["enamel-mug"]);  await s.addProduct("linen-apron", { name: "Linen work apron", active: false });  assert.deepEqual(ids(await s.call("GET", "/search?q=apron")), ["canvas-tote"]);});test("limit caps the results and q is required", async () => {  const s = await shop();  assert.deepEqual(ids(await s.call("GET", "/search?q=linen&limit=1")), ["linen-apron"]);  assert.equal((await s.call("GET", "/search?q=%20")).body.error, "bad_query");  assert.equal((await s.call("GET", `/search?q=${"a".repeat(101)}`)).body.error, "bad_query");  assert.equal((await s.call("GET", "/search?q=linen&limit=0")).body.error, "bad_limit");  assert.deepEqual(ids(await s.call("GET", "/search?q=%2A%2A")), []);});test("a rebuild puts every active product back in the index", async () => {  const s = await shop();  // A product written straight to the catalog, as a seed script does, is not in the index yet.  await s.env.DB.prepare(    "INSERT INTO products (id, name, description, price_cents, stock, active, created_at, updated_at) VALUES ('wool-socks', 'Merino wool socks', '', 1800, 40, 1, 0, 0)",  ).run();  assert.deepEqual(ids(await s.call("GET", "/search?q=socks")), []);  assert.equal((await s.call("POST", "/admin/search/rebuild")).status, 401);  const res = await s.call("POST", "/admin/search/rebuild", undefined, { authorization: `Bearer ${ADMIN_TOKEN}` });  assert.deepEqual(res.body, { indexed: 5 });  assert.deepEqual(ids(await s.call("GET", "/search?q=socks")), ["wool-socks"]);  assert.deepEqual(ids(await s.call("GET", "/search?q=linen")), ["linen-apron", "canvas-tote"]);});test("a failed index write is reported, and the product is still saved", async () => {  const s = await shop();  const search = s.env.SEARCH;  s.env.SEARCH = {    prepare: (sql: string) => search.prepare(sql),    batch: async () => {      throw new Error("D1_ERROR: storefront-search is unavailable");    },  } as unknown as D1Database;  const res = await s.call("PUT", "/admin/products/enamel-mug", { name: "Enamel camp cup", price_cents: 1600 }, s.admin);  assert.equal(res.status, 503);  assert.equal(res.body.error, "search_index_failed");  assert.equal((await s.call("GET", "/products/enamel-mug")).body.product.name, "Enamel camp cup");});