Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
Blame · Line-by-line history

editor-nested-paths.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.

editor-nested-paths.test.tsBlame88 lines · 1 contributor
4f4cb3accantynz-alt1/**
2 * The web editor must not destroy directory structure.
3 *
4 * Both editor write paths built their commit by piping `git ls-tree -r` into
5 * `git mktree`:
6 *
7 * ls-tree -r emits a FLAT, recursive listing whose names are full paths
8 * ("100644 blob <sha>\tsrc/lib/foo.ts")
9 * mktree builds ONE tree object and expects bare filenames for a
10 * single directory level
11 *
12 * So any repository with a subdirectory produced a rejected or malformed
13 * tree. Editing or creating a file in a nested repo silently lost the
14 * structure — data loss on a core feature, and invisible because the handler
15 * never checked mktree's exit status before running commit-tree.
16 *
17 * Both handlers now delegate to createOrUpdateFileOnBranch(), which uses
18 * read-tree + update-index --cacheinfo + write-tree against an isolated
19 * temporary index. That handles arbitrary nesting and cannot be stomped by a
20 * concurrent write.
21 *
22 * These are structural: a functional test needs a real bare repo with nested
23 * directories on disk, and git/repository.ts already owns that behaviour.
24 * What must not regress is that the editor no longer hand-rolls it.
25 */
26
27import { describe, expect, it } from "bun:test";
28import { readFileSync } from "fs";
29
30const SRC = readFileSync("src/routes/editor.tsx", "utf8");
31// Comments explain the old bug and necessarily contain the strings being
32// asserted absent — assert on code, not prose.
33const CODE = SRC.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
34
35describe("the editor no longer hand-rolls tree construction", () => {
36 it("calls git mktree nowhere", () => {
37 expect(CODE).not.toContain('["git", "mktree"]');
38 expect(CODE).not.toMatch(/"mktree"/);
39 });
40
41 it("does not feed a recursive ls-tree into a tree builder", () => {
42 // The specific combination that caused the loss.
43 expect(CODE).not.toMatch(/"ls-tree",\s*"-r"/);
44 });
45
46 it("does not hand-roll commit-tree either", () => {
47 // commit-tree on a malformed tree is how this stayed silent: the handler
48 // never checked mktree's exit code before committing.
49 expect(CODE).not.toContain("commit-tree");
50 });
51});
52
53describe("both write paths use the shared helper", () => {
54 it("create-file and edit-file both call createOrUpdateFileOnBranch", () => {
55 const calls = CODE.match(/createOrUpdateFileOnBranch\(\{/g) ?? [];
56 expect(calls.length).toBe(2);
57 });
58
59 it("both surface a write failure instead of redirecting to a broken blob", () => {
60 // Previously a failed write still redirected to the file view, which then
61 // 404'd or showed stale content — the user had no idea it had not saved.
62 const guards = CODE.match(/if \("error" in written\)/g) ?? [];
63 expect(guards.length).toBe(2);
64 });
65
66 it("distinguishes a concurrent edit from a write failure", () => {
67 expect(CODE).toContain('written.error === "sha-mismatch"');
68 });
69});
70
71describe("the helper it delegates to handles nesting", () => {
72 const REPO = readFileSync("src/git/repository.ts", "utf8");
73 const start = REPO.indexOf("export async function createOrUpdateFileOnBranch");
74 // To the next top-level export, not a fixed char count — 3000 chars stopped
75 // short of write-tree and the assertion failed on a correct implementation.
76 const nextExport = REPO.indexOf("\nexport ", start + 10);
77 const fn = REPO.slice(start, nextExport > -1 ? nextExport : REPO.length);
78
79 it("seeds an index from the parent tree rather than rebuilding one flat tree", () => {
80 expect(fn).toContain('"git", "read-tree"');
81 expect(fn).toContain("update-index");
82 expect(fn).toContain("write-tree");
83 });
84
85 it("uses an isolated index so concurrent writes cannot stomp each other", () => {
86 expect(fn).toContain("GIT_INDEX_FILE");
87 });
88});