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

A repository's runs list names its Server-Timing phases: session, runs, render #3263

Merged opened this

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

Merged

scout/forge:scout/3261-a-repository-s-runs-list-names-its-serve → main 1 commit fork

  • Merged. scout/3261-a-repository-s-runs-list-names-its-serve was merged into main as 21e7328.

Review

Sign in to review this change.
2 files changed +59 −1
src/web/studio/lists.ts +9 −1
@@ -24,6 +24,7 @@ import { canRead, type Repo } from "../../core/repos.ts";
2424 import { waitingCount } from "../../actions/reports.ts";
2525
2626 import { listRepoRuns } from "../../actions/runs.ts";
27 import { notePhase, takeSessionTime } from "../../core/route_timing.ts";
2728 import { repoDir } from "../../git/repos.ts";
2829 import type { Ctx } from "../../http/context.ts";
2930 import {
@@ -604,9 +605,11 @@ export async function runsListPage(ctx: Ctx, repo: Repo): Promise<Response> {
604605 const filter = readWordFilter(ctx);
605606 const kind = readRunsKind(ctx);
606607 const cursor = ctx.url.searchParams.get("cursor") ?? undefined;
608 notePhase(ctx.request, "session", takeSessionTime(ctx.request));
607609
608610 // A private app's runs are for those who may enter it, and a sealed
609611 // report's for those who may see it (`listRepoRuns`).
612 const reading = performance.now();
610613 const listed = listRepoRuns(ctx.app, ctx.auth.user, repo, {
611614 kind,
612615 limit: LIST_PAGE,
@@ -635,6 +638,9 @@ export async function runsListPage(ctx: Ctx, repo: Repo): Promise<Response> {
635638 inWordFilter(r.info.word, filter),
636639 );
637640
641 notePhase(ctx.request, "runs", performance.now() - reading);
642
643 const rendering = performance.now();
638644 const list =
639645 rows.length === 0
640646 ? empty({
@@ -703,7 +709,7 @@ export async function runsListPage(ctx: Ctx, repo: Repo): Promise<Response> {
703709 </p>
704710 ${liveMount({ repo: base })}`;
705711
706 return page({
712 const res = page({
707713 ctx,
708714 title: pageTitle("runs", repo, true),
709715 crumbs: crumbsFor(repo),
@@ -711,4 +717,6 @@ export async function runsListPage(ctx: Ctx, repo: Repo): Promise<Response> {
711717 body,
712718 scriptJs: liveClientJs,
713719 });
720 notePhase(ctx.request, "render", performance.now() - rendering);
721 return res;
714722 }
test/runs-list-timing.test.ts added +50
@@ -0,0 +1,50 @@
1 /** A repository's runs list names its phases in Server-Timing (#3261). */
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 { createRepo } from "../src/actions/repos.ts";
8 import { createRun, finishRun } from "../src/core/runs.ts";
9 import { serve, type Servers } from "../src/http/server.ts";
10 import { seedAdmin } from "./helpers/admin.ts";
11
12 let dataDir = "";
13 let app: App, servers: Servers, bearer = "";
14
15 beforeAll(async () => {
16 dataDir = await mkdtemp(join(tmpdir(), "forge-rltime-"));
17 app = createApp({ dataDir, port: 0 });
18 servers = await serve(app);
19 const admin = await seedAdmin(app, { username: "alice", email: "[email protected]", password: "correct-horse-battery" });
20 if (!admin.ok) throw new Error(admin.error);
21 const made = await createRepo(app, admin.value, { name: "notes", visibility: "public", description: "" });
22 if (!made.ok) throw new Error(String(made.error));
23 const run = createRun(app.db, { repoId: made.value.id, kind: "check" });
24 finishRun(app.db, run.id, { state: "ok", summary: "fine" });
25 finishRun(app.db, createRun(app.db, { repoId: made.value.id, kind: "check" }).id, { state: "ok", summary: "fine" });
26 const minted = await fetch(`${servers.url}/api/v1/tokens`, {
27 method: "POST",
28 headers: { "content-type": "application/json" },
29 body: JSON.stringify({ adminConfirm: true, username: "alice", password: "correct-horse-battery", name: "cli", scope: "admin" }),
30 });
31 bearer = ((await minted.json()) as { token: string }).token;
32 });
33
34 afterAll(async () => {
35 servers.stop();
36 app.close();
37 await rm(dataDir, { recursive: true, force: true });
38 });
39
40 function names(res: Response): string[] {
41 return (res.headers.get("server-timing") ?? "").split(", ").map((p) => p.split(";")[0]!);
42 }
43
44 test("a repository's runs list names session, runs and render once each", async () => {
45 const res = await fetch(`${servers.url}/alice/notes/runs`, { headers: { authorization: `Bearer ${bearer}` } });
46 expect(res.status).toBe(200);
47 await res.text();
48 expect(res.headers.get("x-content-type-options")).toBe("nosniff");
49 for (const n of ["session", "runs", "render"]) expect(names(res).filter((x) => x === n).length).toBe(1);
50 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs