Livrare LOT 1 - Didi

This commit is contained in:
Dezvoltari Evotech 2026-06-25 14:13:25 -07:00
commit 5380c3fc63
990 changed files with 133308 additions and 0 deletions

View file

@ -0,0 +1,96 @@
"""CRUD tests for the DB-backed model/extractor catalog (Val 2).
Uses an in-memory SQLite async DB no Postgres needed. Route handlers are
called directly with an injected session + principal.
"""
from __future__ import annotations
import os
import pytest
import pytest_asyncio
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from dashboard.api.routes import catalog
from dashboard.db.models import Base
# Prefer a real Postgres (matches production) when TEST_DATABASE_URL is set;
# fall back to in-memory SQLite for quick local runs.
_DB_URL = os.environ.get("TEST_DATABASE_URL", "sqlite+aiosqlite:///:memory:")
# HTTPException is imported lazily so the module imports even without fastapi
from fastapi import HTTPException # noqa: E402
@pytest_asyncio.fixture
async def session():
engine = create_async_engine(_DB_URL)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await conn.run_sync(Base.metadata.create_all)
maker = async_sessionmaker(engine, expire_on_commit=False)
async with maker() as s:
yield s
await engine.dispose()
async def test_crud_lifecycle(session):
created = await catalog.create_catalog(
catalog.CatalogIn(
name="qwen3.5", service="llm", supports_gpu=True, context_length=32768,
quantization="awq",
),
principal="tester",
session=session,
)
assert created["name"] == "qwen3.5"
assert created["supports_gpu"] is True
eid = created["id"]
listed = await catalog.list_catalog(session=session)
assert listed["total"] == 1
filtered = await catalog.list_catalog(service="embeddings", session=session)
assert filtered["total"] == 0
updated = await catalog.update_catalog(
eid, catalog.CatalogPatch(enabled=False, notes="retired"),
principal="tester", session=session,
)
assert updated["enabled"] is False
assert updated["notes"] == "retired"
deleted = await catalog.delete_catalog(eid, principal="tester", session=session)
assert deleted["deleted"] == eid
assert (await catalog.list_catalog(session=session))["total"] == 0
async def test_duplicate_rejected(session):
await catalog.create_catalog(
catalog.CatalogIn(name="bge-m3", service="embeddings"),
principal="t", session=session,
)
with pytest.raises(HTTPException) as exc:
await catalog.create_catalog(
catalog.CatalogIn(name="bge-m3", service="embeddings"),
principal="t", session=session,
)
assert exc.value.status_code == 409
async def test_invalid_kind_rejected(session):
with pytest.raises(HTTPException) as exc:
await catalog.create_catalog(
catalog.CatalogIn(name="x", service="llm", kind="bogus"),
principal="t", session=session,
)
assert exc.value.status_code == 422
async def test_update_missing_404(session):
with pytest.raises(HTTPException) as exc:
await catalog.update_catalog(
999, catalog.CatalogPatch(enabled=True), principal="t", session=session
)
assert exc.value.status_code == 404

View file

@ -0,0 +1,118 @@
"""Tests for the central AI monitoring routes (Val 1).
External sources (service health, RabbitMQ, Prometheus) are mocked with respx /
monkeypatch no real infra needed.
"""
from __future__ import annotations
import httpx
import pytest
import respx
from dashboard.api.routes import monitoring
from dashboard.config import DashboardSettings, SettingsCache
def _settings(**overrides) -> DashboardSettings:
base = {
"database_url": "postgresql+asyncpg://u:p@localhost/db",
"external_url": "http://localhost:51300",
}
base.update(overrides)
s = DashboardSettings(**base)
SettingsCache.set(s)
return s
# --- services aggregation -------------------------------------------------
async def test_services_summary(monkeypatch):
_settings()
async def fake_probe(client, mid, url):
status = "down" if mid == "video" else "healthy"
return {"module": mid, "url": url, "status": status, "latency_ms": 1.0}
monkeypatch.setattr(monitoring, "_probe", fake_probe)
out = await monitoring.monitoring_services()
assert out["summary"]["total"] == len(monitoring._service_endpoints())
assert out["summary"]["down"] == 1
assert out["summary"]["healthy"] == out["summary"]["total"] - 1
assert any(s["module"] == "video" and s["status"] == "down" for s in out["services"])
# --- RabbitMQ queues ------------------------------------------------------
async def test_queues_disabled_when_unset():
_settings(rabbitmq_mgmt_url=None)
out = await monitoring.monitoring_queues()
assert out["enabled"] is False
assert out["queues"] == []
@respx.mock
async def test_queues_parsed_and_sorted():
_settings(rabbitmq_mgmt_url="http://rmq:15672")
respx.get("http://rmq:15672/api/queues").mock(
return_value=httpx.Response(
200,
json=[
{"name": "low", "vhost": "/", "messages": 2, "messages_ready": 2,
"messages_unacknowledged": 0, "consumers": 1, "state": "running"},
{"name": "busy", "vhost": "/", "messages": 50, "messages_ready": 48,
"messages_unacknowledged": 2, "consumers": 3, "state": "running"},
],
)
)
out = await monitoring.monitoring_queues()
assert out["enabled"] is True
# sorted by messages desc
assert [q["name"] for q in out["queues"]] == ["busy", "low"]
assert out["queues"][0]["unacked"] == 2
@respx.mock
async def test_queues_failopen_on_error():
_settings(rabbitmq_mgmt_url="http://rmq:15672")
respx.get("http://rmq:15672/api/queues").mock(side_effect=httpx.ConnectError("x"))
out = await monitoring.monitoring_queues()
assert out["enabled"] is True
assert out["queues"] == []
assert "error" in out
# --- Prometheus latency ---------------------------------------------------
async def test_latency_disabled_when_unset():
_settings(prometheus_url=None)
out = await monitoring.monitoring_latency()
assert out["enabled"] is False
@respx.mock
async def test_latency_parsed_per_job():
_settings(prometheus_url="http://prom:9090")
# Same response for all three quantile queries → equal p50/p90/p99.
respx.get("http://prom:9090/api/v1/query").mock(
return_value=httpx.Response(
200,
json={
"data": {
"result": [
{"metric": {"job": "llm"}, "value": [0, "0.250"]},
{"metric": {"job": "web"}, "value": [0, "1.500"]},
]
}
},
)
)
out = await monitoring.monitoring_latency()
assert out["enabled"] is True
by_job = {s["job"]: s for s in out["services"]}
assert by_job["llm"]["p50_ms"] == 250.0 # 0.250s → 250ms
assert by_job["web"]["p99_ms"] == 1500.0

View file

@ -0,0 +1,38 @@
"""RBAC wiring invariants (Val 2).
Asserts that human-facing read routers carry the auth dependency, while the
service-to-service routers (config polling, ingest) stay open breaking the
latter would cut config propagation / event ingestion to the AI services.
"""
from __future__ import annotations
from dashboard.api.routes import (
audit,
catalog,
config,
history,
ingest,
monitoring,
proxy,
stats,
)
def test_human_read_routers_are_protected():
for r in (
monitoring.router,
stats.router,
history.router,
audit.router,
proxy.router,
catalog.router,
):
assert len(r.dependencies) >= 1, "expected router-level auth dependency"
def test_service_routers_stay_open():
# config (polled by AI services) and ingest (service-to-service) must NOT
# gain router-level auth — that would break the platform.
assert len(config.router.dependencies) == 0
assert len(ingest.router.dependencies) == 0