diff --git a/packages/host-router/src/ports/git-pr-status.ts b/packages/host-router/src/ports/git-pr-status.ts index eadfaed4b5..9fc55205a2 100644 --- a/packages/host-router/src/ports/git-pr-status.ts +++ b/packages/host-router/src/ports/git-pr-status.ts @@ -13,5 +13,5 @@ export interface IGitPrStatus { cloudPrUrl: string | null, ): Promise; getCachedPrUrl(taskId: string): CachedPrUrlOutput; - setPrimaryPrUrl(taskId: string, prUrl: string): void; + setPrimaryPrUrl(taskId: string, prUrl: string): Promise; } diff --git a/packages/workspace-server/src/services/git/task-pr-status.test.ts b/packages/workspace-server/src/services/git/task-pr-status.test.ts index b2492f7442..ca8dd5e7ba 100644 --- a/packages/workspace-server/src/services/git/task-pr-status.test.ts +++ b/packages/workspace-server/src/services/git/task-pr-status.test.ts @@ -215,14 +215,15 @@ describe("TaskPrStatusService revalidation PR detection", () => { }); describe("TaskPrStatusService.setPrimaryPrUrl", () => { - it("emits the promoted url as prUrl even though the row column is stale", () => { - const PR_OLD = "https://github.com/acme/repo/pull/1"; - const PR_NEW = "https://github.com/acme/repo/pull/2"; - const gitService = {} as unknown as GitService; + const PR_OLD = "https://github.com/acme/repo/pull/1"; + const PR_NEW = "https://github.com/acme/repo/pull/2"; + + function makeService(getPrDetailsByUrl: ReturnType) { + const gitService = { getPrDetailsByUrl } as unknown as GitService; const workspaceService = { emit: vi.fn() }; const workspaceRepo = { promotePrUrl: vi.fn(), - findByTaskId: vi.fn().mockReturnValue({ prUrl: PR_OLD, prState: "open" }), + updatePrCache: vi.fn(), getPrUrls: vi.fn().mockReturnValue([PR_NEW, PR_OLD]), }; const service = new TaskPrStatusService( @@ -230,15 +231,46 @@ describe("TaskPrStatusService.setPrimaryPrUrl", () => { workspaceRepo as unknown as IWorkspaceRepository, workspaceService as unknown as WorkspaceService, ); + return { service, workspaceService, workspaceRepo }; + } + + it.each([ + { + name: "recomputes and emits the promoted PR's live state, not the stale cache", + details: vi.fn().mockResolvedValue({ + state: "open", + merged: false, + draft: false, + }), + expectedPrState: "open", + }, + { + name: "emits a null state when the promoted PR's details are unavailable", + details: vi.fn().mockResolvedValue(null), + expectedPrState: null, + }, + { + name: "falls back to a null state when the details fetch rejects", + details: vi.fn().mockRejectedValue(new Error("network down")), + expectedPrState: null, + }, + ])("$name", async ({ details, expectedPrState }) => { + const { service, workspaceService, workspaceRepo } = makeService(details); - service.setPrimaryPrUrl("task-1", PR_NEW); + await service.setPrimaryPrUrl("task-1", PR_NEW); expect(workspaceRepo.promotePrUrl).toHaveBeenCalledWith("task-1", PR_NEW); + expect(details).toHaveBeenCalledWith(PR_NEW); + expect(workspaceRepo.updatePrCache).toHaveBeenCalledWith("task-1", { + prUrl: PR_NEW, + prState: expectedPrState, + accumulate: false, + }); expect(workspaceService.emit).toHaveBeenCalledWith("taskPrInfoChanged", { taskId: "task-1", prUrl: PR_NEW, prUrls: [PR_NEW, PR_OLD], - prState: "open", + prState: expectedPrState, }); }); }); diff --git a/packages/workspace-server/src/services/git/task-pr-status.ts b/packages/workspace-server/src/services/git/task-pr-status.ts index 09c98723b6..d499c58087 100644 --- a/packages/workspace-server/src/services/git/task-pr-status.ts +++ b/packages/workspace-server/src/services/git/task-pr-status.ts @@ -48,14 +48,24 @@ export class TaskPrStatusService { }; } - setPrimaryPrUrl(taskId: string, prUrl: string): void { + async setPrimaryPrUrl(taskId: string, prUrl: string): Promise { this.workspaceRepo.promotePrUrl(taskId, prUrl); - const row = this.workspaceRepo.findByTaskId(taskId); + const details = await this.gitService + .getPrDetailsByUrl(prUrl) + .catch(() => null); + const prState: SidebarPrState = details + ? mapPrState(details.state, details.merged, details.draft) + : null; + this.workspaceRepo.updatePrCache(taskId, { + prUrl, + prState, + accumulate: false, + }); this.workspaceService.emit("taskPrInfoChanged", { taskId, prUrl, prUrls: this.workspaceRepo.getPrUrls(taskId), - prState: row?.prState ?? null, + prState, }); }