feat: TTL enforcement and operational hardening (SAND-WP-0009)

Add TTL parser, expires_at on create, extend_ttl and expire/reap APIs,
activity-core integration doc, repo classification, registry refresh,
HTTP parity, and 69 tests.
This commit is contained in:
2026-06-24 12:44:04 +02:00
parent b58191b23e
commit df658e7ef9
20 changed files with 913 additions and 39 deletions

View File

@@ -88,4 +88,64 @@ def test_restore_snapshot() -> None:
json={"consumer": {"actor": "adm", "project": "sand-boxer"}},
)
assert resp.status_code == 200
assert resp.json()["sandbox_id"] == "restored1"
assert resp.json()["sandbox_id"] == "restored1"
def test_recreate_sandbox() -> None:
from datetime import UTC, datetime
status = SandboxStatus(
sandbox_id="new12345",
profile_id="profile.compose-e2e",
extension_id="ext.compose-ssh",
state=SandboxState.READY,
consumer=Consumer(actor=ActorType.ADM, project="sand-boxer"),
created_at=datetime.now(UTC),
updated_at=datetime.now(UTC),
)
with patch("sandboxer.api.app._manager") as mgr:
mgr.recreate.return_value = status
client = TestClient(app)
resp = client.post("/v1/sandboxes/abc12345/recreate")
assert resp.status_code == 200
assert resp.json()["sandbox_id"] == "new12345"
def test_extend_ttl() -> None:
from datetime import UTC, datetime
now = datetime.now(UTC)
status = SandboxStatus(
sandbox_id="abc12345",
profile_id="profile.compose-e2e",
extension_id="ext.compose-ssh",
state=SandboxState.READY,
consumer=Consumer(actor=ActorType.ADM, project="sand-boxer"),
ttl="2h",
expires_at=now,
created_at=now,
updated_at=now,
ready_at=now,
)
with patch("sandboxer.api.app._manager") as mgr:
mgr.extend_ttl.return_value = status
client = TestClient(app)
resp = client.patch(
"/v1/sandboxes/abc12345/ttl",
json={"duration": "2h"},
)
assert resp.status_code == 200
assert resp.json()["ttl"] == "2h"
def test_expire_sandboxes() -> None:
from sandboxer.models import ExpireActionResult
with patch("sandboxer.api.app._manager") as mgr:
mgr.expire.return_value = [
ExpireActionResult(sandbox_id="x", reason="ttl", action="dry-run")
]
client = TestClient(app)
resp = client.post("/v1/sandboxes/expire")
assert resp.status_code == 200
assert resp.json()[0]["action"] == "dry-run"