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

A variant group's page's Server-Timing names its phases: session, each read, render #3454

Merged opened this

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

Merged

scout/forge:scout/3424-a-variant-group-s-page-s-server-timing-n → main 1 commit fork

  • Merged. scout/3424-a-variant-group-s-page-s-server-timing-n was merged into main as 602ac57.

Review

Sign in to review this change.
2 files changed +86 −4
src/web/pages/variants.ts +11 −4
@@ -9,6 +9,7 @@ import { DEFAULT_REASON, REASON_MAX, voteVariant } from "../../actions/variants.
99 import { getAppByRepo } from "../../core/apps.ts";
1010 import { variantMembers, type Pull } from "../../core/pulls.ts";
1111 import { canWrite, type Repo } from "../../core/repos.ts";
12 import { notePhase, takeSessionTime, timePhase } from "../../core/route_timing.ts";
1213 import { mayView } from "../../core/sealed.ts";
1314 import type { Ctx } from "../../http/context.ts";
1415 import {
@@ -85,14 +86,18 @@ function deckCard(
8586
8687 // GET /:owner/:name/variants/:group
8788 export function variantsPage(ctx: Ctx): Response {
88 const repo = loadRepo(ctx);
89 notePhase(ctx.request, "session", takeSessionTime(ctx.request));
90 const repo = timePhase(ctx.request, "repo", () => loadRepo(ctx));
8991 if (!repo) return notFoundPage(ctx);
9092 const group = ctx.params.group ?? "";
91 const pulls = deck(ctx, repo, group);
93 const pulls = timePhase(ctx.request, "pulls", () => deck(ctx, repo, group));
9294 if (pulls.length === 0) return notFoundPage(ctx);
9395 const open = pulls.filter((p) => p.state === "open" && p.mergedAt === null);
94 const writer = canWrite(ctx.app.db, repo, ctx.auth.user);
95 return page({
96 const writer = timePhase(ctx.request, "access", () =>
97 canWrite(ctx.app.db, repo, ctx.auth.user),
98 );
99 const rendering = performance.now();
100 const response = page({
96101 ctx,
97102 title: `Variants in ${repo.name}`,
98103 crumbs: crumbsFor(repo),
@@ -104,6 +109,8 @@ export function variantsPage(ctx: Ctx): Response {
104109 deckCard(ctx, repo, p, group, writer, open.length === 1),
105110 )}`,
106111 });
112 notePhase(ctx.request, "render", performance.now() - rendering);
113 return response;
107114 }
108115
109116 // POST /:owner/:name/variants/:group/:number/(like|dislike)
test/variants-timing.test.ts added +75
@@ -0,0 +1,75 @@
1 /** `/:owner/:name/variants/:group` names session, repo, pulls, access and render in Server-Timing (#3424). */
2 import { afterAll, beforeAll, expect, test } from "bun:test";
3 import { mkdtemp, rm } from "node:fs/promises";
4 import { tmpdir } from "node:os";
5 import { join } from "node:path";
6 import { createApp, type App } from "../src/app.ts";
7 import { serve, type Servers } from "../src/http/server.ts";
8 import { gitClientEnv } from "./helpers/git-client.ts";
9 import { adminCookie } from "./helpers/admin.ts";
10
11 const gitEnv = gitClientEnv();
12 let dataDir = "";
13 let work = "";
14 let app: App;
15 let servers: Servers;
16 let cookie = "";
17
18 async function git(cwd: string, args: string[]): Promise<void> {
19 const p = Bun.spawn(["git", ...args], { cwd, env: gitEnv, stdout: "ignore", stderr: "pipe" });
20 const [err, code] = await Promise.all([new Response(p.stderr).text(), p.exited]);
21 if (code !== 0) throw new Error(`git ${args.join(" ")}: ${err}`);
22 }
23
24 beforeAll(async () => {
25 dataDir = await mkdtemp(join(tmpdir(), "forge-vt-"));
26 work = await mkdtemp(join(tmpdir(), "forge-vt-work-"));
27 app = createApp({ dataDir, port: 0 });
28 servers = await serve(app);
29 const base = servers.url;
30 cookie = await adminCookie(app, { username: "dev", email: "[email protected]", password: "variants-timing-pass" });
31 const token = await fetch(`${base}/api/v1/tokens`, {
32 method: "POST",
33 headers: { "content-type": "application/json" },
34 body: JSON.stringify({ adminConfirm: true, username: "dev", password: "variants-timing-pass", name: "cli", scope: "rw" }),
35 })
36 .then((r) => r.json())
37 .then((j) => (j as { token: string }).token);
38 const auth = { authorization: `Bearer ${token}`, "content-type": "application/json" };
39 await fetch(`${base}/api/v1/repos`, {
40 method: "POST",
41 headers: auth,
42 body: JSON.stringify({ name: "app", visibility: "public" }),
43 });
44 await git(work, ["clone", `${base.replace("http://", `http://dev:${token}@`)}/dev/app.git`, "app"]);
45 const dir = join(work, "app");
46 await Bun.write(join(dir, "code.txt"), "alpha\n");
47 await git(dir, ["add", "."]);
48 await git(dir, ["commit", "-m", "base"]);
49 await git(dir, ["push", "origin", "main"]);
50 await git(dir, ["checkout", "-B", "a", "main"]);
51 await Bun.write(join(dir, "code.txt"), "alpha\na\n");
52 await git(dir, ["commit", "-am", "a"]);
53 await git(dir, ["push", "origin", "a"]);
54 await fetch(`${base}/api/v1/repos/dev/app/pulls`, {
55 method: "POST",
56 headers: auth,
57 body: JSON.stringify({ base: "main", head: "a", title: "Try a" }),
58 });
59 app.db.run("UPDATE pull SET variant_group = 'deck1'");
60 });
61 afterAll(async () => {
62 servers.stop();
63 app.close();
64 await rm(dataDir, { recursive: true, force: true });
65 await rm(work, { recursive: true, force: true });
66 });
67
68 test("GET /:owner/:name/variants/:group names its phases", async () => {
69 const res = await fetch(`${servers.url}/dev/app/variants/deck1`, { headers: { cookie } });
70 expect(res.status).toBe(200);
71 expect(await res.text()).toContain("Try a");
72 const parts = (res.headers.get("server-timing") ?? "").split(", ");
73 for (const name of ["app", "session", "repo", "pulls", "access", "render"])
74 expect(parts.find((p) => p.startsWith(`${name};`))).toMatch(/^[a-z]+;dur=\d+\.\d$/);
75 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs