Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

commit-statuses.test.ts

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

commit-statuses.test.tsBlame159 lines · 1 contributor
0cdfd89Claude1/**
2 * Block J8 — Commit status API tests. Pure helpers + route-auth smokes.
3 * DB-backed CRUD is covered in integration.
4 */
5
6import { describe, it, expect } from "bun:test";
7import app from "../app";
8import {
9 STATUS_STATES,
10 isValidSha,
11 isValidState,
12 reduceCombined,
13 sanitiseContext,
14 __internal,
15} from "../lib/commit-statuses";
16
17describe("commit-statuses — isValidSha", () => {
18 it("accepts 4 to 40 hex chars", () => {
19 expect(isValidSha("abcd")).toBe(true);
20 expect(isValidSha("a".repeat(40))).toBe(true);
21 expect(isValidSha("DEADBEEF")).toBe(true);
22 });
23
24 it("rejects too short / too long / bad chars / empty / null", () => {
25 expect(isValidSha("abc")).toBe(false);
26 expect(isValidSha("a".repeat(41))).toBe(false);
27 expect(isValidSha("xyz1")).toBe(false);
28 expect(isValidSha("")).toBe(false);
29 expect(isValidSha(null)).toBe(false);
30 expect(isValidSha(undefined)).toBe(false);
31 });
32});
33
34describe("commit-statuses — isValidState", () => {
35 it("accepts the four canonical states", () => {
36 for (const s of STATUS_STATES) expect(isValidState(s)).toBe(true);
37 });
38
39 it("rejects anything else", () => {
40 expect(isValidState("")).toBe(false);
41 expect(isValidState("passed")).toBe(false);
42 expect(isValidState("ok")).toBe(false);
43 expect(isValidState(null)).toBe(false);
44 expect(isValidState(42)).toBe(false);
45 expect(isValidState(undefined)).toBe(false);
46 });
47});
48
49describe("commit-statuses — sanitiseContext", () => {
50 it("defaults to 'default' on empty / nullish", () => {
51 expect(sanitiseContext("")).toBe("default");
52 expect(sanitiseContext(null)).toBe("default");
53 expect(sanitiseContext(undefined)).toBe("default");
54 expect(sanitiseContext(" ")).toBe("default");
55 });
56
57 it("trims and caps length", () => {
58 expect(sanitiseContext(" ci/build ")).toBe("ci/build");
59 const long = "x".repeat(500);
60 expect(sanitiseContext(long).length).toBe(__internal.CONTEXT_MAX);
61 });
62});
63
64describe("commit-statuses — reduceCombined", () => {
65 it("empty list rolls up as success (no blockers)", () => {
66 expect(reduceCombined([])).toBe("success");
67 });
68
69 it("any failure dominates", () => {
70 expect(reduceCombined(["success", "failure"])).toBe("failure");
71 expect(reduceCombined(["pending", "failure"])).toBe("failure");
72 });
73
74 it("any error dominates (bucketed as failure)", () => {
75 expect(reduceCombined(["success", "error"])).toBe("failure");
76 expect(reduceCombined(["pending", "error"])).toBe("failure");
77 });
78
79 it("no failure + any pending → pending", () => {
80 expect(reduceCombined(["pending"])).toBe("pending");
81 expect(reduceCombined(["success", "pending", "success"])).toBe("pending");
82 });
83
84 it("all success → success", () => {
85 expect(reduceCombined(["success", "success", "success"])).toBe("success");
86 });
87});
88
89describe("commit-statuses — __internal.clamp", () => {
90 it("returns null for empty / nullish", () => {
91 expect(__internal.clamp("", 10)).toBeNull();
92 expect(__internal.clamp(null, 10)).toBeNull();
93 expect(__internal.clamp(undefined, 10)).toBeNull();
94 });
95
96 it("caps at max", () => {
97 expect(__internal.clamp("abcdef", 3)).toBe("abc");
98 expect(__internal.clamp("ok", 10)).toBe("ok");
99 });
100});
101
102// ---------------------------------------------------------------------------
103// Route auth — these run without a DB so we only assert unauthenticated
104// behaviour. Authenticated paths (owner check, actual upsert) belong in
105// integration tests against a live DB.
106// ---------------------------------------------------------------------------
107
108describe("commit-statuses — route auth", () => {
109 it("POST without auth → 302 /login redirect", async () => {
110 const res = await app.request(
111 "/api/v1/repos/alice/repo/statuses/abc1234",
112 {
113 method: "POST",
114 headers: { "content-type": "application/json" },
115 body: JSON.stringify({ state: "success" }),
116 }
117 );
118 expect(res.status).toBe(302);
119 expect(res.headers.get("location") || "").toContain("/login");
120 });
121
122 it("POST with invalid bearer token → 401 JSON", async () => {
123 const res = await app.request(
124 "/api/v1/repos/alice/repo/statuses/abc1234",
125 {
126 method: "POST",
127 headers: {
128 "content-type": "application/json",
129 authorization: "Bearer glc_not_a_real_token",
130 },
131 body: JSON.stringify({ state: "success" }),
132 }
133 );
134 expect(res.status).toBe(401);
135 });
136
137 it("GET list returns JSON (200 / 404 / 500 depending on env)", async () => {
138 const res = await app.request(
139 "/api/v1/repos/alice/repo/commits/abc1234/statuses"
140 );
141 expect([200, 404, 500]).toContain(res.status);
142 });
143
144 it("GET combined status returns JSON", async () => {
145 const res = await app.request(
146 "/api/v1/repos/alice/repo/commits/abc1234/status"
147 );
148 expect([200, 404, 500]).toContain(res.status);
149 });
150
151 it("GET with obviously invalid sha → 400", async () => {
152 const res = await app.request(
153 "/api/v1/repos/alice/repo/commits/NOT_HEX/status"
154 );
155 expect(res.status).toBe(400);
156 const body = await res.json();
157 expect(body.error).toContain("sha");
158 });
159});