165 lines
5.1 KiB
Python
165 lines
5.1 KiB
Python
from datetime import UTC, datetime, timedelta
|
|
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 / "appops_test.db"
|
|
monkeypatch.setenv("APPOPS_DB_PATH", str(db_path))
|
|
monkeypatch.setenv("APP_OPS_VOICE_TOKEN", "test-voice-token")
|
|
|
|
from backend.app.main import app
|
|
from backend.app.db import session
|
|
from backend.app.db.init_db import init_db
|
|
|
|
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_statistics_summary_uses_live_audit_job_and_unraid_data(api_client, monkeypatch):
|
|
from backend.app.connectors.unraid import UnraidConnector
|
|
from backend.app.api import routes_observability
|
|
|
|
monkeypatch.setattr(
|
|
UnraidConnector,
|
|
"get_system_metrics",
|
|
lambda self: {"status": "ok", "cpuUsage": 37, "ramUsage": 64},
|
|
raising=True,
|
|
)
|
|
routes_observability._STATISTICS_HISTORY.clear()
|
|
|
|
now = datetime.now(UTC).replace(tzinfo=None)
|
|
|
|
api_client.post(
|
|
"/api/actions/execute",
|
|
json={
|
|
"intent": "volume.delete",
|
|
"target": "postgres-data",
|
|
"source": "manual",
|
|
"risk": "critical",
|
|
"plan": [],
|
|
},
|
|
)
|
|
api_client.post(
|
|
"/api/actions/execute",
|
|
json={
|
|
"intent": "container.restart",
|
|
"target": "portainer",
|
|
"source": "voice",
|
|
"risk": "high",
|
|
"plan": [],
|
|
},
|
|
)
|
|
|
|
response = api_client.get("/api/statistics/summary")
|
|
|
|
assert response.status_code == 200
|
|
payload = response.json()
|
|
assert payload["cpuHistory"][-1]["value"] == 37
|
|
assert payload["ramHistory"][-1]["value"] == 64
|
|
assert payload["blockedActions"] >= 1
|
|
assert payload["voiceActionCount"] >= 1
|
|
assert all(point["time"] != "00:00" for point in payload["cpuHistory"])
|
|
assert payload["generatedAt"] >= (now - timedelta(minutes=1)).isoformat()
|
|
|
|
|
|
def test_statistics_summary_keeps_partial_unraid_metrics_explicit(api_client, monkeypatch):
|
|
from backend.app.connectors.unraid import UnraidConnector
|
|
from backend.app.api import routes_observability
|
|
|
|
monkeypatch.setattr(
|
|
UnraidConnector,
|
|
"get_system_metrics",
|
|
lambda self: {
|
|
"status": "partial",
|
|
"cpuUsage": None,
|
|
"ramUsage": 92,
|
|
"reason": "cpu_unavailable",
|
|
},
|
|
raising=True,
|
|
)
|
|
routes_observability._STATISTICS_HISTORY.clear()
|
|
|
|
response = api_client.get("/api/statistics/summary")
|
|
|
|
assert response.status_code == 200
|
|
payload = response.json()
|
|
assert payload["sources"]["unraid"] == "partial"
|
|
assert payload["cpuHistory"][-1]["value"] is None
|
|
assert payload["ramHistory"][-1]["value"] == 92
|
|
assert payload["sourceWarnings"][0]["source"] == "unraid"
|
|
assert payload["sourceWarnings"][0]["surface"] == "metrics"
|
|
|
|
|
|
def test_storage_summary_exposes_cache_pool_inventory(api_client, monkeypatch):
|
|
from backend.app.connectors.portainer import PortainerConnector
|
|
from backend.app.connectors.unraid import UnraidConnector
|
|
|
|
monkeypatch.setattr(
|
|
UnraidConnector,
|
|
"get_cache_status",
|
|
lambda self: {
|
|
"status": "ok",
|
|
"cacheUsage": 88,
|
|
"pools": [
|
|
{
|
|
"name": "cache",
|
|
"status": "DISK_OK",
|
|
"healthy": True,
|
|
"type": "CACHE",
|
|
"usedGiB": 88,
|
|
"sizeGiB": 100,
|
|
"freeGiB": 12,
|
|
"usagePercent": 88,
|
|
}
|
|
],
|
|
},
|
|
raising=True,
|
|
)
|
|
monkeypatch.setattr(
|
|
UnraidConnector,
|
|
"get_share_summary",
|
|
lambda self: {"status": "ok", "shares": [{"name": "appdata", "usedGiB": 88, "sizeGiB": 100, "critical": True}]},
|
|
raising=True,
|
|
)
|
|
monkeypatch.setattr(
|
|
UnraidConnector,
|
|
"get_disk_warnings",
|
|
lambda self: {
|
|
"status": "ok",
|
|
"disks": [{"name": "disk1", "status": "DISK_OK", "healthy": True, "type": "DATA", "usedGiB": 50, "sizeGiB": 100, "freeGiB": 50}],
|
|
"diskWarnings": 0,
|
|
},
|
|
raising=True,
|
|
)
|
|
monkeypatch.setattr(PortainerConnector, "list_containers", lambda self: {"status": "ok", "containers": []}, raising=True)
|
|
|
|
response = api_client.get("/api/storage/summary")
|
|
|
|
assert response.status_code == 200
|
|
payload = response.json()
|
|
assert payload["cacheUsage"] == 88
|
|
assert payload["storagePools"] == [
|
|
{
|
|
"name": "cache",
|
|
"status": "DISK_OK",
|
|
"healthy": True,
|
|
"type": "CACHE",
|
|
"usedGiB": 88,
|
|
"sizeGiB": 100,
|
|
"freeGiB": 12,
|
|
"usagePercent": 88,
|
|
}
|
|
]
|
|
assert payload["disks"][0]["name"] == "disk1"
|