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

gate-gatetest-availability.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.

gate-gatetest-availability.test.tsBlame84 lines · 1 contributor
6930df0ccanty labs1/**
2 * runGateTestScan availability handling.
3 *
4 * A GateTest outage or misconfiguration (non-2xx response, network error)
5 * must not hard-block merges platform-wide — that makes every merge
6 * hostage to a third-party service's uptime. Only an actual scan verdict
7 * (a 200 response with a pass/fail result) should block. This mirrors the
8 * "not configured" path, which already skips rather than fails.
9 *
10 * Regression coverage for the 2026-07-15 incident: gatetest.ai returned
11 * 503 (missing GLUECRON_EMITTER_SECRET on its side) and every merge on
12 * every repo failed with "GateTest returned 503: ...".
13 */
14
15import { afterEach, beforeEach, describe, expect, it } from "bun:test";
16import { runGateTestScan } from "../lib/gate";
17import { config } from "../lib/config";
18
19const origFetch = globalThis.fetch;
20const origUrl = process.env.GATETEST_URL;
21
22function mockFetch(responder: () => Response | Promise<Response>): void {
23 // @ts-expect-error — override global fetch for the test
24 globalThis.fetch = async (): Promise<Response> => responder();
25}
26
27beforeEach(() => {
28 process.env.GATETEST_URL = "https://gatetest.example/api/events/push";
29});
30
31afterEach(() => {
32 globalThis.fetch = origFetch;
33 if (origUrl === undefined) delete process.env.GATETEST_URL;
34 else process.env.GATETEST_URL = origUrl;
35});
36
37describe("runGateTestScan — availability vs verdict", () => {
38 it("skips (does not block) when GateTest is not configured", async () => {
39 delete process.env.GATETEST_URL;
40 const result = await runGateTestScan("o", "r", "refs/heads/main", "a".repeat(40));
41 expect(result.skipped).toBe(true);
42 expect(result.passed).toBe(true);
43 });
44
45 it("skips (does not block) on a non-2xx response — e.g. GateTest itself is down", async () => {
46 mockFetch(() => new Response(JSON.stringify({ error: "GLUECRON_EMITTER_SECRET is not set" }), { status: 503 }));
47 const result = await runGateTestScan("o", "r", "refs/heads/main", "a".repeat(40));
48 expect(result.skipped).toBe(true);
49 expect(result.passed).toBe(true);
50 expect(result.details).toContain("503");
51 });
52
53 it("skips (does not block) when the request throws — network error / timeout", async () => {
54 mockFetch(() => {
55 throw new Error("ECONNREFUSED");
56 });
57 const result = await runGateTestScan("o", "r", "refs/heads/main", "a".repeat(40));
58 expect(result.skipped).toBe(true);
59 expect(result.passed).toBe(true);
60 expect(result.details).toContain("ECONNREFUSED");
61 });
62
63 it("blocks on a genuine 200 scan-fail verdict", async () => {
64 mockFetch(() => new Response(JSON.stringify({ passed: false, summary: "2 vulnerabilities found" }), { status: 200 }));
65 const result = await runGateTestScan("o", "r", "refs/heads/main", "a".repeat(40));
66 expect(result.skipped).toBeFalsy();
67 expect(result.passed).toBe(false);
68 expect(result.details).toBe("2 vulnerabilities found");
69 });
70
71 it("passes on a genuine 200 scan-pass verdict", async () => {
72 mockFetch(() => new Response(JSON.stringify({ passed: true, summary: "All checks passed" }), { status: 200 }));
73 const result = await runGateTestScan("o", "r", "refs/heads/main", "a".repeat(40));
74 expect(result.skipped).toBeFalsy();
75 expect(result.passed).toBe(true);
76 });
77});
78
79// Keep config import referenced — sanity that config.gatetestUrl reflects env for this test file.
80describe("config.gatetestUrl reflects GATETEST_URL for this suite", () => {
81 it("resolves to the mocked value while set", () => {
82 expect(config.gatetestUrl).toBe("https://gatetest.example/api/events/push");
83 });
84});