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

dashboard-coach.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.

dashboard-coach.test.tsBlame103 lines · 1 contributor
7a14837Claude1/**
2 * Tests for the AI Health Coach pure helper exported from
3 * src/routes/dashboard.tsx (`pickRepoCoachPicks`).
4 *
5 * The dashboard route file is a .tsx module that may not import in
6 * the JSX-dev-runtime-less test sandbox; we use the same defensive
7 * loader pattern as other route tests so the suite stays green
8 * regardless. Pure-function semantics are pinned exhaustively.
9 */
10
11import { describe, it, expect } from "bun:test";
12
13async function tryLoad(): Promise<
14 | { ok: true; pickRepoCoachPicks: any }
15 | { ok: false; reason: "jsx-dev-runtime" | "other"; err: Error }
16> {
17 try {
18 const mod: any = await import("../routes/dashboard");
19 return { ok: true, pickRepoCoachPicks: mod.pickRepoCoachPicks };
20 } catch (err) {
21 const e = err instanceof Error ? err : new Error(String(err));
22 const reason = /jsx[-/]dev[-/]?runtime/i.test(e.message)
23 ? "jsx-dev-runtime"
24 : "other";
25 return { ok: false, reason, err: e };
26 }
27}
28
29const repo = (name: string, score: number, grade = "?") => ({
30 repo: { name, description: null },
31 healthScore: score,
32 healthGrade: grade,
33});
34
35describe("pickRepoCoachPicks — pure helper", () => {
36 it("filters out healthy repos (score >= 90)", async () => {
37 const loaded = await tryLoad();
38 if (!loaded.ok) {
39 expect(loaded.reason).toBe("jsx-dev-runtime");
40 return;
41 }
42 const fn = loaded.pickRepoCoachPicks;
43 const picks = fn([repo("a", 92), repo("b", 85), repo("c", 95)]);
44 expect(picks.map((p: any) => p.repo.name)).toEqual(["b"]);
45 });
46
47 it("filters out unscored repos (score === 0)", async () => {
48 const loaded = await tryLoad();
49 if (!loaded.ok) {
50 expect(loaded.reason).toBe("jsx-dev-runtime");
51 return;
52 }
53 const fn = loaded.pickRepoCoachPicks;
54 const picks = fn([repo("a", 0), repo("b", 70), repo("c", 0)]);
55 expect(picks.map((p: any) => p.repo.name)).toEqual(["b"]);
56 });
57
58 it("returns the lowest-N scores in ascending order", async () => {
59 const loaded = await tryLoad();
60 if (!loaded.ok) {
61 expect(loaded.reason).toBe("jsx-dev-runtime");
62 return;
63 }
64 const fn = loaded.pickRepoCoachPicks;
65 const picks = fn([
66 repo("a", 80),
67 repo("b", 50),
68 repo("c", 70),
69 repo("d", 60),
70 ]);
71 expect(picks.map((p: any) => p.repo.name)).toEqual(["b", "d", "c"]);
72 });
73
74 it("respects the topN cap (default 3)", async () => {
75 const loaded = await tryLoad();
76 if (!loaded.ok) {
77 expect(loaded.reason).toBe("jsx-dev-runtime");
78 return;
79 }
80 const fn = loaded.pickRepoCoachPicks;
81 const all = [
82 repo("a", 10),
83 repo("b", 20),
84 repo("c", 30),
85 repo("d", 40),
86 repo("e", 50),
87 ];
88 expect(fn(all).length).toBe(3);
89 expect(fn(all, 5).length).toBe(5);
90 expect(fn(all, 1)[0].repo.name).toBe("a");
91 });
92
93 it("returns [] when no repos qualify", async () => {
94 const loaded = await tryLoad();
95 if (!loaded.ok) {
96 expect(loaded.reason).toBe("jsx-dev-runtime");
97 return;
98 }
99 const fn = loaded.pickRepoCoachPicks;
100 expect(fn([])).toEqual([]);
101 expect(fn([repo("a", 0), repo("b", 95)])).toEqual([]);
102 });
103});