test: cover the config-editor globals regression + secrets surfaces
- endpoint-level: a deployment/program edit via /config/* leaves castle.yaml globals byte-identical (the exact regression that shipped — the endpoints were tested but nobody asserted globals survived) - write_program_file leaves globals untouched (core) - /secrets/info reports the backend Still uncovered (tracked): castle mesh CLI, direct-read refactor (dns/stacks), backend-aware doctor, and all frontend (no test runner configured).
This commit is contained in:
@@ -78,3 +78,34 @@ class TestProgramEditSafety:
|
|||||||
assert m["description"] == "renamed" # change applied
|
assert m["description"] == "renamed" # change applied
|
||||||
assert m["source"].endswith("wired-in") # source preserved
|
assert m["source"].endswith("wired-in") # source preserved
|
||||||
assert m["commands"]["lint"] == [["make", "lint"]] # commands preserved
|
assert m["commands"]["lint"] == [["make", "lint"]] # commands preserved
|
||||||
|
|
||||||
|
|
||||||
|
def test_deployment_edit_leaves_castle_yaml_globals_untouched(client, castle_root):
|
||||||
|
"""The regression that bit us: a config-editor deployment edit must NOT rewrite
|
||||||
|
castle.yaml globals (role/secrets). Scoped writes guarantee this."""
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
cy = castle_root / "castle.yaml"
|
||||||
|
data = yaml.safe_load(cy.read_text())
|
||||||
|
data["role"] = "authority"
|
||||||
|
data["secrets"] = {"backend": "openbao", "addr": "https://v:8200"}
|
||||||
|
cy.write_text(yaml.safe_dump(data))
|
||||||
|
before = cy.read_text()
|
||||||
|
|
||||||
|
resp = client.put("/config/deployments/test-svc", json={"config": {"reach": "off"}})
|
||||||
|
assert resp.status_code == 200
|
||||||
|
assert cy.read_text() == before, "deployment edit rewrote castle.yaml globals"
|
||||||
|
|
||||||
|
|
||||||
|
def test_program_edit_leaves_castle_yaml_globals_untouched(client, castle_root):
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
cy = castle_root / "castle.yaml"
|
||||||
|
data = yaml.safe_load(cy.read_text())
|
||||||
|
data["role"] = "authority"
|
||||||
|
cy.write_text(yaml.safe_dump(data))
|
||||||
|
before = cy.read_text()
|
||||||
|
|
||||||
|
resp = client.put("/config/programs/wired-in", json={"config": {"version": "9.9.9"}})
|
||||||
|
assert resp.status_code == 200
|
||||||
|
assert cy.read_text() == before, "program edit rewrote castle.yaml globals"
|
||||||
|
|||||||
@@ -22,3 +22,12 @@ def test_set_override_rejected_on_file_backend(client: TestClient) -> None:
|
|||||||
|
|
||||||
def test_get_missing_override_is_404(client: TestClient) -> None:
|
def test_get_missing_override_is_404(client: TestClient) -> None:
|
||||||
assert client.get("/secrets/overrides/primer/NOPE").status_code == 404
|
assert client.get("/secrets/overrides/primer/NOPE").status_code == 404
|
||||||
|
|
||||||
|
|
||||||
|
def test_secrets_info_reports_backend(client: TestClient) -> None:
|
||||||
|
r = client.get("/secrets/info")
|
||||||
|
assert r.status_code == 200
|
||||||
|
body = r.json()
|
||||||
|
assert body["backend"] == "file" # conftest forces file in tests
|
||||||
|
assert body["writable"] is True
|
||||||
|
assert "role" in body
|
||||||
|
|||||||
@@ -86,6 +86,24 @@ def test_write_deployment_file_leaves_globals_untouched(castle_root: Path) -> No
|
|||||||
assert cy.read_text() == before # globals byte-identical — nothing touched them
|
assert cy.read_text() == before # globals byte-identical — nothing touched them
|
||||||
|
|
||||||
|
|
||||||
|
def test_write_program_file_leaves_globals_untouched(castle_root: Path) -> None:
|
||||||
|
"""Scoped program write must not rewrite castle.yaml globals either."""
|
||||||
|
from castle_core.config import load_config, write_program_file
|
||||||
|
|
||||||
|
cy = castle_root / "castle.yaml"
|
||||||
|
data = yaml.safe_load(cy.read_text())
|
||||||
|
data["role"] = "authority"
|
||||||
|
data["secrets"] = {"backend": "openbao"}
|
||||||
|
cy.write_text(yaml.safe_dump(data))
|
||||||
|
before = cy.read_text()
|
||||||
|
|
||||||
|
config = load_config(castle_root)
|
||||||
|
name = next(iter(config.programs))
|
||||||
|
write_program_file(config, name)
|
||||||
|
|
||||||
|
assert cy.read_text() == before
|
||||||
|
|
||||||
|
|
||||||
def test_registry_role_round_trip(tmp_path: Path) -> None:
|
def test_registry_role_round_trip(tmp_path: Path) -> None:
|
||||||
reg = NodeRegistry(
|
reg = NodeRegistry(
|
||||||
node=NodeConfig(hostname="civil", role="authority"), deployed={}
|
node=NodeConfig(hostname="civil", role="authority"), deployed={}
|
||||||
|
|||||||
Reference in New Issue
Block a user