diff --git a/test/unit/miner-attempt-worktree.test.ts b/test/unit/miner-attempt-worktree.test.ts index 503e5dd8b4..9b0b68d7a8 100644 --- a/test/unit/miner-attempt-worktree.test.ts +++ b/test/unit/miner-attempt-worktree.test.ts @@ -57,6 +57,11 @@ describe("createRealWorktreeExec (#5132)", () => { describe("prepareAttemptWorktree / cleanupAttemptWorktree (#5132)", () => { it("REGRESSION: worktreePath is a real, checked-out git repo on a real branch, not an empty directory", async () => { + // Six real, sequential git subprocess spawns (origin init/add/commit, the clone inside + // prepareAttemptWorktree, its `git worktree add`, and this test's own rev-parse) -- legitimately more + // wall-clock latency than the default 15s test timeout reliably covers under concurrent full-suite + // load (passes in well under 1s in isolation; the same class of flake fixed for + // test/unit/agent-sdk-driver.test.ts's real-git-subprocess test). const root = tempRoot("loopover-miner-attempt-worktree-"); const originPath = initOriginRepo(root); const cloneBaseDir = join(root, "cache"); @@ -72,9 +77,11 @@ describe("prepareAttemptWorktree / cleanupAttemptWorktree (#5132)", () => { // And it's a real, distinct branch -- not just a copy of main. const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: result.worktreePath, encoding: "utf8" }).trim(); expect(branch).toBe("loopover/attempt/attempt-1"); - }); + }, 60000); it("removes a succeeded attempt's worktree but retains a failed one's, per the engine's own retention policy", async () => { + // Real git subprocess round trip -- two full prepareAttemptWorktree/cleanupAttemptWorktree cycles, more + // real spawns than the REGRESSION test above. See its comment for why this needs an explicit timeout. const root = tempRoot("loopover-miner-attempt-worktree-cleanup-"); const originPath = initOriginRepo(root); const cloneBaseDir = join(root, "cache"); @@ -90,7 +97,7 @@ describe("prepareAttemptWorktree / cleanupAttemptWorktree (#5132)", () => { const retainedResult = await cleanupAttemptWorktree(failed.repoPath, failed.worktreePath, false); expect(retainedResult).toEqual({ ok: true, removed: false }); expect(existsSync(failed.worktreePath)).toBe(true); - }); + }, 60000); it("returns ok:false when the base clone cannot be prepared, without attempting git worktree add", async () => { const root = tempRoot("loopover-miner-attempt-worktree-clonefail-"); @@ -111,6 +118,8 @@ describe("prepareAttemptWorktree / cleanupAttemptWorktree (#5132)", () => { }); it("returns ok:false with git's real stderr when git worktree add fails (e.g. an unknown base branch)", async () => { + // Real git subprocess round trip (origin init + a real clone + a failing `git worktree add`). See the + // REGRESSION test above for why this needs an explicit timeout. const root = tempRoot("loopover-miner-attempt-worktree-addfail-"); const originPath = initOriginRepo(root); const cloneBaseDir = join(root, "cache"); @@ -121,5 +130,5 @@ describe("prepareAttemptWorktree / cleanupAttemptWorktree (#5132)", () => { if (result.ok) throw new Error("expected failure"); expect(result.repoPath).toBe(join(cloneBaseDir, "acme", "widgets")); expect(result.error).toBeTruthy(); - }); + }, 60000); }); diff --git a/test/unit/miner-repo-clone.test.ts b/test/unit/miner-repo-clone.test.ts index c803b314ff..847541eaf2 100644 --- a/test/unit/miner-repo-clone.test.ts +++ b/test/unit/miner-repo-clone.test.ts @@ -62,6 +62,11 @@ describe("resolveRepoCloneBaseDir / resolveRepoCloneDir (#5132)", () => { describe("ensureRepoCloned (#5132)", () => { it("clones a real repo on first use, and fetches + hard-resets an existing clone to pick up new commits", async () => { + // Nine real, sequential git subprocess spawns (origin init/add/commit, the first ensureRepoCloned's + // clone, a second origin commit, and the second ensureRepoCloned's fetch+checkout+reset) -- + // legitimately more wall-clock latency than the default 15s test timeout reliably covers under + // concurrent full-suite load (passes in well under 1s in isolation; the same class of flake fixed for + // test/unit/agent-sdk-driver.test.ts's real-git-subprocess test). const root = tempRoot("loopover-miner-repo-clone-"); const originPath = initOriginRepo(root); const cloneBaseDir = join(root, "cache"); @@ -80,9 +85,14 @@ describe("ensureRepoCloned (#5132)", () => { expect(second.ok).toBe(true); expect(readFileSync(join(second.repoPath, "README.md"), "utf8")).toBe("hello\n"); expect(readFileSync(join(second.repoPath, "second.txt"), "utf8")).toBe("second file\n"); - }); + }, 60000); it("respects a non-default baseBranch on the fetch+reset path", async () => { + // Ten real, sequential git subprocess spawns (origin init/add/commit, the branch checkout, the first + // ensureRepoCloned's clone, the second commitFile's add/commit, and the second ensureRepoCloned's + // fetch+checkout+reset) -- legitimately more wall-clock latency than the default 15s test timeout + // reliably covers under concurrent full-suite load (passes in well under 1s in isolation; the same + // class of flake fixed for test/unit/agent-sdk-driver.test.ts's real-git-subprocess test). const root = tempRoot("loopover-miner-repo-clone-branch-"); const originPath = initOriginRepo(root); execFileSync("git", ["checkout", "-b", "develop"], { cwd: originPath, stdio: "ignore" }); @@ -95,7 +105,7 @@ describe("ensureRepoCloned (#5132)", () => { const second = await ensureRepoCloned("acme/widgets", { cloneBaseDir, remoteUrl: originPath, baseBranch: "develop" }); expect(second.ok).toBe(true); expect(readFileSync(join(second.repoPath, "develop-only.txt"), "utf8")).toBe("develop content\n"); - }); + }, 60000); it("rejects a malformed repoFullName", async () => { await expect(ensureRepoCloned("not-a-repo")).rejects.toThrow("invalid_repo_full_name"); @@ -110,6 +120,8 @@ describe("ensureRepoCloned (#5132)", () => { }); it("returns ok:false on a fetch failure without touching the existing clone (injected runGit)", async () => { + // Real origin init + a real clone before the injected-runGit assertion. See the first test in this + // block for why this needs an explicit timeout. const root = tempRoot("loopover-miner-repo-clone-fetchfail-"); const originPath = initOriginRepo(root); const cloneBaseDir = join(root, "cache"); @@ -120,9 +132,11 @@ describe("ensureRepoCloned (#5132)", () => { const second = await ensureRepoCloned("acme/widgets", { cloneBaseDir, remoteUrl: originPath, runGit }); expect(second.ok).toBe(false); expect(second.error).toBe("network unreachable"); - }); + }, 60000); it("returns ok:false on a checkout failure and a reset failure (injected runGit)", async () => { + // Real origin init + a real clone before the injected-runGit assertions. See the first test in this + // block for why this needs an explicit timeout. const root = tempRoot("loopover-miner-repo-clone-checkoutfail-"); const originPath = initOriginRepo(root); const cloneBaseDir = join(root, "cache"); @@ -137,7 +151,7 @@ describe("ensureRepoCloned (#5132)", () => { const resetResult = await ensureRepoCloned("acme/widgets", { cloneBaseDir, remoteUrl: originPath, runGit: resetFails }); expect(resetResult.ok).toBe(false); expect(resetResult.error).toBe("git_reset_failed"); - }); + }, 60000); it("returns ok:false with a fallback error message on a clone failure with no stderr (injected runGit)", async () => { const root = tempRoot("loopover-miner-repo-clone-nostderr-"); @@ -149,6 +163,8 @@ describe("ensureRepoCloned (#5132)", () => { }); it("rejects a dash-prefixed baseBranch before invoking git (#5923)", async () => { + // Real origin init + a real clone before the injected-runGit assertion. See the first test in this + // block for why this needs an explicit timeout. const root = tempRoot("loopover-miner-repo-clone-unsafe-branch-"); const originPath = initOriginRepo(root); const cloneBaseDir = join(root, "cache"); @@ -163,7 +179,7 @@ describe("ensureRepoCloned (#5132)", () => { expect(result.ok).toBe(false); expect(result.error).toBe("invalid_base_branch"); expect(runGitCalls).toBe(0); - }); + }, 60000); it("rejects a dash-prefixed remoteUrl before invoking git (#5923)", async () => { const root = tempRoot("loopover-miner-repo-clone-unsafe-url-");