Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d
This commit is contained in:
commit
1dfbca08a1
528 files changed
+131054
No files matched your search
@@ -0,0 +1,154 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlmodel import SQLModel, create_engine
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def api_client(tmp_path, monkeypatch):
|
||||
db_path = tmp_path / "unraid_vm_actions.db"
|
||||
monkeypatch.setenv("APPOPS_DB_PATH", str(db_path))
|
||||
|
||||
from backend.app.db import session
|
||||
from backend.app.db.init_db import init_db
|
||||
from backend.app.main import app
|
||||
|
||||
engine = create_engine(f"sqlite:///{Path(db_path)}", connect_args={"check_same_thread": False})
|
||||
session.engine = engine
|
||||
SQLModel.metadata.drop_all(engine)
|
||||
SQLModel.metadata.create_all(engine)
|
||||
init_db()
|
||||
|
||||
with TestClient(app) as client:
|
||||
yield client
|
||||
|
||||
|
||||
def test_vm_capabilities_expose_safe_execution_boundary(api_client):
|
||||
response = api_client.get("/api/unraid/vms/capabilities")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
actions = {item["id"]: item for item in payload["actions"]}
|
||||
assert actions["start"]["policyDecision"] == "allowed"
|
||||
assert actions["start"]["executionSupported"] is True
|
||||
assert actions["pause"]["policyDecision"] == "approval_required"
|
||||
assert actions["pause"]["executionSupported"] is True
|
||||
assert actions["stop"]["policyDecision"] == "blocked"
|
||||
assert actions["stop"]["executionSupported"] is False
|
||||
assert actions["force_stop"]["policyDecision"] == "blocked"
|
||||
|
||||
|
||||
def test_manual_vm_start_runs_job_postcheck_and_audit(api_client, monkeypatch):
|
||||
from backend.app.services import unraid_vm_service
|
||||
|
||||
inventory_calls = {"count": 0}
|
||||
|
||||
def fake_inventory(_self):
|
||||
inventory_calls["count"] += 1
|
||||
state = "SHUTOFF" if inventory_calls["count"] == 1 else "RUNNING"
|
||||
return {"status": "ok", "vms": [{"id": "server:vm-1", "name": "Test VM", "state": state}]}
|
||||
|
||||
monkeypatch.setattr(unraid_vm_service.UnraidConnector, "get_vm_summary", fake_inventory)
|
||||
monkeypatch.setattr(
|
||||
unraid_vm_service.UnraidConnector,
|
||||
"mutate_vm",
|
||||
lambda _self, vm_id, action: {
|
||||
"success": vm_id == "server:vm-1" and action == "start",
|
||||
"status": "ok",
|
||||
"message": "vm_start_accepted",
|
||||
},
|
||||
)
|
||||
|
||||
response = api_client.post(
|
||||
"/api/unraid/vms/server%3Avm-1/actions/start",
|
||||
json={"source": "manual", "confirm": True},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["success"] is True
|
||||
assert payload["decision"] == "allowed"
|
||||
assert payload["before_state"]["state"] == "SHUTOFF"
|
||||
assert payload["after_state"] == {"state": "RUNNING", "verified": True}
|
||||
assert payload["jobId"].startswith("job-")
|
||||
assert payload["auditId"].startswith("audit-")
|
||||
|
||||
jobs = api_client.get("/api/jobs").json()
|
||||
assert any(job["id"] == payload["jobId"] and job["status"] == "succeeded" for job in jobs)
|
||||
|
||||
audits = api_client.get("/api/audit").json()
|
||||
audit = next(item for item in audits if item["id"] == payload["auditId"])
|
||||
assert audit["action_id"] == "unraid.vm.start"
|
||||
assert audit["target_type"] == "virtual_machine"
|
||||
|
||||
|
||||
def test_vm_start_requires_explicit_confirmation(api_client):
|
||||
response = api_client.post(
|
||||
"/api/unraid/vms/vm-1/actions/start",
|
||||
json={"source": "manual", "confirm": False},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["status"] == "confirm_required"
|
||||
|
||||
|
||||
def test_vm_pause_creates_executable_approval(api_client, monkeypatch):
|
||||
from backend.app.services import unraid_vm_service
|
||||
|
||||
inventory_calls = {"count": 0}
|
||||
|
||||
def fake_inventory(_self):
|
||||
inventory_calls["count"] += 1
|
||||
state = "RUNNING" if inventory_calls["count"] == 1 else "PAUSED"
|
||||
return {"status": "ok", "vms": [{"id": "vm-2", "name": "Workload", "state": state}]}
|
||||
|
||||
monkeypatch.setattr(unraid_vm_service.UnraidConnector, "get_vm_summary", fake_inventory)
|
||||
monkeypatch.setattr(
|
||||
unraid_vm_service.UnraidConnector,
|
||||
"mutate_vm",
|
||||
lambda _self, vm_id, action: {
|
||||
"success": vm_id == "vm-2" and action == "pause",
|
||||
"status": "ok",
|
||||
"message": "vm_pause_accepted",
|
||||
},
|
||||
)
|
||||
|
||||
requested = api_client.post(
|
||||
"/api/unraid/vms/vm-2/actions/pause",
|
||||
json={"source": "manual", "confirm": True},
|
||||
).json()
|
||||
assert requested["decision"] == "approval_required"
|
||||
assert requested["approvalId"].startswith("appr-")
|
||||
|
||||
approval_id = requested["approvalId"]
|
||||
approved = api_client.patch(f"/api/approvals/{approval_id}", json={"status": "approved"})
|
||||
assert approved.status_code == 200
|
||||
|
||||
executed = api_client.post(f"/api/approvals/{approval_id}/execute")
|
||||
assert executed.status_code == 200
|
||||
payload = executed.json()
|
||||
assert payload["success"] is True
|
||||
assert payload["approvalStatus"] == "executed"
|
||||
assert payload["after_state"] == {"state": "PAUSED", "verified": True}
|
||||
|
||||
|
||||
def test_vm_stop_is_blocked_without_calling_connector(api_client, monkeypatch):
|
||||
from backend.app.services import unraid_vm_service
|
||||
|
||||
monkeypatch.setattr(
|
||||
unraid_vm_service.UnraidConnector,
|
||||
"mutate_vm",
|
||||
lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("blocked action reached connector")),
|
||||
)
|
||||
|
||||
response = api_client.post(
|
||||
"/api/unraid/vms/vm-3/actions/stop",
|
||||
json={"source": "manual", "confirm": True},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["decision"] == "blocked"
|
||||
assert payload["result"] == "blocked"
|
||||
assert "force-stop" in payload["message"]
|
||||
Reference in new issue
Block a user