first commit
This commit is contained in:
102
tests/unit/test_cache.py
Normal file
102
tests/unit/test_cache.py
Normal file
@@ -0,0 +1,102 @@
|
||||
"""Unit tests for app.cache LRU cache wrapper."""
|
||||
|
||||
from datetime import date
|
||||
|
||||
from app.cache import LRUCache
|
||||
|
||||
|
||||
def _key(ticker: str, indicator: str = "RSI") -> tuple:
|
||||
return (ticker, date(2024, 1, 1), date(2024, 6, 1), indicator)
|
||||
|
||||
|
||||
class TestLRUCacheBasics:
|
||||
def test_get_miss_returns_none(self):
|
||||
cache = LRUCache()
|
||||
assert cache.get(_key("AAPL")) is None
|
||||
|
||||
def test_set_and_get_round_trip(self):
|
||||
cache = LRUCache()
|
||||
cache.set(_key("AAPL"), {"score": 72})
|
||||
assert cache.get(_key("AAPL")) == {"score": 72}
|
||||
|
||||
def test_set_overwrites_existing(self):
|
||||
cache = LRUCache()
|
||||
cache.set(_key("AAPL"), 1)
|
||||
cache.set(_key("AAPL"), 2)
|
||||
assert cache.get(_key("AAPL")) == 2
|
||||
assert cache.size == 1
|
||||
|
||||
def test_size_and_clear(self):
|
||||
cache = LRUCache(max_size=5)
|
||||
for i in range(3):
|
||||
cache.set((f"T{i}", None, None, "RSI"), i)
|
||||
assert cache.size == 3
|
||||
cache.clear()
|
||||
assert cache.size == 0
|
||||
|
||||
|
||||
class TestLRUEviction:
|
||||
def test_evicts_lru_when_full(self):
|
||||
cache = LRUCache(max_size=3)
|
||||
cache.set(_key("A"), 1)
|
||||
cache.set(_key("B"), 2)
|
||||
cache.set(_key("C"), 3)
|
||||
# A is LRU — inserting D should evict A
|
||||
cache.set(_key("D"), 4)
|
||||
assert cache.get(_key("A")) is None
|
||||
assert cache.size == 3
|
||||
|
||||
def test_access_promotes_entry(self):
|
||||
cache = LRUCache(max_size=3)
|
||||
cache.set(_key("A"), 1)
|
||||
cache.set(_key("B"), 2)
|
||||
cache.set(_key("C"), 3)
|
||||
# Access A so B becomes LRU
|
||||
cache.get(_key("A"))
|
||||
cache.set(_key("D"), 4)
|
||||
assert cache.get(_key("B")) is None
|
||||
assert cache.get(_key("A")) == 1
|
||||
|
||||
def test_update_promotes_entry(self):
|
||||
cache = LRUCache(max_size=3)
|
||||
cache.set(_key("A"), 1)
|
||||
cache.set(_key("B"), 2)
|
||||
cache.set(_key("C"), 3)
|
||||
# Update A so B becomes LRU
|
||||
cache.set(_key("A"), 10)
|
||||
cache.set(_key("D"), 4)
|
||||
assert cache.get(_key("B")) is None
|
||||
assert cache.get(_key("A")) == 10
|
||||
|
||||
|
||||
class TestTickerInvalidation:
|
||||
def test_invalidate_removes_all_entries_for_ticker(self):
|
||||
cache = LRUCache()
|
||||
cache.set(("AAPL", date(2024, 1, 1), date(2024, 6, 1), "RSI"), 1)
|
||||
cache.set(("AAPL", date(2024, 1, 1), date(2024, 6, 1), "ADX"), 2)
|
||||
cache.set(("MSFT", date(2024, 1, 1), date(2024, 6, 1), "RSI"), 3)
|
||||
removed = cache.invalidate_ticker("AAPL")
|
||||
assert removed == 2
|
||||
assert cache.get(("AAPL", date(2024, 1, 1), date(2024, 6, 1), "RSI")) is None
|
||||
assert cache.get(("AAPL", date(2024, 1, 1), date(2024, 6, 1), "ADX")) is None
|
||||
assert cache.get(("MSFT", date(2024, 1, 1), date(2024, 6, 1), "RSI")) == 3
|
||||
|
||||
def test_invalidate_nonexistent_ticker_returns_zero(self):
|
||||
cache = LRUCache()
|
||||
cache.set(_key("AAPL"), 1)
|
||||
assert cache.invalidate_ticker("GOOG") == 0
|
||||
assert cache.size == 1
|
||||
|
||||
def test_invalidate_on_empty_cache(self):
|
||||
cache = LRUCache()
|
||||
assert cache.invalidate_ticker("AAPL") == 0
|
||||
|
||||
|
||||
class TestMaxSizeProperty:
|
||||
def test_default_max_size(self):
|
||||
cache = LRUCache()
|
||||
assert cache.max_size == 1000
|
||||
|
||||
def test_custom_max_size(self):
|
||||
cache = LRUCache(max_size=50)
|
||||
assert cache.max_size == 50
|
||||
Reference in New Issue
Block a user