1import type { PullRequestCheck, PullRequestCheckStatus } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3
4import { dedupeChecks } from "./pullRequestChecks.ts";
5
6function entry(
7 name: string,
8 status: PullRequestCheckStatus,
9 extra: { readonly workflowName?: string | null; readonly at?: string | null } = {},
10): {
11 readonly check: PullRequestCheck;
12 readonly workflowName: string | null;
13 readonly at: string | null;
14} {
15 return {
16 check: { name, status, description: null, url: null },
17 workflowName: extra.workflowName ?? null,
18 at: extra.at ?? null,
19 };
20}
21
22describe("dedupeChecks", () => {
23 it("keeps the newest run of a check the host listed twice", () => {
24 const checks = dedupeChecks([
25 entry("Prepare PR size config", "success", {
26 workflowName: "PR Size",
27 at: "2026-08-11T16:06:25Z",
28 }),
29 entry("Prepare PR size config", "pending", {
30 workflowName: "PR Size",
31 at: "2026-08-11T17:01:04Z",
32 }),
33 ]);
34
35 expect(checks.map((check) => [check.name, check.status])).toEqual([
36 ["Prepare PR size config", "pending"],
37 ]);
38 });
39
40 it("holds a check at the place it first appeared, so a re-run does not reshuffle the list", () => {
41 const checks = dedupeChecks([
42 entry("lint", "success", { workflowName: "CI", at: "2026-08-11T16:00:00Z" }),
43 entry("test", "success", { workflowName: "CI", at: "2026-08-11T16:00:00Z" }),
44 entry("lint", "failure", { workflowName: "CI", at: "2026-08-11T18:00:00Z" }),
45 ]);
46
47 expect(checks.map((check) => [check.name, check.status])).toEqual([
48 ["lint", "failure"],
49 ["test", "success"],
50 ]);
51 });
52
53 it("keeps distinct workflow and check pairs even when their names join to the same text", () => {
54 const checks = dedupeChecks([
55 entry("Linux tests", "success", {
56 workflowName: "Build",
57 at: "2026-08-11T16:00:00Z",
58 }),
59 entry("tests", "success", {
60 workflowName: "Build Linux",
61 at: "2026-08-11T17:00:00Z",
62 }),
63 entry("Linux tests", "failure", {
64 workflowName: "Build",
65 at: "2026-08-11T18:00:00Z",
66 }),
67 ]);
68
69 expect(checks.map((check) => [check.name, check.status])).toEqual([
70 ["Linux tests", "failure"],
71 ["tests", "success"],
72 ]);
73 });
74
75 it("loses a run that never said when it happened to one that did, whichever came first", () => {
76 const undated = dedupeChecks([
77 entry("build", "success", { at: "2026-08-11T16:00:00Z" }),
78 entry("build", "pending"),
79 ]);
80 const dated = dedupeChecks([
81 entry("build", "pending"),
82 entry("build", "success", { at: "2026-08-11T16:00:00Z" }),
83 ]);
84
85 expect([undated[0]?.status, dated[0]?.status]).toEqual(["success", "success"]);
86 });
87
88 it("takes the last copy when neither run is dated, which is how an update is listed", () => {
89 const checks = dedupeChecks([entry("build", "pending"), entry("build", "failure")]);
90
91 expect(checks.map((check) => check.status)).toEqual(["failure"]);
92 });
93
94 it("keeps two workflows that name a job the same thing, and says which is which", () => {
95 const checks = dedupeChecks([
96 entry("build", "success", { workflowName: "CI", at: "2026-08-11T16:00:00Z" }),
97 entry("build", "failure", { workflowName: "Release", at: "2026-08-11T16:00:00Z" }),
98 ]);
99
100 expect(checks.map((check) => [check.name, check.status])).toEqual([
101 ["CI / build", "success"],
102 ["Release / build", "failure"],
103 ]);
104 });
105
106 it("leaves a colliding check with no workflow of its own unqualified", () => {
107 // An app-provided check run belongs to no workflow, which GitHub reports as an empty name.
108 const checks = dedupeChecks([
109 entry("build", "success", { workflowName: "", at: "2026-08-11T16:00:00Z" }),
110 entry("build", "failure", { workflowName: "CI", at: "2026-08-11T16:00:00Z" }),
111 ]);
112
113 expect(checks.map((check) => check.name)).toEqual(["build", "CI / build"]);
114 });
115});