Fork Horse repository, listed by its owner. Publisher snapshot.

packages/forkhorse-dashboard/test/review-fixtures.ts

Snapshot at 34e3175

Containing directory

6,143 bytes · typescriptView raw
1import type { ReviewDetail, ReviewDiff, StateResponse } from "../src/contracts.ts";
2import { fixtureState } from "./fixtures.ts";
3
4/** FH-039 contract fixtures only; no core or owner account is contacted. */
5export const reviewScenarios = [
6  "review-current",
7  "review-approved",
8  "review-promoted",
9  "review-returned",
10  "review-stale",
11  "review-main-moved",
12  "review-main-moved-stale",
13  "review-out-of-date",
14  "review-reconciler",
15  "review-reconciler-stale",
16  "review-pending-promotion",
17  "review-pending-promotion-stale",
18  "review-advisory",
19  "review-changes",
20  "review-error",
21  "review-loading",
22  "review-not-found",
23  "review-conflict",
24];
25export function reviewFixtureState(id: string): StateResponse {
26  const state = fixtureState("receipts");
27  state.project.id = id;
28  state.reviews = [state.reviews[2]];
29  const r = state.reviews[0];
30  r.id = "review-1";
31  r.packet = "FH-040";
32  r.returned = null;
33  r.approval = null;
34  state.project.settings = { requireReceipt: id !== "review-advisory" };
35  state.packets = [
36    {
37      id: r.packet,
38      title: "Review in a minute",
39      goal: "Understand the request and inspect each claim.\nKeep the evidence separate.",
40      paths: ["packages/forkhorse-dashboard/**"],
41      acceptance: [
42        "Owner and agent see the request.",
43        "Return records a reason.",
44        "Changed files open inline diffs.",
45      ],
46    },
47  ];
48  state.blockers = [];
49  if (id === "review-approved" || id === "review-promoted")
50    r.approval = { sha: r.sha, receiptDigest: r.receiptDigest };
51  if (id === "review-promoted") {
52    r.promoted = true;
53    state.ledger = [
54      {
55        id: "promotion-1",
56        review: r.id,
57        repoName: state.project.repoName,
58        ref: "refs/heads/main",
59        before: r.base,
60        after: r.sha,
61        receiptDigest: r.receiptDigest,
62        recordedAt: "2026-10-13T08:00:00Z",
63      },
64    ];
65  }
66  if (id === "review-returned")
67    r.returned = {
68      reason:
69        "Explain the changed behavior.\n<script>plain text</script>\nPreserve the acceptance evidence.",
70      at: "2026-10-13T08:00:00Z",
71    };
72  if (id === "review-stale") {
73    r.stale = true;
74    state.forks.find((f) => f.repoName === r.repoName)!.head = "9".repeat(40);
75  }
76  if (id === "review-out-of-date") r.stale = true;
77  if (/^review-(main-moved|reconciler|pending-promotion)/.test(id)) {
78    r.approval = { sha: r.sha, receiptDigest: r.receiptDigest };
79    r.stale = id.endsWith("-stale");
80    state.ledger = [{
81      id: "another-promotion", review: "another-review", repoName: state.project.repoName,
82      ref: "refs/heads/main", before: r.base, after: "7".repeat(40),
83      receiptDigest: null, recordedAt: "2026-10-13T08:00:00Z",
84    }];
85    if (id.startsWith("review-reconciler")) state.reconcilerAlerts = [{
86      id: "current-head", repoName: state.project.repoName, ref: "refs/heads/main",
87      before: "7".repeat(40), after: "8".repeat(40), reason: "Fixture main changed outside a promotion.",
88    }];
89    if (id.startsWith("review-pending-promotion")) state.promotionPending = state.ledger[0];
90  }
91  // Quick-check duration fixture; every receipt surface uses the same formatter.
92  if (r.receipt) {
93    r.receipt.durationMs = 227;
94    r.receipt.checks[0].durationMs = 227;
95  }
96  if (id === "review-advisory")
97    r.receipt = {
98      ...r.receipt!,
99      status: "failed",
100      checks: [{ ...r.receipt!.checks[0], outcome: "failed" }],
101    };
102  if (id === "review-changes") {
103    state.activity[0].changedPaths = Array.from({ length: 400 }, (_, i) => `src/path-${i}.ts`);
104    state.activity[0].changedPathCount = 400;
105  }
106  return state;
107}
108export function reviewFixtureDetail(state: StateResponse): ReviewDetail {
109  const review = state.reviews[0];
110  const large = state.project.id === "review-changes";
111  const extraCommits = large
112    ? Array.from({ length: 48 }, (_, i) => ({
113        sha: (i + 16).toString(16).padStart(40, "0"),
114        subject: `Earlier fixture change ${i + 1}`,
115        authorName: "fixture-agent",
116        packetTrailer: null,
117        agentTrailer: null,
118      }))
119    : [];
120  return {
121    review,
122    packet: state.packets[0],
123    changes: {
124      base: review.base,
125      head: review.sha,
126      fileCount: large ? 400 : 4,
127      truncated: large,
128      files: (
129        [
130          { path: "src/review.tsx", status: "modified" },
131          { path: "assets/image.bin", status: "added" },
132          { path: "docs/large.txt", status: "deleted" },
133          { path: "missing.txt", status: "modified" },
134          ...(large
135            ? Array.from({ length: 196 }, (_, i) => ({
136                path: `src/very/long/nested/fixture/path-${i}.ts`,
137                status: "modified" as const,
138              }))
139            : []),
140        ] satisfies ReviewDetail["changes"]["files"]
141      ).toSorted((a, b) => a.path.localeCompare(b.path)),
142    },
143    commits: [
144      {
145        sha: review.sha,
146        subject: "🐎 Add the review view",
147        authorName: "fixture-agent",
148        packetTrailer: "FH-040",
149        agentTrailer: "codex",
150      },
151      {
152        sha: "8".repeat(40),
153        subject: "Prepare review contract",
154        authorName: "fixture-agent",
155        packetTrailer: null,
156        agentTrailer: null,
157      },
158      ...extraCommits,
159    ],
160    commitsTruncated: large,
161    promotion: state.ledger.find((l) => l.review === review.id) ?? null,
162    // FH-048 preserves claimant-packet detail; pushing gates/history come from /state.
163    blockers: state.blockers.filter((blocker) => blocker.packet === review.packet),
164  };
165}
166export function reviewFixtureDiff(path: string): ReviewDiff {
167  return {
168    path,
169    status:
170      path === "assets/image.bin" ? "added" : path === "docs/large.txt" ? "deleted" : "modified",
171    binary: path === "assets/image.bin",
172    truncated: path === "docs/large.txt",
173    patch:
174      path === "docs/large.txt"
175        ? "@@ -1,2 +0,0 @@\n-old content\n-more removed content"
176        : "--- a/src/review.tsx\n+++ b/src/review.tsx\n@@ -1 +1 @@\n-previous value\n+<script>plain text only</script>\n context with a very long " +
177          "fixture ".repeat(40),
178  };
179}
180