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

intelligence.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.

intelligence.test.tsBlame209 lines · 1 contributor
2c34075Claude1import { describe, it, expect, beforeAll, afterAll } from "bun:test";
2import { join } from "path";
3import { rm, mkdir } from "fs/promises";
4import { initBareRepo, getRepoPath } from "../git/repository";
5import {
6 computeHealthScore,
7 detectCIConfig,
8 analyzePush,
9} from "../lib/intelligence";
10import { autoRepair } from "../lib/autorepair";
11
12const TEST_REPOS = join(import.meta.dir, "../../.test-repos-intel-" + Date.now());
13
14beforeAll(async () => {
15 await rm(TEST_REPOS, { recursive: true, force: true });
16 await mkdir(TEST_REPOS, { recursive: true });
17 process.env.GIT_REPOS_PATH = TEST_REPOS;
18
19 // Create repo with various files
20 await initBareRepo("dev", "myapp");
21 const cloneDir = join(TEST_REPOS, "_clone");
22 const workDir = join(cloneDir, "work");
23
24 const run = async (cmd: string[], cwd: string) => {
25 const proc = Bun.spawn(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
26 await proc.exited;
27 };
28
29 await run(["git", "clone", getRepoPath("dev", "myapp"), workDir], TEST_REPOS);
30 await run(["git", "config", "user.email", "dev@test.com"], workDir);
31 await run(["git", "config", "user.name", "Dev"], workDir);
32
33 await mkdir(join(workDir, "src"), { recursive: true });
34 await mkdir(join(workDir, "src/__tests__"), { recursive: true });
35
36 // package.json
37 await Bun.write(
38 join(workDir, "package.json"),
39 JSON.stringify(
40 {
41 name: "myapp",
42 version: "1.0.0",
43 scripts: { test: "bun test", lint: "eslint .", build: "tsc" },
44 dependencies: { hono: "^4.0.0" },
45 devDependencies: { typescript: "^5.0.0", "@types/bun": "^1.0.0" },
46 },
47 null,
48 2
49 )
50 );
51
52 // tsconfig
53 await Bun.write(
54 join(workDir, "tsconfig.json"),
55 JSON.stringify({ compilerOptions: { strict: true } }, null, 2)
56 );
57
58 // README
59 await Bun.write(join(workDir, "README.md"), "# My App\n\nA test project.\n");
60
61 // Source files
62 await Bun.write(
63 join(workDir, "src/index.ts"),
64 'const greeting = "hello world";\nconsole.log(greeting);\n'
65 );
66 await Bun.write(
67 join(workDir, "src/util.ts"),
68 'export function add(a: number, b: number): number {\n return a + b;\n}\n'
69 );
70
71 // Test file
72 await Bun.write(
73 join(workDir, "src/__tests__/util.test.ts"),
74 'import { expect, test } from "bun:test";\nimport { add } from "../util";\n\ntest("add", () => {\n expect(add(1, 2)).toBe(3);\n});\n'
75 );
76
77 // Trailing whitespace in a file (for auto-repair test)
78 await Bun.write(
79 join(workDir, "src/messy.ts"),
80 'const x = 1; \nconst y = 2;\t\t\n// no newline at end'
81 );
82
83 // bun.lock (just a dummy)
84 await Bun.write(join(workDir, "bun.lock"), "{}");
85
86 // .gitignore with some entries
87 await Bun.write(join(workDir, ".gitignore"), "node_modules/\ndist/\n");
88
89 await run(["git", "add", "-A"], workDir);
90 await run(["git", "commit", "-m", "Initial commit"], workDir);
91 await run(["git", "branch", "-M", "main"], workDir);
92 await run(["git", "push", "-u", "origin", "main"], workDir);
93
94 await rm(cloneDir, { recursive: true, force: true });
95});
96
97afterAll(async () => {
98 await rm(TEST_REPOS, { recursive: true, force: true });
99});
100
101describe("health score", () => {
102 it("computes a health report", async () => {
103 const report = await computeHealthScore("dev", "myapp");
104
105 expect(report.score).toBeGreaterThan(0);
106 expect(report.score).toBeLessThanOrEqual(100);
107 expect(["A+", "A", "B", "C", "D", "F"]).toContain(report.grade);
108 expect(report.breakdown.security).toBeDefined();
109 expect(report.breakdown.testing).toBeDefined();
110 expect(report.breakdown.complexity).toBeDefined();
111 expect(report.breakdown.dependencies).toBeDefined();
112 expect(report.breakdown.documentation).toBeDefined();
113 expect(report.breakdown.activity).toBeDefined();
114 expect(report.insights.length).toBeGreaterThan(0);
115 });
116
117 it("detects tests exist", async () => {
118 const report = await computeHealthScore("dev", "myapp");
119 expect(report.breakdown.testing.hasTests).toBe(true);
120 expect(report.breakdown.testing.testFileCount).toBeGreaterThan(0);
121 });
122
123 it("detects README", async () => {
124 const report = await computeHealthScore("dev", "myapp");
125 expect(report.breakdown.documentation.hasReadme).toBe(true);
126 });
127
128 it("detects dependencies", async () => {
129 const report = await computeHealthScore("dev", "myapp");
130 expect(report.breakdown.dependencies.total).toBeGreaterThan(0);
131 expect(report.breakdown.dependencies.lockfileExists).toBe(true);
132 });
133
134 it("has at least 1 contributor", async () => {
135 const report = await computeHealthScore("dev", "myapp");
136 expect(report.breakdown.activity.uniqueContributors).toBeGreaterThanOrEqual(1);
137 });
138});
139
140describe("zero-config CI detection", () => {
141 it("detects Bun + TypeScript project", async () => {
142 const ci = await detectCIConfig("dev", "myapp", "main");
143
144 expect(ci.projectType).toBe("typescript");
145 expect(ci.runtime).toBe("bun");
146 expect(ci.detected).toContain("Bun project detected");
147 expect(ci.detected).toContain("TypeScript detected");
148 });
149
150 it("detects test, lint, and build commands", async () => {
151 const ci = await detectCIConfig("dev", "myapp", "main");
152
153 const names = ci.commands.map((c) => c.name);
154 expect(names).toContain("Test");
155 expect(names).toContain("Lint");
156 });
157
158 it("detects Hono framework", async () => {
159 const ci = await detectCIConfig("dev", "myapp", "main");
160 expect(ci.detected).toContain("Hono framework");
161 });
162});
163
164describe("auto-repair", () => {
165 it("fixes whitespace issues", async () => {
166 const result = await autoRepair("dev", "myapp", "main");
167
168 expect(result.repaired).toBe(true);
169 expect(result.repairs.length).toBeGreaterThan(0);
170
171 // Should have fixed trailing whitespace
172 const whitespaceRepairs = result.repairs.filter(
173 (r) => r.type === "whitespace"
174 );
175 expect(whitespaceRepairs.length).toBeGreaterThan(0);
176 });
177
178 it("adds missing .gitignore entries", async () => {
179 const result = await autoRepair("dev", "myapp", "main");
180
181 const gitignoreRepairs = result.repairs.filter(
182 (r) => r.type === "gitignore"
183 );
184 // May or may not need repair depending on current state
185 // (first run may have already fixed it)
186 expect(result.repaired).toBeDefined();
187 });
188
189 it("subsequent runs have fewer repairs", async () => {
190 // Run once to fix everything
191 const first = await autoRepair("dev", "myapp", "main");
192 // Run again — should have fewer or no repairs
193 const second = await autoRepair("dev", "myapp", "main");
194 expect(second.repairs.length).toBeLessThanOrEqual(first.repairs.length);
195 });
196});
197
198describe("health dashboard route", () => {
199 it("GET /:owner/:repo/health returns health page", async () => {
200 const app = (await import("../app")).default;
201 const res = await app.request("/dev/myapp/health");
202 expect(res.status).toBe(200);
203 const html = await res.text();
204 expect(html).toContain("Health Score");
205 expect(html).toContain("Security");
206 expect(html).toContain("Testing");
207 expect(html).toContain("Zero-Config CI");
208 });
209});