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

branch-protection.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.

branch-protection.test.tsBlame140 lines · 1 contributor
1e162a8Claude1/**
2 * Block D5 — branch-protection enforcement unit tests.
3 * Covers `evaluateProtection` (pure) with various rule shapes + contexts.
4 */
5
6import { describe, expect, test } from "bun:test";
7import { evaluateProtection } from "../lib/branch-protection";
8import type { BranchProtection } from "../db/schema";
9
10function rule(overrides: Partial<BranchProtection>): BranchProtection {
11 return {
12 id: "id",
13 repositoryId: "repo",
14 pattern: "main",
15 requirePullRequest: true,
16 requireGreenGates: false,
17 requireAiApproval: false,
18 requireHumanReview: false,
19 requiredApprovals: 0,
20 allowForcePush: false,
21 allowDeletion: false,
22 dismissStaleReviews: true,
23 createdAt: new Date(),
24 updatedAt: new Date(),
25 ...overrides,
26 } as BranchProtection;
27}
28
29describe("evaluateProtection", () => {
30 test("no rule → allowed with no reasons", () => {
31 const r = evaluateProtection(null, {
32 aiApproved: false,
33 humanApprovalCount: 0,
34 gateResultGreen: false,
35 hasFailedGates: true,
36 });
37 expect(r.allowed).toBe(true);
38 expect(r.reasons).toEqual([]);
39 });
40
41 test("requireAiApproval blocks when not approved", () => {
42 const r = evaluateProtection(
43 rule({ requireAiApproval: true }),
44 {
45 aiApproved: false,
46 humanApprovalCount: 0,
47 gateResultGreen: true,
48 hasFailedGates: false,
49 }
50 );
51 expect(r.allowed).toBe(false);
52 expect(r.reasons[0]).toMatch(/AI approval/i);
53 });
54
55 test("requireAiApproval allows when approved", () => {
56 const r = evaluateProtection(
57 rule({ requireAiApproval: true }),
58 {
59 aiApproved: true,
60 humanApprovalCount: 0,
61 gateResultGreen: true,
62 hasFailedGates: false,
63 }
64 );
65 expect(r.allowed).toBe(true);
66 });
67
68 test("requireGreenGates blocks when failing", () => {
69 const r = evaluateProtection(
70 rule({ requireGreenGates: true }),
71 {
72 aiApproved: true,
73 humanApprovalCount: 1,
74 gateResultGreen: false,
75 hasFailedGates: true,
76 }
77 );
78 expect(r.allowed).toBe(false);
79 expect(r.reasons.some((x) => /green gates/i.test(x))).toBe(true);
80 });
81
82 test("requireHumanReview blocks when 0 approvals", () => {
83 const r = evaluateProtection(
84 rule({ requireHumanReview: true }),
85 {
86 aiApproved: true,
87 humanApprovalCount: 0,
88 gateResultGreen: true,
89 hasFailedGates: false,
90 }
91 );
92 expect(r.allowed).toBe(false);
93 expect(r.reasons[0]).toMatch(/human review/i);
94 });
95
96 test("requiredApprovals=2 blocks when only 1", () => {
97 const r = evaluateProtection(
98 rule({ requiredApprovals: 2 }),
99 {
100 aiApproved: true,
101 humanApprovalCount: 1,
102 gateResultGreen: true,
103 hasFailedGates: false,
104 }
105 );
106 expect(r.allowed).toBe(false);
107 expect(r.reasons[0]).toMatch(/2 approvals/i);
108 });
109
110 test("requiredApprovals=2 allows when 2 reached", () => {
111 const r = evaluateProtection(
112 rule({ requiredApprovals: 2 }),
113 {
114 aiApproved: true,
115 humanApprovalCount: 2,
116 gateResultGreen: true,
117 hasFailedGates: false,
118 }
119 );
120 expect(r.allowed).toBe(true);
121 });
122
123 test("multiple rules combine into multiple reasons", () => {
124 const r = evaluateProtection(
125 rule({
126 requireAiApproval: true,
127 requireGreenGates: true,
128 requireHumanReview: true,
129 }),
130 {
131 aiApproved: false,
132 humanApprovalCount: 0,
133 gateResultGreen: false,
134 hasFailedGates: true,
135 }
136 );
137 expect(r.allowed).toBe(false);
138 expect(r.reasons.length).toBeGreaterThanOrEqual(3);
139 });
140});