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

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

week3.test.tsBlame174 lines · 1 contributor
79136bbClaude1import { describe, it, expect, beforeAll, afterAll } from "bun:test";
2import { join } from "path";
3import { rm, mkdir } from "fs/promises";
4import app from "../app";
5import { initBareRepo, getRepoPath } from "../git/repository";
6import { renderMarkdown } from "../lib/markdown";
7
8const TEST_REPOS = join(import.meta.dir, "../../.test-repos-w3-" + Date.now());
9
10beforeAll(async () => {
11 await rm(TEST_REPOS, { recursive: true, force: true });
12 await mkdir(TEST_REPOS, { recursive: true });
13 process.env.GIT_REPOS_PATH = TEST_REPOS;
14
15 await initBareRepo("alice", "project");
16 const cloneDir = join(TEST_REPOS, "_clone");
17 await mkdir(cloneDir, { recursive: true });
18 const repoPath = getRepoPath("alice", "project");
19 const workDir = join(cloneDir, "work");
20
21 const run = async (cmd: string[], cwd: string) => {
22 const proc = Bun.spawn(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
23 await proc.exited;
24 };
25
26 await run(["git", "clone", repoPath, workDir], TEST_REPOS);
27 await run(["git", "config", "user.email", "alice@test.com"], workDir);
28 await run(["git", "config", "user.name", "Alice"], workDir);
29
30 await mkdir(join(workDir, "src"), { recursive: true });
31 await Bun.write(
32 join(workDir, "README.md"),
33 "# My Project\n\nThis is a **bold** statement.\n\n- Item 1\n- Item 2\n\n```ts\nconst x = 1;\n```"
34 );
35 await Bun.write(
36 join(workDir, "src/main.ts"),
37 'function hello(name: string) {\n console.log(`Hello, ${name}!`);\n}\nhello("world");'
38 );
39 await Bun.write(join(workDir, "data.json"), '{"key": "value"}');
40
41 await run(["git", "add", "-A"], workDir);
42 await run(["git", "commit", "-m", "Initial commit"], workDir);
43 await run(["git", "branch", "-M", "main"], workDir);
44 await run(["git", "push", "-u", "origin", "main"], workDir);
45
46 // Second branch
47 await run(["git", "checkout", "-b", "dev"], workDir);
48 await Bun.write(join(workDir, "src/new.ts"), "export const y = 2;");
49 await run(["git", "add", "-A"], workDir);
50 await run(["git", "commit", "-m", "Add new module"], workDir);
51 await run(["git", "push", "-u", "origin", "dev"], workDir);
52
53 await rm(cloneDir, { recursive: true, force: true });
54});
55
56afterAll(async () => {
57 await rm(TEST_REPOS, { recursive: true, force: true });
58});
59
60describe("markdown rendering", () => {
61 it("should render bold text", () => {
62 const html = renderMarkdown("This is **bold**.");
63 expect(html).toContain("<strong>bold</strong>");
64 });
65
66 it("should render code blocks with highlighting", () => {
67 const html = renderMarkdown("```ts\nconst x = 1;\n```");
68 expect(html).toContain("<pre>");
69 expect(html).toContain("hljs-");
70 });
71
72 it("should render lists", () => {
73 const html = renderMarkdown("- one\n- two");
74 expect(html).toContain("<li>");
75 });
76
77 it("should sanitize dangerous links", () => {
78 const html = renderMarkdown("[click](javascript:alert(1))");
79 expect(html).not.toContain("javascript:");
80 });
81
82 it("README renders as markdown in repo view", async () => {
83 const res = await app.request("/alice/project");
84 expect(res.status).toBe(200);
85 const html = await res.text();
86 expect(html).toContain("<strong>bold</strong>");
87 expect(html).toContain("markdown-body");
88 });
89});
90
91describe("raw file download", () => {
92 it("GET /:owner/:repo/raw/:ref/:path returns file content", async () => {
93 const res = await app.request("/alice/project/raw/main/data.json");
94 expect(res.status).toBe(200);
95 expect(res.headers.get("content-type")).toContain("application/octet-stream");
96 const text = await res.text();
97 expect(text).toContain('"key"');
98 });
99
100 it("returns 404 for missing file", async () => {
101 const res = await app.request("/alice/project/raw/main/nope.txt");
102 expect(res.status).toBe(404);
103 });
104});
105
106describe("blame view", () => {
107 it("GET /:owner/:repo/blame/:ref/:path shows blame", async () => {
108 const res = await app.request("/alice/project/blame/main/src/main.ts");
109 expect(res.status).toBe(200);
110 const html = await res.text();
111 expect(html).toContain("blame");
112 expect(html).toContain("Alice");
113 expect(html).toContain("hello");
114 });
115
116 it("returns 404 for missing file", async () => {
117 const res = await app.request("/alice/project/blame/main/nope.ts");
118 expect(res.status).toBe(404);
119 });
120});
121
122describe("code search", () => {
123 it("GET /:owner/:repo/search?q=... returns results", async () => {
124 const res = await app.request("/alice/project/search?q=hello");
125 expect(res.status).toBe(200);
126 const html = await res.text();
127 expect(html).toContain("result");
128 expect(html).toContain("main.ts");
129 });
130
131 it("empty query shows no results", async () => {
132 const res = await app.request("/alice/project/search");
133 expect(res.status).toBe(200);
134 const html = await res.text();
135 expect(html).toContain("Search");
136 });
137});
138
139describe("compare view", () => {
140 it("GET /:owner/:repo/compare shows picker", async () => {
141 const res = await app.request("/alice/project/compare");
142 expect(res.status).toBe(200);
143 const html = await res.text();
144 expect(html).toContain("Compare");
145 expect(html).toContain("main");
146 expect(html).toContain("dev");
147 });
148
149 it("GET /:owner/:repo/compare/:base...:head shows diff", async () => {
150 const res = await app.request("/alice/project/compare/main...dev");
151 expect(res.status).toBe(200);
152 const html = await res.text();
153 expect(html).toContain("new.ts");
154 expect(html).toContain("Add new module");
155 });
156});
157
158describe("blob view links", () => {
159 it("blob view contains Raw and Blame links", async () => {
160 const res = await app.request("/alice/project/blob/main/src/main.ts");
161 expect(res.status).toBe(200);
162 const html = await res.text();
163 expect(html).toContain("/alice/project/raw/main/src/main.ts");
164 expect(html).toContain("/alice/project/blame/main/src/main.ts");
165 });
166});
167
168describe("error handling", () => {
169 it("404 for unknown routes returns proper page", async () => {
170 const res = await app.request("/alice/project/unknown-route-xyz");
171 // This might hit the repo page or 404 depending on routing
172 expect([200, 404]).toContain(res.status);
173 });
174});