import { describe, expect, it } from "vitest"; import type { IssueWorkProduct } from "@paperclipai/shared"; import { reconcileRuntimeServiceWorkProducts } from "../services/work-products.js"; function workProduct(overrides: Partial = {}): IssueWorkProduct { return { id: "wp-1", companyId: "company-1", projectId: "project-1", issueId: "issue-1", executionWorkspaceId: "ews-1", runtimeServiceId: "runtime-1", type: "runtime_service", provider: "paperclip", externalId: null, title: "Workspace preview", url: "https://workspace.example.ts.net:42013/", status: "open", reviewState: "none", isPrimary: true, healthStatus: "healthy", summary: null, metadata: null, sourceTrust: null, createdByRunId: null, createdAt: new Date("2026-08-19T00:00:00.000Z"), updatedAt: new Date("2026-08-19T00:00:00.000Z"), ...overrides, }; } describe("reconcileRuntimeServiceWorkProducts", () => { it("adopts the live runtime URL after a port change", () => { const [reconciled] = reconcileRuntimeServiceWorkProducts( [workProduct()], [{ id: "runtime-1", url: "https://workspace.example.ts.net:42055/", status: "running", healthStatus: "healthy" }], ); expect(reconciled!.url).toBe("https://workspace.example.ts.net:42055/"); expect(reconciled!.healthStatus).toBe("healthy"); }); it("closes a stopped runtime but keeps a running unhealthy runtime active", () => { const [stopped] = reconcileRuntimeServiceWorkProducts( [workProduct()], [{ id: "runtime-1", url: "https://workspace.example.ts.net:42013/", status: "stopped", healthStatus: "healthy" }], ); expect(stopped!.status).toBe("closed"); expect(stopped!.healthStatus).toBe("unhealthy"); const [unhealthy] = reconcileRuntimeServiceWorkProducts( [workProduct()], [{ id: "runtime-1", url: "https://workspace.example.ts.net:42013/", status: "running", healthStatus: "unhealthy" }], ); expect(unhealthy!.status).toBe("open"); expect(unhealthy!.healthStatus).toBe("unhealthy"); }); it("keeps the recorded URL when the runtime row is gone and closes the work product", () => { const [reconciled] = reconcileRuntimeServiceWorkProducts([workProduct()], []); expect(reconciled!.url).toBe("https://workspace.example.ts.net:42013/"); expect(reconciled!.status).toBe("closed"); expect(reconciled!.healthStatus).toBe("unhealthy"); }); it("leaves non-runtime work products and unlinked rows untouched", () => { const pullRequest = workProduct({ id: "wp-pr", type: "pull_request", runtimeServiceId: null }); const unlinked = workProduct({ id: "wp-unlinked", runtimeServiceId: null }); const reconciled = reconcileRuntimeServiceWorkProducts([pullRequest, unlinked], []); expect(reconciled[0]).toBe(pullRequest); expect(reconciled[1]).toBe(unlinked); }); it("returns the same objects when nothing drifted", () => { const product = workProduct(); const reconciled = reconcileRuntimeServiceWorkProducts( [product], [{ id: "runtime-1", url: product.url, status: "running", healthStatus: "healthy" }], ); expect(reconciled[0]).toBe(product); }); });