From 6499794c120c86e10f43786832bc44da6f9fd822 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Tue, 14 Jul 2026 08:33:08 -0400 Subject: [PATCH] fix(testing): add MockCacheManager.get_cached_data_with_strategy/save_cache (#409) * fix(testing): add MockCacheManager.get_cached_data_with_strategy/save_cache ledmatrix-leaderboard's data_fetcher.py calls these two real-CacheManager methods (src/cache_manager.py:313,817), but MockCacheManager had neither -- update() always hit an AttributeError, caught by a broad except and logged, so the harness rendered an empty-but-green leaderboard on every test run without ever exercising real standings data. Both mocks delegate to the existing get()/set() -- a mock doesn't need the real strategy's per-data-type max_age/market-hours timing, plugins under test just need the methods to exist and round-trip whatever was cached. * fix(testing): clear get_cached_data_with_strategy_calls in MockCacheManager.reset() reset() cleared get_calls/set_calls/delete_calls but not the newer get_cached_data_with_strategy_calls tracker, so a reused mock (e.g. across test cases sharing a fixture) retained stale strategy-call records after reset(). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ --------- Co-authored-by: Claude Sonnet 5 --- src/plugin_system/testing/mocks.py | 20 +++++++++++++ test/test_testing_mocks.py | 45 ++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 test/test_testing_mocks.py diff --git a/src/plugin_system/testing/mocks.py b/src/plugin_system/testing/mocks.py index 2bbc9bcc..46104c68 100644 --- a/src/plugin_system/testing/mocks.py +++ b/src/plugin_system/testing/mocks.py @@ -71,6 +71,7 @@ class MockCacheManager: self.get_calls = [] self.set_calls = [] self.delete_calls = [] + self.get_cached_data_with_strategy_calls = [] # Real temp dir for plugins that write/read files under cache_dir. # Registered for cleanup so each mock instance doesn't leak a tmp dir. self.cache_dir = tempfile.mkdtemp(prefix="ledmatrix-mock-cache-") @@ -108,6 +109,24 @@ class MockCacheManager: self.delete_calls.append(key) if key in self._cache: del self._cache[key] + + def get_cached_data_with_strategy(self, key: str, data_type: str = 'default') -> Optional[Any]: + """Mock of CacheManager.get_cached_data_with_strategy (src/cache_manager.py). + + The real method picks a max_age/memory_ttl strategy per data_type + (and extends it during market-closed hours for market data) before + delegating to get_cached_data(). None of that timing nuance matters + for a mock -- plugins under test just need the method to exist and + return whatever was cached, so this delegates straight to get(). + """ + self.get_cached_data_with_strategy_calls.append({'key': key, 'data_type': data_type}) + return self.get(key) + + def save_cache(self, key: str, data: Any) -> None: + """Mock of CacheManager.save_cache (src/cache_manager.py) -- the + write-side counterpart to get_cached_data_with_strategy, used by the + same real-CacheManager-oriented plugins. Delegates to set().""" + self.set(key, data) if key in self._cache_timestamps: del self._cache_timestamps[key] @@ -118,6 +137,7 @@ class MockCacheManager: self.get_calls = [] self.set_calls = [] self.delete_calls = [] + self.get_cached_data_with_strategy_calls = [] class MockConfigManager: diff --git a/test/test_testing_mocks.py b/test/test_testing_mocks.py new file mode 100644 index 00000000..f8ed6516 --- /dev/null +++ b/test/test_testing_mocks.py @@ -0,0 +1,45 @@ +""" +Unit tests for src/plugin_system/testing/mocks.py. + +MockCacheManager/MockPluginManager stand in for the real production +managers under the plugin safety harness -- a missing method here isn't a +harness bug in the abstract, it's a plugin silently failing to render +under test (confirmed on ledmatrix-leaderboard, which calls +get_cached_data_with_strategy() and previously hit an AttributeError that +its own broad except swallowed, producing an empty-but-green render). +""" + +from src.plugin_system.testing.mocks import MockCacheManager + + +class TestMockCacheManagerStrategyMethod: + def test_get_cached_data_with_strategy_returns_cached_value(self): + cm = MockCacheManager() + cm.set("standings_nfl", {"teams": ["KC", "BUF"]}) + result = cm.get_cached_data_with_strategy("standings_nfl", "sports_live") + assert result == {"teams": ["KC", "BUF"]} + + def test_get_cached_data_with_strategy_returns_none_when_missing(self): + cm = MockCacheManager() + assert cm.get_cached_data_with_strategy("missing_key") is None + + def test_get_cached_data_with_strategy_defaults_data_type(self): + cm = MockCacheManager() + cm.set("k", "v") + assert cm.get_cached_data_with_strategy("k") == "v" + + def test_calls_are_tracked(self): + cm = MockCacheManager() + cm.get_cached_data_with_strategy("k", "sports_live") + assert cm.get_cached_data_with_strategy_calls == [{"key": "k", "data_type": "sports_live"}] + + def test_save_cache_is_readable_via_strategy_lookup(self): + cm = MockCacheManager() + cm.save_cache("standings_nfl", {"teams": ["KC", "BUF"]}) + assert cm.get_cached_data_with_strategy("standings_nfl") == {"teams": ["KC", "BUF"]} + + def test_reset_clears_strategy_call_tracking(self): + cm = MockCacheManager() + cm.get_cached_data_with_strategy("k", "sports_live") + cm.reset() + assert cm.get_cached_data_with_strategy_calls == []