mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 08:22:20 +02:00
Allow Boat disconnect while cleanup remains pending
This commit is contained in:
1 parent
a48dafe553
commit
83ec986f59
2 files changed
+47
-3
No files matched your search
@@ -1871,6 +1871,48 @@ describe("CompanyEnvironments — test provider button", () => {
|
||||
expect(mockEnvironmentsApi.remove).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ driver: "computer", instanceDefault: false, allowed: true },
|
||||
{ driver: "computer", instanceDefault: true, allowed: false },
|
||||
{ driver: "sandbox", instanceDefault: false, allowed: false },
|
||||
])("matches pending-cleanup removal policy for $driver (instance default=$instanceDefault)", async ({ driver, instanceDefault, allowed }) => {
|
||||
mockEnvironmentsApi.list.mockResolvedValue([{
|
||||
id: "env-1", name: "Cleanup environment", driver, status: "active",
|
||||
config: driver === "computer" ? { provider: "boat", sandboxId: "bx_existing" } : { provider: "daytona" },
|
||||
}]);
|
||||
mockEnvironmentsApi.deleteBlastRadius.mockResolvedValue(createDeleteBlastRadius({
|
||||
canDelete: false,
|
||||
deleteBlockedReasons: instanceDefault ? ["instance_default", "pending_cleanup"] : ["pending_cleanup"],
|
||||
staticReferences: { isInstanceDefault: instanceDefault },
|
||||
pendingCleanupLeaseCount: 1,
|
||||
}));
|
||||
root = createRoot(container);
|
||||
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
await act(async () => {
|
||||
root!.render(renderCompanyEnvironments(queryClient, `${ENVIRONMENTS_PATH}/env-1/edit`));
|
||||
});
|
||||
await waitForAssertion(() => {
|
||||
expect(document.body.querySelector("[data-testid='environment-delete-button']")).not.toBeNull();
|
||||
});
|
||||
await act(async () => click(document.body.querySelector("[data-testid='environment-delete-button']")));
|
||||
await waitForAssertion(() => {
|
||||
expect(mockEnvironmentsApi.deleteBlastRadius).toHaveBeenCalledWith("env-1");
|
||||
const confirm = document.body.querySelector<HTMLButtonElement>("[data-testid='environment-delete-confirm']");
|
||||
expect(confirm).not.toBeNull();
|
||||
expect(confirm!.disabled).toBe(!allowed);
|
||||
});
|
||||
if (allowed) {
|
||||
expect(document.body.textContent).toContain("Disconnect computer");
|
||||
await act(async () => click(document.body.querySelector("[data-testid='environment-delete-confirm']")));
|
||||
await waitForAssertion(() => expect(mockEnvironmentsApi.remove).toHaveBeenCalledExactlyOnceWith("env-1"));
|
||||
} else {
|
||||
expect(mockEnvironmentsApi.remove).not.toHaveBeenCalled();
|
||||
expect(document.body.textContent).toContain(instanceDefault
|
||||
? "Choose another default environment before disconnecting this computer."
|
||||
: "Cannot delete this environment while a sandbox cleanup is pending.");
|
||||
}
|
||||
});
|
||||
|
||||
it("links the workspaces holding blocking sandbox leases in the delete dialog", async () => {
|
||||
mockEnvironmentsApi.deleteBlastRadius.mockResolvedValue(
|
||||
createDeleteBlastRadius({
|
||||
|
||||
@@ -103,19 +103,21 @@ function environmentEditPath(environmentId: string) {
|
||||
// the server enforces these gates with a 409; this copy lets the modal explain
|
||||
// the block before the user hits it.
|
||||
function environmentDeleteBlockMessage(impact: EnvironmentDeleteBlastRadius, isComputer = false): string | null {
|
||||
if (isComputer) {
|
||||
return impact.staticReferences.isInstanceDefault
|
||||
? "Choose another default environment before disconnecting this computer."
|
||||
: null;
|
||||
}
|
||||
if (impact.staticReferences.isManagedLocal) {
|
||||
return "Cannot delete the managed local environment.";
|
||||
}
|
||||
if (impact.staticReferences.isInstanceDefault) {
|
||||
if (isComputer) return "Choose another default environment before disconnecting this computer.";
|
||||
return "Cannot delete the current instance default environment. Set a new default environment before deleting this one.";
|
||||
}
|
||||
if (impact.pendingCleanupLeaseCount > 0) {
|
||||
if (isComputer) return "Computer cleanup is pending. Wait for it to finish, then retry disconnecting. Its files will remain on Boat.";
|
||||
return "Cannot delete this environment while a sandbox cleanup is pending. Wait for the cleanup sweep to destroy the orphan sandbox, then retry.";
|
||||
}
|
||||
if (impact.reusableSandboxLeaseCount > 0) {
|
||||
if (isComputer) return "This computer still has saved runner connections. Disconnecting stops its runners and retains its files on Boat.";
|
||||
return "Cannot delete this environment while it has a reusable sandbox lease. Remove the associated execution workspace or issue so Paperclip can destroy the sandbox, then retry.";
|
||||
}
|
||||
return null;
|
||||
|
||||
Reference in new issue
Block a user