feat: 31 new tests, metrics LLM counters, Dockerfile caching, Makefile targets, compose limits, code fixes

This commit is contained in:
Marker689
2026-05-11 23:08:09 +03:00
parent 20bf7e6745
commit 18efcf482e
26 changed files with 840 additions and 12 deletions

View File

@@ -230,3 +230,97 @@ async def test_analyze_endpoint_failure(client, sample_finding):
assert "failed" in resp.text.lower()
guarddog_nexus.config.config.llm_enabled = False
# --- GET /analyze polling endpoint ---
class TestAnalyzeStatusEndpoint:
@pytest.mark.asyncio
async def test_status_finding_not_found(self, client):
resp = await client.get("/api/v1/findings/99999/analyze")
assert resp.status_code == 404
@pytest.mark.asyncio
async def test_status_returns_report_when_complete(self, client, sample_finding_with_report):
import guarddog_nexus.config
guarddog_nexus.config.config.llm_enabled = True
resp = await client.get(f"/api/v1/findings/{sample_finding_with_report.id}/analyze")
assert resp.status_code == 200
assert "safe" in resp.text
guarddog_nexus.config.config.llm_enabled = False
@pytest.mark.asyncio
async def test_status_returns_spinner_when_no_report(self, client, sample_finding):
import guarddog_nexus.config
guarddog_nexus.config.config.llm_enabled = True
resp = await client.get(f"/api/v1/findings/{sample_finding.id}/analyze")
assert resp.status_code == 200
assert "hx-get" in resp.text.lower()
guarddog_nexus.config.config.llm_enabled = False
@pytest.mark.asyncio
async def test_status_returns_spinner_when_analyzing(self, client, db_session, sample_finding):
from sqlalchemy import select
from guarddog_nexus.db.models import Finding
finding = await db_session.scalar(select(Finding).where(Finding.id == sample_finding.id))
finding.report = {"status": "analyzing"}
await db_session.commit()
resp = await client.get(f"/api/v1/findings/{sample_finding.id}/analyze")
assert resp.status_code == 200
assert "hx-get" in resp.text.lower()
# --- LLM retry exhaustion ---
@pytest.mark.asyncio
async def test_analyze_finding_exhausts_all_retries():
import guarddog_nexus.config
from guarddog_nexus.core.llm import analyze_finding
guarddog_nexus.config.config.llm_api_key = "sk-test"
with patch("guarddog_nexus.core.llm._attempt_llm_call", return_value=None):
with patch("guarddog_nexus.core.llm.asyncio.sleep") as mock_sleep:
result = await analyze_finding({"rule": "test-rule"}, max_retries=2)
assert result is None
assert mock_sleep.call_count == 1
guarddog_nexus.config.config.llm_api_key = ""
# --- LLM lock cleanup ---
@pytest.mark.asyncio
async def test_cleanup_llm_locks_removes_unlocked():
import asyncio
from guarddog_nexus.routes.web import _llm_lock, _llm_locks
async with _llm_lock:
_llm_locks[100] = asyncio.Lock()
_llm_locks[200] = asyncio.Lock()
await _llm_locks[100].acquire()
for key in list(_llm_locks.keys()):
if not _llm_locks[key].locked():
_llm_locks.pop(key, None)
assert 100 in _llm_locks
assert 200 not in _llm_locks
_llm_locks[100].release()
_llm_locks.clear()