| ... | @@ -279,6 +279,51 @@ describe("git integration", () => { | ... | @@ -279,6 +279,51 @@ describe("git integration", () => { |
| 279 | expect(details.behindCount).toBe(1); | 279 | expect(details.behindCount).toBe(1); |
| 280 | }); | 280 | }); |
| 281 | | 281 | |
| | 282 | it("keeps checkout successful when upstream refresh fails", async () => { |
| | 283 | await using remote = await makeTmpDir(); |
| | 284 | await using source = await makeTmpDir(); |
| | 285 | await git(remote.path, "init --bare"); |
| | 286 | |
| | 287 | await initRepoWithCommit(source.path); |
| | 288 | const defaultBranch = (await listGitBranches({ cwd: source.path })).branches.find( |
| | 289 | (branch) => branch.current, |
| | 290 | )!.name; |
| | 291 | await git(source.path, `remote add origin ${JSON.stringify(remote.path)}`); |
| | 292 | await git(source.path, `push -u origin ${defaultBranch}`); |
| | 293 | |
| | 294 | const featureBranch = "feature-refresh-failure"; |
| | 295 | await git(source.path, `branch ${featureBranch}`); |
| | 296 | await git(source.path, `checkout ${featureBranch}`); |
| | 297 | await writeFile(path.join(source.path, "feature.txt"), "feature base\n"); |
| | 298 | await git(source.path, "add feature.txt"); |
| | 299 | await git(source.path, "commit -m 'feature base'"); |
| | 300 | await git(source.path, `push -u origin ${featureBranch}`); |
| | 301 | await git(source.path, `checkout ${defaultBranch}`); |
| | 302 | |
| | 303 | class RefreshFailingGitCoreService extends GitCoreService { |
| | 304 | refreshFetchAttempts = 0; |
| | 305 | |
| | 306 | override async git( |
| | 307 | cwd: string, |
| | 308 | args: readonly string[], |
| | 309 | allowNonZeroExit = false, |
| | 310 | ): Promise<void> { |
| | 311 | if (args[0] === "fetch") { |
| | 312 | this.refreshFetchAttempts += 1; |
| | 313 | throw new Error("simulated fetch timeout"); |
| | 314 | } |
| | 315 | await super.git(cwd, args, allowNonZeroExit); |
| | 316 | } |
| | 317 | } |
| | 318 | |
| | 319 | const core = new RefreshFailingGitCoreService(); |
| | 320 | await expect(core.checkoutBranch({ cwd: source.path, branch: featureBranch })).resolves.toBe( |
| | 321 | undefined, |
| | 322 | ); |
| | 323 | expect(core.refreshFetchAttempts).toBe(1); |
| | 324 | expect(await git(source.path, "branch --show-current")).toBe(featureBranch); |
| | 325 | }); |
| | 326 | |
| 282 | it("throws when branch does not exist", async () => { | 327 | it("throws when branch does not exist", async () => { |
| 283 | await using tmp = await makeTmpDir(); | 328 | await using tmp = await makeTmpDir(); |
| 284 | await initRepoWithCommit(tmp.path); | 329 | await initRepoWithCommit(tmp.path); |