Files
wild-pc/castle-api/tests/test_config_delete.py
Paul Payne fa8890ac45 Delete program cascades to its deployments (no more dead-end gate)
A program and its 1:1 tool/static deployment are one thing to the user, so
'Delete program' now just works instead of refusing. DELETE /config/programs/
{name}?cascade=true tears down each referencing deployment (manager-aware:
uninstall a tool from PATH, stop+disable a service/job, drop a static route),
removes them and the program, then converges the runtime (prune orphan units,
regenerate the Caddyfile, reload the gateway). Without cascade it still 409s
(safe API default). The dashboard drops the 'can't remove while deployed' block,
labels the action 'Delete program', and the confirm names exactly what will go.

Verified live: throwaway tool → cascade delete removes program + path deployment,
real config untouched; plain delete of a referenced program still 409s.
castle-api 57 passed.
2026-07-01 11:42:48 -07:00

39 lines
1.5 KiB
Python

"""Tests for cascade program deletion via /config/programs/{name}."""
from __future__ import annotations
import pytest
from fastapi.testclient import TestClient
class TestCascadeDelete:
def test_blocked_without_cascade(self, client: TestClient) -> None:
"""A program with a referencing deployment refuses a plain delete (409)."""
# test-tool (program) is referenced by test-tool (a path deployment).
r = client.delete("/config/programs/test-tool")
assert r.status_code == 409
assert "cascade" in r.json()["detail"]
def test_cascade_removes_program_and_deployment(
self, client: TestClient, monkeypatch: pytest.MonkeyPatch
) -> None:
"""cascade=true tears down + removes the deployments and the program."""
# Stub the runtime teardown so the test has no systemctl/deploy side effects.
import castle_core.deploy as dp
import castle_core.lifecycle as lc
async def _noop(*_a: object, **_k: object) -> None:
return None
monkeypatch.setattr(lc, "deactivate", _noop)
monkeypatch.setattr(dp, "deploy", lambda *_a, **_k: None)
r = client.delete("/config/programs/test-tool?cascade=true")
assert r.status_code == 200
body = r.json()
assert body["action"] == "deleted"
assert "test-tool" in body["removed_deployments"]
# The program is gone from the catalog.
assert client.get("/programs/test-tool").status_code == 404