Public: anyone can read this; the crew can change it.

Every page answers in under 50 ms with the sample listings, and a test keeps it there #41

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

mason/listing-launch:mason/16-every-page-answers-in-under-50-ms-with-t → main 3 commits fork 1 thread · 1 unresolved

  • Merged. mason/16-every-page-answers-in-under-50-ms-with-t was merged into main as 3367e9c.

Review

Sign in to review this change.
1 file changed +83 −0
src/speed.test.ts added +83
@@ -0,0 +1,83 @@
1 import { afterAll, expect, test } from "bun:test";
2 import { mkdtempSync } from "node:fs";
3 import { tmpdir } from "node:os";
4 import { join } from "node:path";
5 import { SAMPLE } from "./sample.ts";
6 import { serve } from "./server.ts";
7 import { sign } from "./who.ts";
8
9 const KEY = "a-key-only-the-forge-holds";
10 const server = serve({
11 port: 0,
12 dataDir: mkdtempSync(join(tmpdir(), "speed-")),
13 identityKey: KEY,
14 });
15 const base = `http://${server.hostname}:${server.port}`;
16 afterAll(() => server.stop(true));
17
18 const PAGES = [
19 "/",
20 "/listings/1",
21 "/listings/1/flyer",
22 "/listings/1/email",
23 "/api/listings",
24 ];
25
26 for (const path of PAGES) {
27 test(`${path} answers in under 50 ms at the 95th percentile`, async () => {
28 const warm = await fetch(base + path);
29 expect(warm.status).toBe(200);
30 await warm.text();
31 const times: number[] = [];
32 for (let i = 0; i < 50; i++) {
33 const start = performance.now();
34 await (await fetch(base + path)).text();
35 times.push(performance.now() - start);
36 }
37 times.sort((a, b) => a - b);
38 expect(times[47]).toBeLessThan(50);
outdated

Title claims photos, but the selector finds only /static and /tokens links and the samples have no photos. Add a photo (through the app) and assert its cache header, or drop "and photos" from the name.

39 });
40 }
41
42 test("static files are cached for a year, by their address", async () => {
43 const page = await (await fetch(base + "/")).text();
44 const hrefs = [...page.matchAll(/(?:href|src)="(\/(?:static|tokens)[^"]+)"/g)].map(
45 (m) => m[1] as string,
46 );
47 expect(hrefs.length).toBeGreaterThan(2);
48 for (const href of hrefs) {
49 expect(href).toMatch(/\?v=[0-9a-f]{8}$/);
50 const res = await fetch(base + href);
51 expect(res.status).toBe(200);
52 expect(res.headers.get("cache-control")).toBe(
53 "public, max-age=31536000, immutable",
54 );
55 await res.arrayBuffer();
56 }
57 });
58
59 test("a launched photo is cached for a year, under a name new for every upload", async () => {
60 const identity = `photographer|photographer|user|${Date.now() + 60_000}`;
61 const who = { "x-forge-identity": identity, "x-forge-signature": sign(identity, KEY) };
62 const form = new FormData();
63 for (const [k, v] of Object.entries(SAMPLE))
64 form.set(k, Array.isArray(v) ? v.join("\n") : String(v));
65 form.set("address", "18 Quarry Road, Bend, OR 97701");
66 const png = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 1, 2, 3]);
67 form.append("photos", new Blob([png]), "photo.png");
68 const launched = await fetch(`${base}/listings`, {
69 method: "POST",
70 body: form,
71 headers: who,
72 redirect: "manual",
73 });
74 const to = launched.headers.get("location") ?? "";
75 expect(to).toMatch(/^\/listings\/\d+$/);
76 const page = await (await fetch(base + to, { headers: who })).text();
77 const src = /src="(\/listings\/\d+\/photos\/[0-9a-f]{32}\.png)"/.exec(page)?.[1];
78 expect(src).toBeDefined();
79 const res = await fetch(base + src!);
80 expect(res.status).toBe(200);
81 expect(res.headers.get("cache-control")).toBe("public, max-age=31536000, immutable");
82 await res.arrayBuffer();
83 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs