martian7777
feat: implement backend core with ORM models, authentication, and AI-driven telemetry diagnostics
e5be436
Raw
History Blame Contribute Delete
3.53 kB
"""AI explanation tests (uses the built-in mock explainer; no network)."""
from __future__ import annotations
import pytest
from httpx import AsyncClient
from app.services import telemetry_service
pytestmark = pytest.mark.asyncio
@pytest.fixture
def _patch_session(session_factory, monkeypatch):
monkeypatch.setattr(telemetry_service, "AsyncSessionLocal", session_factory)
def _csv(rows: int = 150) -> bytes:
import numpy as np
import pandas as pd
rng = np.random.default_rng(3)
temp = rng.normal(70, 1.0, rows)
temp[10] += 30
df = pd.DataFrame(
{
"timestamp": pd.date_range("2026-01-01", periods=rows, freq="min"),
"temperature": temp,
"vibration": rng.normal(0.5, 0.05, rows),
"pressure": rng.normal(30, 0.5, rows),
"rotational_speed": rng.normal(1500, 10, rows),
}
)
return df.to_csv(index=False).encode()
async def test_explain_no_data_returns_mock(auth_client: AsyncClient, machine_id: str):
resp = await auth_client.get(f"/api/v1/ai/explain/{machine_id}")
assert resp.status_code == 200
body = resp.json()
assert body["is_mock"] is True
assert body["model_used"] == "mock"
assert body["anomalies_found"] == 0
assert isinstance(body["recommendations"], list)
async def test_explain_after_upload(auth_client: AsyncClient, machine_id: str, _patch_session):
files = {"file": ("sensors.csv", _csv(150), "text/csv")}
await auth_client.post(f"/api/v1/telemetry/upload/{machine_id}", files=files)
resp = await auth_client.get(f"/api/v1/ai/explain/{machine_id}?window=200")
assert resp.status_code == 200
body = resp.json()
assert body["window_analyzed"] == 150
assert body["anomalies_found"] >= 1
assert len(body["recommendations"]) >= 1
assert body["machine_name"] == "Pump-1"
async def test_explain_requires_auth(client: AsyncClient):
import uuid
resp = await client.get(f"/api/v1/ai/explain/{uuid.uuid4()}")
assert resp.status_code == 401
async def test_explain_uses_openrouter_when_enabled(
auth_client: AsyncClient, machine_id: str, monkeypatch
):
"""When a key is configured, the OpenRouter path is used and parsed."""
import json as _json
import types
from app.core import config as config_module
from app.services import ai_service as ai_module
# Pretend AI is enabled.
monkeypatch.setattr(config_module.settings, "openrouter_api_key", "test-key")
monkeypatch.setattr(ai_module.settings, "openrouter_api_key", "test-key")
payload = {
"summary": "Bearing wear detected.",
"explanation": "Vibration trending up.",
"recommendations": ["Inspect bearings", "Re-lubricate"],
}
class _FakeCompletions:
async def create(self, **kwargs):
msg = types.SimpleNamespace(content=_json.dumps(payload))
choice = types.SimpleNamespace(message=msg)
return types.SimpleNamespace(choices=[choice])
class _FakeChat:
completions = _FakeCompletions()
class _FakeClient:
def __init__(self, *a, **k):
self.chat = _FakeChat()
import openai
monkeypatch.setattr(openai, "AsyncOpenAI", _FakeClient)
resp = await auth_client.get(f"/api/v1/ai/explain/{machine_id}")
assert resp.status_code == 200
body = resp.json()
assert body["is_mock"] is False
assert body["summary"] == "Bearing wear detected."
assert "Inspect bearings" in body["recommendations"]