Blame · Line-by-line history
import-empty-guard.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.
| 9bc1aed | 1 | /** |
| 2 | * Regression coverage for the silent-empty-import bug: a `git clone --bare | |
| 3 | * --mirror` of an empty / unreachable / token-less-private source exits 0 yet | |
| 4 | * produces a repo with zero commits, which the importer used to register as a | |
| 5 | * successful import (DB row + "success"). A Vapron import "succeeded" this way | |
| 6 | * while transferring nothing — dangerous once the GitHub source is deleted. | |
| 7 | * | |
| 8 | * countRepoCommits() is the gate that makes an import trustworthy: it must | |
| 9 | * return 0 for an empty bare repo and > 0 once history exists. | |
| 10 | */ | |
| 11 | import { describe, it, expect, beforeAll, afterAll } from "bun:test"; | |
| 12 | import { mkdtempSync, rmSync } from "node:fs"; | |
| 13 | import { tmpdir } from "node:os"; | |
| 14 | import { join } from "node:path"; | |
| 15 | import { countRepoCommits } from "../lib/import-helper"; | |
| 16 | ||
| 17 | function git(cwd: string, ...args: string[]) { | |
| 18 | const p = Bun.spawnSync(["git", ...args], { cwd, stdout: "pipe", stderr: "pipe" }); | |
| 19 | if (p.exitCode !== 0) { | |
| 20 | throw new Error(`git ${args.join(" ")} failed: ${new TextDecoder().decode(p.stderr)}`); | |
| 21 | } | |
| 22 | } | |
| 23 | ||
| 24 | let root = ""; | |
| 25 | beforeAll(() => { | |
| 26 | root = mkdtempSync(join(tmpdir(), "import-empty-guard-")); | |
| 27 | }); | |
| 28 | afterAll(() => { | |
| 29 | if (root) rmSync(root, { recursive: true, force: true }); | |
| 30 | }); | |
| 31 | ||
| 32 | describe("countRepoCommits — the import empty-guard", () => { | |
| 33 | it("returns 0 for a freshly-init'd EMPTY bare repo (the phantom-import case)", async () => { | |
| 34 | const empty = join(root, "empty.git"); | |
| 35 | git(root, "init", "--bare", empty); | |
| 36 | expect(await countRepoCommits(empty)).toBe(0); | |
| 37 | }); | |
| 38 | ||
| 39 | it("returns 0 for a path that isn't a git repo at all", async () => { | |
| 40 | expect(await countRepoCommits(join(root, "does-not-exist"))).toBe(0); | |
| 41 | }); | |
| 42 | ||
| 43 | it("returns > 0 once the repo actually has history", async () => { | |
| 44 | // Build a work repo with one commit, then mirror-clone it bare (exactly | |
| 45 | // what the importer does) and confirm the guard sees the history. | |
| 46 | const work = join(root, "work"); | |
| 47 | git(root, "init", "-b", "main", work); | |
| 48 | git(work, "config", "user.email", "t@t.com"); | |
| 49 | git(work, "config", "user.name", "t"); | |
| 50 | Bun.spawnSync(["git", "-C", work, "commit", "--allow-empty", "-m", "seed"], { stdout: "pipe", stderr: "pipe" }); | |
| 51 | ||
| 52 | const mirror = join(root, "mirror.git"); | |
| 53 | git(root, "clone", "--bare", "--mirror", work, mirror); | |
| 54 | expect(await countRepoCommits(mirror)).toBeGreaterThan(0); | |
| 55 | }); | |
| 56 | }); |