omnidiag / tests /test_cache.py
yahyoha's picture
test: add comprehensive test suite for Features 2.1–2.6 (81 tests)
5b98477
Raw
History Blame Contribute Delete
4.55 kB
"""
Tests — Feature 2.5: Redis Caching Layer
Tests cache hit/miss behaviour and flush via backend cache utilities.
"""
import pytest
from fastapi_cache import FastAPICache
from fastapi_cache.backends.inmemory import InMemoryBackend
from backend.cache import cache_get, cache_set, cache_flush, predict_cache_key, schema_cache_key
HEART_PAYLOAD = {
"Age": 55,
"Sex": "M",
"ChestPainType": "ATA",
"RestingBP": 130,
"Cholesterol": 250,
"FastingBS": 0,
"RestingECG": "Normal",
"MaxHR": 150,
"ExerciseAngina": "N",
"Oldpeak": 1.5,
"ST_Slope": "Up",
}
class TestCacheUtilities:
async def test_cache_set_and_get(self, db_tables):
key = "omnidiag:test:unit"
value = {"hello": "world", "number": 42}
await cache_set(key, value, ttl=60)
result = await cache_get(key)
assert result == value
async def test_cache_get_missing_key_returns_none(self, db_tables):
result = await cache_get("omnidiag:test:nonexistent-key-xyz")
assert result is None
async def test_cache_set_overwrites(self, db_tables):
key = "omnidiag:test:overwrite"
await cache_set(key, {"v": 1}, ttl=60)
await cache_set(key, {"v": 2}, ttl=60)
result = await cache_get(key)
assert result == {"v": 2}
async def test_cache_flush_removes_omnidiag_keys(self, db_tables):
await cache_set("omnidiag:test:flush-a", {"a": 1}, ttl=60)
await cache_set("omnidiag:test:flush-b", {"b": 2}, ttl=60)
deleted = await cache_flush()
assert isinstance(deleted, int)
# Keys should be gone
assert await cache_get("omnidiag:test:flush-a") is None
assert await cache_get("omnidiag:test:flush-b") is None
async def test_predict_cache_key_is_deterministic(self, db_tables):
key1 = predict_cache_key("heart_disease", HEART_PAYLOAD)
key2 = predict_cache_key("heart_disease", HEART_PAYLOAD)
assert key1 == key2
async def test_predict_cache_key_differs_by_disease(self, db_tables):
key_heart = predict_cache_key("heart_disease", HEART_PAYLOAD)
key_diabetes = predict_cache_key("diabetes", HEART_PAYLOAD)
assert key_heart != key_diabetes
async def test_predict_cache_key_differs_by_data(self, db_tables):
payload_a = {**HEART_PAYLOAD, "age": 30}
payload_b = {**HEART_PAYLOAD, "age": 70}
assert predict_cache_key("heart_disease", payload_a) != predict_cache_key("heart_disease", payload_b)
async def test_schema_cache_key_format(self, db_tables):
key = schema_cache_key("heart_disease")
assert "heart_disease" in key
assert "schema" in key
class TestCacheIntegration:
"""End-to-end tests: verify Cache-Hit header changes on repeated calls."""
async def test_second_predict_call_is_cache_hit(self, client, doctor_token):
# First call — miss
await client.post(
"/api/v4/heart_disease/predict",
json=HEART_PAYLOAD,
headers={"Authorization": f"Bearer {doctor_token}"},
)
# Second call with identical payload — must be a hit
resp2 = await client.post(
"/api/v4/heart_disease/predict",
json=HEART_PAYLOAD,
headers={"Authorization": f"Bearer {doctor_token}"},
)
assert resp2.status_code == 200
assert resp2.headers.get("cache-hit") == "true"
async def test_cache_flush_resets_hit_to_miss(self, client, doctor_token, admin_token):
# Populate cache
await client.post(
"/api/v4/heart_disease/predict",
json=HEART_PAYLOAD,
headers={"Authorization": f"Bearer {doctor_token}"},
)
# Flush via admin endpoint
flush_resp = await client.post(
"/admin/cache/flush",
headers={"Authorization": f"Bearer {admin_token}"},
)
assert flush_resp.status_code == 200
# Next predict should be a miss again
resp = await client.post(
"/api/v4/heart_disease/predict",
json=HEART_PAYLOAD,
headers={"Authorization": f"Bearer {doctor_token}"},
)
assert resp.headers.get("cache-hit") == "false"
async def test_schema_cached_on_second_call(self, client, db_tables):
await client.get("/api/v4/heart_disease/schema")
resp2 = await client.get("/api/v4/heart_disease/schema")
assert resp2.status_code == 200
assert resp2.headers.get("cache-hit") == "true"