Fork Horse repository, listed by its owner. Publisher snapshot.
packages/forkhorse-dashboard/test/review-fixtures.ts
Snapshot at 34e3175
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