Files
oh-my-pi/packages/coding-agent/test/utils/git-clone.test.ts
T
oldschoola 14252e71cb fix: Windows test failures — path handling, EBUSY, SQLite handle leaks
Fix all Windows-specific test failures caused by path handling problems
and EBUSY errors from unclosed SQLite database handles.

Root causes fixed:
1. POSIX path assumptions: replaced hard-coded file:///tmp, /repo, etc.
   with pathToFileURL/path.resolve/path.join computed expectations
2. shortenPath() now normalizes backslashes to forward slashes after ~
   and respects home directory boundaries
3. HistoryStorage.resetInstance() leaked its Database — added #close()
   that finalizes all prepared statements and closes the DB
4. AgentStorage gained the same resetInstance()/#close() pattern
5. SqliteAuthCredentialStore.close() leaked one-off prepared statements
   from inline this.#db.prepare() calls — wrapped each in try/finally
6. model-cache.ts used a process-global DB even for custom dbPath —
   now opens/closes per-call via withModelCacheDb
7. createAgentSession leaked AuthStorage on construction failure —
   added ownsAuthStorage cleanup in catch block
8. MnemopiBackend.removeDbFiles() now truly best-effort (catches errors)
9. TempDir retry window expanded from 4x10ms to 40x25ms
10. TempDir prefix convention: non-@ prefixes created dirs relative to
    cwd instead of os.tmpdir() — all test temp dirs now use @ prefix
11. Shell-escaped interpolated paths in bash tool tests
12. git core.autocrlf false in autoresearch test repo init

All 522 previously-failing Windows tests now pass.
2026-06-18 21:32:38 -07:00

80 lines
2.8 KiB
TypeScript

import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import * as url from "node:url";
import * as git from "@oh-my-pi/pi-coding-agent/utils/git";
// Regression coverage for #1589: `git.clone({ sha })` used to hardcode
// `--depth 1`, producing a shallow clone whose object store never contained
// non-tip commits. The subsequent `git checkout <sha>` then failed with
// "shallow clone may not contain this commit".
const GIT_ENV = {
GIT_AUTHOR_NAME: "t",
GIT_AUTHOR_EMAIL: "t@example.com",
GIT_COMMITTER_NAME: "t",
GIT_COMMITTER_EMAIL: "t@example.com",
} as const;
function gitRun(cwd: string, args: string[]): string {
const result = Bun.spawnSync({
cmd: ["git", ...args],
cwd,
env: { ...process.env, ...GIT_ENV },
stdout: "pipe",
stderr: "pipe",
});
if (result.exitCode !== 0) {
throw new Error(`git ${args.join(" ")} failed: ${result.stderr.toString()}`);
}
return result.stdout.toString().trim();
}
describe("git.clone with options.sha", () => {
let tmpRoot: string;
let upstreamUrl: string;
let firstSha: string;
let tipSha: string;
beforeAll(async () => {
tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), "omp-git-clone-test-"));
const upstream = path.join(tmpRoot, "upstream");
await fs.mkdir(upstream, { recursive: true });
// `file://` is required: local-path clones ignore `--depth`, which would
// mask the bug. See git-clone(1) "GIT URLS" / "LOCAL PROTOCOL".
upstreamUrl = url.pathToFileURL(upstream).href;
gitRun(upstream, ["init", "-q", "-b", "main"]);
gitRun(upstream, ["commit", "-q", "--allow-empty", "-m", "first"]);
firstSha = gitRun(upstream, ["rev-parse", "HEAD"]);
gitRun(upstream, ["commit", "-q", "--allow-empty", "-m", "second"]);
gitRun(upstream, ["commit", "-q", "--allow-empty", "-m", "third"]);
tipSha = gitRun(upstream, ["rev-parse", "HEAD"]);
});
afterAll(async () => {
await fs.rm(tmpRoot, { recursive: true, force: true });
});
test("checks out a non-tip SHA (regression for #1589)", async () => {
const target = path.join(tmpRoot, "clone-non-tip");
await git.clone(upstreamUrl, target, { sha: firstSha });
expect(gitRun(target, ["rev-parse", "HEAD"])).toBe(firstSha);
});
test("still succeeds when SHA happens to be the tip", async () => {
const target = path.join(tmpRoot, "clone-tip");
await git.clone(upstreamUrl, target, { sha: tipSha });
expect(gitRun(target, ["rev-parse", "HEAD"])).toBe(tipSha);
});
test("cleans up the target directory when SHA does not exist", async () => {
const target = path.join(tmpRoot, "clone-missing");
await expect(git.clone(upstreamUrl, target, { sha: "0".repeat(40) })).rejects.toThrow(/Failed to checkout SHA/);
await expect(fs.stat(target)).rejects.toMatchObject({ code: "ENOENT" });
});
});