Rebasely Beta: data may be purged

examples/storefront Public

TypeScript · 67 lines · 3.1 KiB · at e83464e

import assert from "node:assert/strict";import { test } from "node:test";import { setup } from "./helpers/app.ts";import type { TestR2 } from "./helpers/r2.ts";const PNG = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 1, 2, 3, 4]);async function upload(s: ReturnType<typeof setup>, id: string, bytes: Uint8Array, type = "image/png") {  return s.call("PUT", `/admin/products/${id}/image`, bytes, { ...s.admin, "content-type": type });}const bucket = (s: ReturnType<typeof setup>) => s.env.IMAGES as unknown as TestR2;const keyOf = (url: string) => new URL(url).pathname.slice(1);test("an uploaded image is stored under a content hash and linked on the bucket's domain", async () => {  const s = setup();  await s.addProduct("wool-socks");  const res = await upload(s, "wool-socks", PNG);  assert.equal(res.status, 200);  assert.match(res.body.product.image_url, /^https:\/\/images\.shop\.test\/products\/wool-socks\/[0-9a-f]{16}\.png$/);  const stored = bucket(s).objects.get(keyOf(res.body.product.image_url))!;  assert.deepEqual([...stored.bytes], [...PNG]);  assert.equal(stored.httpMetadata.contentType, "image/png");  assert.equal(stored.httpMetadata.cacheControl, "public, max-age=31536000, immutable");});test("the image URL shows up in the catalog and in carts", async () => {  const s = setup();  await s.addProduct("wool-socks");  const url = (await upload(s, "wool-socks", PNG)).body.product.image_url;  assert.equal((await s.call("GET", "/products")).body.products[0].image_url, url);  const cartId = (await s.call("POST", "/carts")).body.cart.id;  const cart = (await s.call("PUT", `/carts/${cartId}/items/wool-socks`, { quantity: 1 })).body.cart;  assert.equal(cart.items[0].image_url, url);});test("a new image gets a new key and the old object stays", async () => {  const s = setup();  await s.addProduct("wool-socks");  const first = (await upload(s, "wool-socks", PNG)).body.product.image_url;  const second = (await upload(s, "wool-socks", new Uint8Array([...PNG, 9]))).body.product.image_url;  assert.notEqual(first, second);  assert.ok(bucket(s).objects.has(keyOf(first)), "carts and caches may still use the old URL");});test("refuses types that are not images and images over 5 MiB", async () => {  const s = setup();  await s.addProduct("wool-socks");  assert.equal((await upload(s, "wool-socks", PNG, "text/html")).status, 415);  assert.equal((await upload(s, "wool-socks", new Uint8Array(5 * 1024 * 1024 + 1))).status, 413);  assert.equal((await upload(s, "wool-socks", new Uint8Array(0))).status, 400);  assert.equal(bucket(s).objects.size, 0);});test("uploading needs the admin token and an existing product", async () => {  const s = setup();  assert.equal((await upload(s, "nope", PNG)).body.error, "product_not_found");  await s.addProduct("wool-socks");  const res = await s.call("PUT", "/admin/products/wool-socks/image", PNG, { "content-type": "image/png" });  assert.equal(res.status, 401);});test("the API no longer serves images itself", async () => {  const s = setup();  assert.equal((await s.call("GET", "/images/products/wool-socks/0011223344556677.png")).status, 404);});