first commit
Some checks failed
Deploy / lint (push) Failing after 7s
Deploy / test (push) Has been skipped
Deploy / deploy (push) Has been skipped

This commit is contained in:
Dennis Thiessen
2026-02-20 17:31:01 +01:00
commit 61ab24490d
160 changed files with 17034 additions and 0 deletions

102
tests/unit/test_cache.py Normal file
View 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