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

The crew teams page's Server-Timing names its phases: session, each read, render #3435

Merged opened this

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

Merged

scout/forge:scout/3404-the-crew-teams-page-s-server-timing-name → main 1 commit fork

  • Merged. scout/3404-the-crew-teams-page-s-server-timing-name was merged into main as 744d378.

Review

Sign in to review this change.
2 files changed +55 −5
src/web/pages/teams.ts +12 −5
@@ -7,6 +7,7 @@
77 import { changeTeam, readTeams, teamChanges, TEAM_SAYS } from "../../actions/teams.ts";
88 import { CHARTER_MAX, DIAL_MAX } from "../../core/teams.ts";
99 import type { Ctx } from "../../http/context.ts";
10 import { notePhase, takeSessionTime, timePhase } from "../../core/route_timing.ts";
1011 import { notFoundPage, settingsLayout } from "../chrome.ts";
1112 import { field, readForm } from "../form.ts";
1213 import { eventStream } from "../../http/sse.ts";
@@ -19,11 +20,13 @@ const isAdminPerson = (ctx: Ctx): boolean =>
1920 ctx.auth.user?.isAdmin === true && ctx.auth.user.kind !== "agent";
2021
2122 /** The page's three regions, as HTML text: what the first paint and the stream both send. */
22 function regions(ctx: Ctx): Record<string, string> | undefined {
23 const read = readTeams(ctx.app, ctx.auth.user);
23 function regions(ctx: Ctx, timed = false): Record<string, string> | undefined {
24 const phase = <T>(name: string, work: () => T): T =>
25 timed ? timePhase(ctx.request, name, work) : work();
26 const read = phase("teams", () => readTeams(ctx.app, ctx.auth.user));
2427 if (!read.ok) return undefined;
2528 const { teams, agents } = read.value;
26 const changes = teamChanges(ctx.app, 10);
29 const changes = phase("changes", () => teamChanges(ctx.app, 10));
2730 const total = teams.reduce((n, t) => n + t.dial, 0);
2831 const cards = teams.map((t) =>
2932 card(
@@ -113,10 +116,12 @@ export function teamsStream(ctx: Ctx): Response {
113116
114117 // GET /settings/teams
115118 export function teamsPage(ctx: Ctx): Response {
116 const r = regions(ctx);
119 notePhase(ctx.request, "session", takeSessionTime(ctx.request));
120 const r = regions(ctx, true);
117121 if (!r) return notFoundPage(ctx);
122 const rendering = performance.now();
118123 const at = (id: string): Html => raw(r[id]!);
119 return page({
124 const out = page({
120125 ctx,
121126 title: "Teams",
122127 scriptJs: teamsLiveJs,
@@ -134,6 +139,8 @@ export function teamsPage(ctx: Ctx): Response {
134139 "Account settings",
135140 )}`,
136141 });
142 notePhase(ctx.request, "render", performance.now() - rendering);
143 return out;
137144 }
138145
139146 // POST /settings/teams/:slug
test/teams-timing.test.ts added +43
@@ -0,0 +1,43 @@
1 /** /settings/teams names its phases in Server-Timing. */
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 { seedAdmin } from "./helpers/admin.ts";
9
10 let dataDir = "";
11 let app: App, servers: Servers, bearer = "";
12
13 beforeAll(async () => {
14 dataDir = await mkdtemp(join(tmpdir(), "forge-ttime-"));
15 app = createApp({ dataDir, port: 0 });
16 servers = await serve(app);
17 await seedAdmin(app, { username: "alice", email: "[email protected]", password: "correct-horse-battery" });
18 const minted = await fetch(`${servers.url}/api/v1/tokens`, {
19 method: "POST",
20 headers: { "content-type": "application/json" },
21 body: JSON.stringify({ adminConfirm: true, username: "alice", password: "correct-horse-battery", name: "cli", scope: "admin" }),
22 });
23 bearer = ((await minted.json()) as { token: string }).token;
24 });
25
26 afterAll(async () => {
27 servers.stop();
28 app.close();
29 await rm(dataDir, { recursive: true, force: true });
30 });
31
32 test("the teams page names session, teams, changes and render once each", async () => {
33 const res = await fetch(`${servers.url}/settings/teams`, { headers: { authorization: `Bearer ${bearer}` } });
34 await res.arrayBuffer();
35 expect(res.status).toBe(200);
36 const parts = (res.headers.get("server-timing") ?? "").split(", ");
37 const names = parts.map((p) => p.split(";")[0]!);
38 for (const n of ["session", "teams", "changes", "render"]) {
39 expect(names.filter((x) => x === n).length).toBe(1);
40 const part = parts.find((p) => p.startsWith(`${n};`))!;
41 expect(Number(/dur=([\d.]+)/.exec(part)?.[1])).not.toBeNaN();
42 }
43 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs