Files
Gov-chat-bot/backend/tests/test_metrics.py
2026-03-26 12:49:43 +09:00

115 lines
3.4 KiB
Python

import pytest
import pytest_asyncio
from unittest.mock import AsyncMock, MagicMock, patch
from app.services.metrics import MetricsCollector
from app.services.routing import RoutingResult
def make_result(source="faq", elapsed_ms=100, is_timeout=False):
return RoutingResult(
answer="test",
tier="A",
source=source,
elapsed_ms=elapsed_ms,
is_timeout=is_timeout,
)
class FakePipeline:
def __init__(self):
self._data = {}
self._sets = {}
self._calls = []
def hincrby(self, key, field, amount):
if key not in self._data:
self._data[key] = {}
self._data[key][field] = self._data[key].get(field, 0) + amount
return self
def zadd(self, key, mapping):
if key not in self._sets:
self._sets[key] = {}
self._sets[key].update(mapping)
return self
def zremrangebyrank(self, key, start, stop):
return self
async def execute(self):
return []
class FakeRedis:
def __init__(self):
self._data = {}
self._sets = {}
self._pipeline = FakePipeline()
def pipeline(self):
return self._pipeline
async def hgetall(self, key):
return {k: str(v) for k, v in self._pipeline._data.get(key, {}).items()}
async def zcard(self, key):
return len(self._pipeline._sets.get(key, {}))
async def zrange(self, key, start, stop, withscores=False):
items = sorted(self._pipeline._sets.get(key, {}).items(), key=lambda x: x[1])
slice_ = items[start:stop + 1 if stop >= 0 else None]
if withscores:
return [(k.encode(), v) for k, v in slice_]
return [k.encode() for k, _ in slice_]
@pytest.mark.asyncio
async def test_record_increments_total_count():
"""record 후 total_count == 1."""
redis = FakeRedis()
collector = MetricsCollector(redis)
result = make_result(source="faq")
await collector.record("tenant-1", result)
assert redis._pipeline._data.get("tenant:tenant-1:metrics", {}).get("total_count", 0) == 1
@pytest.mark.asyncio
async def test_record_faq_increments_faq_hit_count():
"""source='faq'인 result record 후 faq_hit_count == 1."""
redis = FakeRedis()
collector = MetricsCollector(redis)
await collector.record("tenant-1", make_result(source="faq"))
counts = redis._pipeline._data.get("tenant:tenant-1:metrics", {})
assert counts.get("faq_hit_count", 0) == 1
@pytest.mark.asyncio
async def test_record_timeout_increments_timeout_count():
"""is_timeout=True인 result record 후 timeout_count == 1."""
redis = FakeRedis()
collector = MetricsCollector(redis)
await collector.record("tenant-1", make_result(is_timeout=True))
counts = redis._pipeline._data.get("tenant:tenant-1:metrics", {})
assert counts.get("timeout_count", 0) == 1
@pytest.mark.asyncio
async def test_get_overview_returns_rates():
"""total=10, faq=4 → faq_hit_rate == 40.0."""
redis = FakeRedis()
collector = MetricsCollector(redis)
# 수동으로 pipeline 데이터 설정
redis._pipeline._data["tenant:tenant-1:metrics"] = {
"total_count": 10,
"faq_hit_count": 4,
"rag_hit_count": 2,
"fallback_count": 3,
"timeout_count": 1,
"response_ms_sum": 1000,
}
overview = await collector.get_overview("tenant-1")
assert overview["rates"]["faq_hit_rate"] == 40.0