refactor: lifespan handlers, module-level imports, bounded scope cache
Replace deprecated @app.on_event startup/shutdown handlers with a FastAPI lifespan context manager, move the inline hashlib/time imports in the auth middleware to module top, and back the unbounded _api_scope_cache with a new size- and TTL-bounded BoundedTTLCache utility. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
"""Tests for the bounded TTL cache utility."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
from aegis_gitea_mcp.cache import BoundedTTLCache
|
||||
|
||||
|
||||
def test_set_and_get_returns_value() -> None:
|
||||
"""A stored value is returned before it expires."""
|
||||
cache: BoundedTTLCache[str, int] = BoundedTTLCache(ttl_seconds=60, max_size=8)
|
||||
cache.set("a", 1)
|
||||
assert cache.get("a") == 1
|
||||
|
||||
|
||||
def test_missing_key_returns_none() -> None:
|
||||
"""An unknown key returns None."""
|
||||
cache: BoundedTTLCache[str, int] = BoundedTTLCache(ttl_seconds=60)
|
||||
assert cache.get("missing") is None
|
||||
|
||||
|
||||
def test_entry_expires_after_ttl() -> None:
|
||||
"""An entry is evicted once its TTL elapses."""
|
||||
cache: BoundedTTLCache[str, int] = BoundedTTLCache(ttl_seconds=0.05, max_size=8)
|
||||
cache.set("a", 1)
|
||||
assert cache.get("a") == 1
|
||||
time.sleep(0.06)
|
||||
assert cache.get("a") is None
|
||||
|
||||
|
||||
def test_size_bound_evicts_oldest() -> None:
|
||||
"""The cache never exceeds max_size; oldest entries are evicted first."""
|
||||
cache: BoundedTTLCache[int, int] = BoundedTTLCache(ttl_seconds=60, max_size=3)
|
||||
for i in range(5):
|
||||
cache.set(i, i)
|
||||
assert len(cache) == 3
|
||||
# 0 and 1 were evicted; 2, 3, 4 remain.
|
||||
assert cache.get(0) is None
|
||||
assert cache.get(1) is None
|
||||
assert cache.get(4) == 4
|
||||
|
||||
|
||||
def test_reinsert_refreshes_recency() -> None:
|
||||
"""Re-setting a key refreshes its position so it is not evicted first."""
|
||||
cache: BoundedTTLCache[str, int] = BoundedTTLCache(ttl_seconds=60, max_size=2)
|
||||
cache.set("a", 1)
|
||||
cache.set("b", 2)
|
||||
cache.set("a", 3) # refresh "a"
|
||||
cache.set("c", 4) # should evict "b", the oldest
|
||||
assert cache.get("b") is None
|
||||
assert cache.get("a") == 3
|
||||
assert cache.get("c") == 4
|
||||
|
||||
|
||||
def test_clear_empties_cache() -> None:
|
||||
"""clear() removes all entries."""
|
||||
cache: BoundedTTLCache[str, int] = BoundedTTLCache(ttl_seconds=60)
|
||||
cache.set("a", 1)
|
||||
cache.clear()
|
||||
assert cache.get("a") is None
|
||||
assert len(cache) == 0
|
||||
|
||||
|
||||
def test_invalid_constructor_args() -> None:
|
||||
"""Non-positive TTL or size is rejected."""
|
||||
with pytest.raises(ValueError):
|
||||
BoundedTTLCache(ttl_seconds=0)
|
||||
with pytest.raises(ValueError):
|
||||
BoundedTTLCache(ttl_seconds=60, max_size=0)
|
||||
Reference in New Issue
Block a user