feat: implement Redis caching for analytics endpoints with fallback to database
Test / test (push) Successful in 15s

This commit is contained in:
k1nq
2025-11-29 09:45:27 +05:00
parent 31d6a05521
commit fbb3116a2d
15 changed files with 671 additions and 13 deletions
+36 -1
View File
@@ -23,6 +23,7 @@ class AnalyticsScenario:
user_id: int
user_email: str
token: str
in_progress_deal_id: int
async def prepare_analytics_scenario(session_factory: async_sessionmaker[AsyncSession]) -> AnalyticsScenario:
@@ -102,6 +103,7 @@ async def prepare_analytics_scenario(session_factory: async_sessionmaker[AsyncSe
user_id=user.id,
user_email=user.email,
token=token,
in_progress_deal_id=next(deal.id for deal in deals if deal.status is DealStatus.IN_PROGRESS),
)
@@ -163,4 +165,37 @@ async def test_deals_funnel_returns_breakdown(
qualification = next(item for item in payload["stages"] if item["stage"] == DealStage.QUALIFICATION.value)
assert qualification["total"] == 1
proposal = next(item for item in payload["stages"] if item["stage"] == DealStage.PROPOSAL.value)
assert proposal["conversion_to_next"] == 100.0
assert proposal["conversion_to_next"] == 100.0
@pytest.mark.asyncio
async def test_deal_update_invalidates_cached_summary(
session_factory: async_sessionmaker[AsyncSession],
client: AsyncClient,
cache_stub,
) -> None:
scenario = await prepare_analytics_scenario(session_factory)
headers = _headers(scenario.token, scenario.organization_id)
first = await client.get(
"/api/v1/analytics/deals/summary?days=30",
headers=headers,
)
assert first.status_code == 200
keys = [key async for key in cache_stub.scan_iter("analytics:summary:*")]
assert keys, "cache should contain warmed summary"
patch_response = await client.patch(
f"/api/v1/deals/{scenario.in_progress_deal_id}",
json={"status": DealStatus.WON.value, "stage": DealStage.CLOSED.value},
headers=headers,
)
assert patch_response.status_code == 200
refreshed = await client.get(
"/api/v1/analytics/deals/summary?days=30",
headers=headers,
)
assert refreshed.status_code == 200
payload = refreshed.json()
assert payload["won"]["count"] == 2