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
claude/adoring-hopper-5x74bqclaude/affectionate-feynman-ykrf1hclaude/architecture-audit-design-wxprenclaude/build-status-update-3MXsfclaude/charming-meitner-mllb5rclaude/compare-gate-gluecron-s4mFQclaude/confident-faraday-tikcwbclaude/continue-work-XMTlIclaude/crontech-gluecron-deploy-7MIECclaude/crontech-platform-setup-SeKfwclaude/design-2026claude/ecstatic-ptolemy-jMdigclaude/enhance-github-integration-QNHdGclaude/fix-aa-loop-issue-PonMQclaude/fix-actions-and-processclaude/fix-desktop-errors-XqoW8claude/fix-red-workflowsclaude/fix-website-access-6FKJNclaude/gatetest-integration-hardeningclaude/github-audit-improvements-bDFr9claude/gluecron-launch-status-FoMRlclaude/hopeful-lamport-olfCTclaude/issue-to-pr-and-protectionsclaude/jolly-heisenberg-2sg1Qclaude/launch-preparation-QmTb6claude/new-session-xk1l7claude/plan-platform-architecture-kkN4yclaude/platform-analysis-roadmap-1nUGLclaude/platform-launch-assessment-8dWV8claude/polish-platform-release-AeDrUclaude/resume-previous-work-KzyLwclaude/review-crontech-handoff-qYEVqclaude/review-project-completeness-lHhS2claude/review-readme-docs-ulqPKclaude/serene-edison-rj87weclaude/setup-multi-repo-dev-BCwNQclaude/ship-fixes-and-tests-Jvz1cclaude/site-audit-competitive-pctlwgclaude/site-migration-vercel-XstpKclaude/standalone-product-repos-XHFTDcopilot/feat-smart-empty-states-keyboard-first-enhancementcopilot/feat-smart-morning-digest-review-context-restorecopilot/fix-and-process-workflowscopilot/update-ai-powered-code-reviewfeat/debt-mapfeat/push-policy-codeowners-hardeningfeat/smart-digest-contextfeat/stage-impactfeat/t1-secret-migrationfeat/u-polishfeat/w-self-hostfeat/w2-claude-configfix/agent-journey-orphan-sweepgatetest/auto-fix-1776586424172gatetest/auto-fix-1776586534814gatetest/auto-fix-1776590685143gatetest/auto-fix-1776590808199mainops/redeploy-retriggerstyle/dxt-cta-themeworktree-agent-a3377aad30d55da26worktree-agent-a7ef607b7ee1d6c74
week3.test.ts6.0 KB · 174 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import { describe, it, expect, beforeAll, afterAll } from "bun:test";
import { join } from "path";
import { rm, mkdir } from "fs/promises";
import app from "../app";
import { initBareRepo, getRepoPath } from "../git/repository";
import { renderMarkdown } from "../lib/markdown";

const TEST_REPOS = join(import.meta.dir, "../../.test-repos-w3-" + Date.now());

beforeAll(async () => {
  await rm(TEST_REPOS, { recursive: true, force: true });
  await mkdir(TEST_REPOS, { recursive: true });
  process.env.GIT_REPOS_PATH = TEST_REPOS;

  await initBareRepo("alice", "project");
  const cloneDir = join(TEST_REPOS, "_clone");
  await mkdir(cloneDir, { recursive: true });
  const repoPath = getRepoPath("alice", "project");
  const workDir = join(cloneDir, "work");

  const run = async (cmd: string[], cwd: string) => {
    const proc = Bun.spawn(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
    await proc.exited;
  };

  await run(["git", "clone", repoPath, workDir], TEST_REPOS);
  await run(["git", "config", "user.email", "alice@test.com"], workDir);
  await run(["git", "config", "user.name", "Alice"], workDir);

  await mkdir(join(workDir, "src"), { recursive: true });
  await Bun.write(
    join(workDir, "README.md"),
    "# My Project\n\nThis is a **bold** statement.\n\n- Item 1\n- Item 2\n\n```ts\nconst x = 1;\n```"
  );
  await Bun.write(
    join(workDir, "src/main.ts"),
    'function hello(name: string) {\n  console.log(`Hello, ${name}!`);\n}\nhello("world");'
  );
  await Bun.write(join(workDir, "data.json"), '{"key": "value"}');

  await run(["git", "add", "-A"], workDir);
  await run(["git", "commit", "-m", "Initial commit"], workDir);
  await run(["git", "branch", "-M", "main"], workDir);
  await run(["git", "push", "-u", "origin", "main"], workDir);

  // Second branch
  await run(["git", "checkout", "-b", "dev"], workDir);
  await Bun.write(join(workDir, "src/new.ts"), "export const y = 2;");
  await run(["git", "add", "-A"], workDir);
  await run(["git", "commit", "-m", "Add new module"], workDir);
  await run(["git", "push", "-u", "origin", "dev"], workDir);

  await rm(cloneDir, { recursive: true, force: true });
});

afterAll(async () => {
  await rm(TEST_REPOS, { recursive: true, force: true });
});

describe("markdown rendering", () => {
  it("should render bold text", () => {
    const html = renderMarkdown("This is **bold**.");
    expect(html).toContain("<strong>bold</strong>");
  });

  it("should render code blocks with highlighting", () => {
    const html = renderMarkdown("```ts\nconst x = 1;\n```");
    expect(html).toContain("<pre>");
    expect(html).toContain("hljs-");
  });

  it("should render lists", () => {
    const html = renderMarkdown("- one\n- two");
    expect(html).toContain("<li>");
  });

  it("should sanitize dangerous links", () => {
    const html = renderMarkdown("[click](javascript:alert(1))");
    expect(html).not.toContain("javascript:");
  });

  it("README renders as markdown in repo view", async () => {
    const res = await app.request("/alice/project");
    expect(res.status).toBe(200);
    const html = await res.text();
    expect(html).toContain("<strong>bold</strong>");
    expect(html).toContain("markdown-body");
  });
});

describe("raw file download", () => {
  it("GET /:owner/:repo/raw/:ref/:path returns file content", async () => {
    const res = await app.request("/alice/project/raw/main/data.json");
    expect(res.status).toBe(200);
    expect(res.headers.get("content-type")).toContain("application/octet-stream");
    const text = await res.text();
    expect(text).toContain('"key"');
  });

  it("returns 404 for missing file", async () => {
    const res = await app.request("/alice/project/raw/main/nope.txt");
    expect(res.status).toBe(404);
  });
});

describe("blame view", () => {
  it("GET /:owner/:repo/blame/:ref/:path shows blame", async () => {
    const res = await app.request("/alice/project/blame/main/src/main.ts");
    expect(res.status).toBe(200);
    const html = await res.text();
    expect(html).toContain("blame");
    expect(html).toContain("Alice");
    expect(html).toContain("hello");
  });

  it("returns 404 for missing file", async () => {
    const res = await app.request("/alice/project/blame/main/nope.ts");
    expect(res.status).toBe(404);
  });
});

describe("code search", () => {
  it("GET /:owner/:repo/search?q=... returns results", async () => {
    const res = await app.request("/alice/project/search?q=hello");
    expect(res.status).toBe(200);
    const html = await res.text();
    expect(html).toContain("result");
    expect(html).toContain("main.ts");
  });

  it("empty query shows no results", async () => {
    const res = await app.request("/alice/project/search");
    expect(res.status).toBe(200);
    const html = await res.text();
    expect(html).toContain("Search");
  });
});

describe("compare view", () => {
  it("GET /:owner/:repo/compare shows picker", async () => {
    const res = await app.request("/alice/project/compare");
    expect(res.status).toBe(200);
    const html = await res.text();
    expect(html).toContain("Compare");
    expect(html).toContain("main");
    expect(html).toContain("dev");
  });

  it("GET /:owner/:repo/compare/:base...:head shows diff", async () => {
    const res = await app.request("/alice/project/compare/main...dev");
    expect(res.status).toBe(200);
    const html = await res.text();
    expect(html).toContain("new.ts");
    expect(html).toContain("Add new module");
  });
});

describe("blob view links", () => {
  it("blob view contains Raw and Blame links", async () => {
    const res = await app.request("/alice/project/blob/main/src/main.ts");
    expect(res.status).toBe(200);
    const html = await res.text();
    expect(html).toContain("/alice/project/raw/main/src/main.ts");
    expect(html).toContain("/alice/project/blame/main/src/main.ts");
  });
});

describe("error handling", () => {
  it("404 for unknown routes returns proper page", async () => {
    const res = await app.request("/alice/project/unknown-route-xyz");
    // This might hit the repo page or 404 depending on routing
    expect([200, 404]).toContain(res.status);
  });
});