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
claude/adoring-hopper-5x74bqclaude/affectionate-feynman-ykrf1hclaude/architecture-audit-design-wxprenclaude/build-status-update-3MXsfclaude/charming-meitner-mllb5rclaude/compare-gate-gluecron-s4mFQclaude/confident-faraday-tikcwbclaude/continue-work-XMTlIclaude/crontech-gluecron-deploy-7MIECclaude/crontech-platform-setup-SeKfwclaude/design-2026claude/ecstatic-ptolemy-jMdigclaude/enhance-github-integration-QNHdGclaude/fix-aa-loop-issue-PonMQclaude/fix-actions-and-processclaude/fix-desktop-errors-XqoW8claude/fix-red-workflowsclaude/fix-website-access-6FKJNclaude/gatetest-integration-hardeningclaude/github-audit-improvements-bDFr9claude/gluecron-launch-status-FoMRlclaude/hopeful-lamport-olfCTclaude/issue-to-pr-and-protectionsclaude/jolly-heisenberg-2sg1Qclaude/launch-preparation-QmTb6claude/new-session-xk1l7claude/plan-platform-architecture-kkN4yclaude/platform-analysis-roadmap-1nUGLclaude/platform-launch-assessment-8dWV8claude/polish-platform-release-AeDrUclaude/resume-previous-work-KzyLwclaude/review-crontech-handoff-qYEVqclaude/review-project-completeness-lHhS2claude/review-readme-docs-ulqPKclaude/serene-edison-rj87weclaude/setup-multi-repo-dev-BCwNQclaude/ship-fixes-and-tests-Jvz1cclaude/site-audit-competitive-pctlwgclaude/site-migration-vercel-XstpKclaude/standalone-product-repos-XHFTDcopilot/feat-smart-empty-states-keyboard-first-enhancementcopilot/feat-smart-morning-digest-review-context-restorecopilot/fix-and-process-workflowscopilot/update-ai-powered-code-reviewfeat/debt-mapfeat/push-policy-codeowners-hardeningfeat/smart-digest-contextfeat/stage-impactfeat/t1-secret-migrationfeat/u-polishfeat/w-self-hostfeat/w2-claude-configfix/agent-journey-orphan-sweepgatetest/auto-fix-1776586424172gatetest/auto-fix-1776586534814gatetest/auto-fix-1776590685143gatetest/auto-fix-1776590808199mainops/redeploy-retriggerstyle/dxt-cta-themeworktree-agent-a3377aad30d55da26worktree-agent-a7ef607b7ee1d6c74
commit-statuses.test.ts5.1 KB · 159 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/**
 * Block J8 — Commit status API tests. Pure helpers + route-auth smokes.
 * DB-backed CRUD is covered in integration.
 */

import { describe, it, expect } from "bun:test";
import app from "../app";
import {
  STATUS_STATES,
  isValidSha,
  isValidState,
  reduceCombined,
  sanitiseContext,
  __internal,
} from "../lib/commit-statuses";

describe("commit-statuses — isValidSha", () => {
  it("accepts 4 to 40 hex chars", () => {
    expect(isValidSha("abcd")).toBe(true);
    expect(isValidSha("a".repeat(40))).toBe(true);
    expect(isValidSha("DEADBEEF")).toBe(true);
  });

  it("rejects too short / too long / bad chars / empty / null", () => {
    expect(isValidSha("abc")).toBe(false);
    expect(isValidSha("a".repeat(41))).toBe(false);
    expect(isValidSha("xyz1")).toBe(false);
    expect(isValidSha("")).toBe(false);
    expect(isValidSha(null)).toBe(false);
    expect(isValidSha(undefined)).toBe(false);
  });
});

describe("commit-statuses — isValidState", () => {
  it("accepts the four canonical states", () => {
    for (const s of STATUS_STATES) expect(isValidState(s)).toBe(true);
  });

  it("rejects anything else", () => {
    expect(isValidState("")).toBe(false);
    expect(isValidState("passed")).toBe(false);
    expect(isValidState("ok")).toBe(false);
    expect(isValidState(null)).toBe(false);
    expect(isValidState(42)).toBe(false);
    expect(isValidState(undefined)).toBe(false);
  });
});

describe("commit-statuses — sanitiseContext", () => {
  it("defaults to 'default' on empty / nullish", () => {
    expect(sanitiseContext("")).toBe("default");
    expect(sanitiseContext(null)).toBe("default");
    expect(sanitiseContext(undefined)).toBe("default");
    expect(sanitiseContext("   ")).toBe("default");
  });

  it("trims and caps length", () => {
    expect(sanitiseContext("  ci/build  ")).toBe("ci/build");
    const long = "x".repeat(500);
    expect(sanitiseContext(long).length).toBe(__internal.CONTEXT_MAX);
  });
});

describe("commit-statuses — reduceCombined", () => {
  it("empty list rolls up as success (no blockers)", () => {
    expect(reduceCombined([])).toBe("success");
  });

  it("any failure dominates", () => {
    expect(reduceCombined(["success", "failure"])).toBe("failure");
    expect(reduceCombined(["pending", "failure"])).toBe("failure");
  });

  it("any error dominates (bucketed as failure)", () => {
    expect(reduceCombined(["success", "error"])).toBe("failure");
    expect(reduceCombined(["pending", "error"])).toBe("failure");
  });

  it("no failure + any pending → pending", () => {
    expect(reduceCombined(["pending"])).toBe("pending");
    expect(reduceCombined(["success", "pending", "success"])).toBe("pending");
  });

  it("all success → success", () => {
    expect(reduceCombined(["success", "success", "success"])).toBe("success");
  });
});

describe("commit-statuses — __internal.clamp", () => {
  it("returns null for empty / nullish", () => {
    expect(__internal.clamp("", 10)).toBeNull();
    expect(__internal.clamp(null, 10)).toBeNull();
    expect(__internal.clamp(undefined, 10)).toBeNull();
  });

  it("caps at max", () => {
    expect(__internal.clamp("abcdef", 3)).toBe("abc");
    expect(__internal.clamp("ok", 10)).toBe("ok");
  });
});

// ---------------------------------------------------------------------------
// Route auth — these run without a DB so we only assert unauthenticated
// behaviour. Authenticated paths (owner check, actual upsert) belong in
// integration tests against a live DB.
// ---------------------------------------------------------------------------

describe("commit-statuses — route auth", () => {
  it("POST without auth → 302 /login redirect", async () => {
    const res = await app.request(
      "/api/v1/repos/alice/repo/statuses/abc1234",
      {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ state: "success" }),
      }
    );
    expect(res.status).toBe(302);
    expect(res.headers.get("location") || "").toContain("/login");
  });

  it("POST with invalid bearer token → 401 JSON", async () => {
    const res = await app.request(
      "/api/v1/repos/alice/repo/statuses/abc1234",
      {
        method: "POST",
        headers: {
          "content-type": "application/json",
          authorization: "Bearer glc_not_a_real_token",
        },
        body: JSON.stringify({ state: "success" }),
      }
    );
    expect(res.status).toBe(401);
  });

  it("GET list returns JSON (200 / 404 / 500 depending on env)", async () => {
    const res = await app.request(
      "/api/v1/repos/alice/repo/commits/abc1234/statuses"
    );
    expect([200, 404, 500]).toContain(res.status);
  });

  it("GET combined status returns JSON", async () => {
    const res = await app.request(
      "/api/v1/repos/alice/repo/commits/abc1234/status"
    );
    expect([200, 404, 500]).toContain(res.status);
  });

  it("GET with obviously invalid sha → 400", async () => {
    const res = await app.request(
      "/api/v1/repos/alice/repo/commits/NOT_HEX/status"
    );
    expect(res.status).toBe(400);
    const body = await res.json();
    expect(body.error).toContain("sha");
  });
});