mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-09 16:46:37 +00:00
* chore: remove dead code, deprecate unused plugin APIs (over-engineering audit) Whole-tree audit. Every symbol was checked against core, the plugin monorepo and all eight third-party plugins in plugins.json first. - Deprecate (removal 3.10.0) plugin-facing methods nothing calls: LogoDownloader bulk download, ConfigManager backup/secret wrappers, APIHelper extras, BackgroundDataService poll API, PluginManager / PluginStateManager info readers, and a few CacheManager, FontManager, BaseOddsManager, DynamicTeamResolver methods and PluginTestCase. plugin_api_usage.py learns their receiver names; DEPRECATIONS doc regenerated. - Remove core-internal dead code: CacheMetrics, Vegas status/stats plumbing, sync "new cycle" message (followers ignore unknown types), unused operation types, test-only PluginCatalog readers, IPC to_dict and ping, _parse_form_value, CacheStrategyProtocol, ErrorAggregator callbacks, duplicate web response helpers. - Web UI: drop never-mounted json-file-manager.js, the example widget, utils/error_handler.js, four uncalled PluginAPI methods, and 29 escapeHtml shims (call window.LEDEscape directly). Public globals, BaseWidget and widget names unchanged. - Remove six one-off scripts (owner decision) and the unused markupsafe and pytest-mock pins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(web): calendar picker error text goes in a text node, not innerHTML Same output as the escaped innerHTML it replaces; clears Codacy's XSS-pattern alerts on the line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
161 lines
5.4 KiB
Python
161 lines
5.4 KiB
Python
"""
|
|
Base test class for LEDMatrix plugins.
|
|
|
|
Provides common fixtures and helper methods for plugin testing.
|
|
|
|
Note: this is the plugin-author-facing base class shipped with the
|
|
core (importable as src.plugin_system.testing.plugin_test_base). The
|
|
repo's own plugin tests use a separate, richer harness in
|
|
test/plugins/test_plugin_base.py — the two are intentionally distinct.
|
|
"""
|
|
|
|
import unittest
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Dict, Any, Optional
|
|
|
|
# Add project root to path for imports
|
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent.parent
|
|
if str(PROJECT_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
from src.plugin_system.testing.mocks import (
|
|
MockDisplayManager,
|
|
MockCacheManager,
|
|
MockConfigManager,
|
|
MockPluginManager
|
|
)
|
|
from src.deprecation import deprecated
|
|
|
|
|
|
class PluginTestCase(unittest.TestCase):
|
|
"""
|
|
Base test case for plugin testing.
|
|
|
|
Provides common fixtures and helper methods.
|
|
"""
|
|
|
|
@deprecated("3.10.0", "use src.plugin_system.testing.harness and the mocks directly")
|
|
def setUp(self):
|
|
"""Set up test fixtures."""
|
|
# Create mock managers
|
|
self.display_manager = MockDisplayManager(width=128, height=32)
|
|
self.cache_manager = MockCacheManager()
|
|
self.config_manager = MockConfigManager()
|
|
self.plugin_manager = MockPluginManager()
|
|
|
|
# Default plugin configuration
|
|
self.plugin_config = {
|
|
'enabled': True,
|
|
'display_duration': 15.0
|
|
}
|
|
|
|
# Plugin ID for tests
|
|
self.plugin_id = 'test-plugin'
|
|
|
|
def tearDown(self):
|
|
"""Clean up after tests."""
|
|
# Reset all mocks
|
|
self.display_manager.reset()
|
|
self.cache_manager.reset()
|
|
self.config_manager.reset()
|
|
self.plugin_manager.reset()
|
|
|
|
def create_plugin_instance(self, plugin_class, plugin_id: Optional[str] = None,
|
|
config: Optional[Dict[str, Any]] = None):
|
|
"""
|
|
Create a plugin instance with mock dependencies.
|
|
|
|
Args:
|
|
plugin_class: Plugin class to instantiate
|
|
plugin_id: Optional plugin ID (defaults to self.plugin_id)
|
|
config: Optional config dict (defaults to self.plugin_config)
|
|
|
|
Returns:
|
|
Plugin instance
|
|
"""
|
|
pid = plugin_id or self.plugin_id
|
|
cfg = config or self.plugin_config.copy()
|
|
|
|
return plugin_class(
|
|
plugin_id=pid,
|
|
config=cfg,
|
|
display_manager=self.display_manager,
|
|
cache_manager=self.cache_manager,
|
|
plugin_manager=self.plugin_manager
|
|
)
|
|
|
|
def assert_plugin_initialized(self, plugin):
|
|
"""Assert that plugin was initialized correctly."""
|
|
self.assertIsNotNone(plugin)
|
|
self.assertEqual(plugin.plugin_id, self.plugin_id)
|
|
self.assertEqual(plugin.config, self.plugin_config)
|
|
self.assertEqual(plugin.display_manager, self.display_manager)
|
|
self.assertEqual(plugin.cache_manager, self.cache_manager)
|
|
self.assertEqual(plugin.plugin_manager, self.plugin_manager)
|
|
|
|
def assert_display_cleared(self):
|
|
"""Assert that display was cleared."""
|
|
self.assertTrue(self.display_manager.clear_called)
|
|
|
|
def assert_display_updated(self):
|
|
"""Assert that display was updated."""
|
|
self.assertTrue(self.display_manager.update_called)
|
|
|
|
def assert_text_drawn(self, text: Optional[str] = None):
|
|
"""
|
|
Assert that text was drawn on display.
|
|
|
|
Args:
|
|
text: Optional text to check for
|
|
"""
|
|
text_calls = [c for c in self.display_manager.draw_calls if c['type'] == 'text']
|
|
self.assertGreater(len(text_calls), 0, "No text was drawn")
|
|
if text:
|
|
texts = [c['text'] for c in text_calls]
|
|
self.assertIn(text, texts, f"Text '{text}' not found in drawn texts: {texts}")
|
|
|
|
def assert_image_drawn(self):
|
|
"""Assert that an image was drawn on display."""
|
|
image_calls = [c for c in self.display_manager.draw_calls if c['type'] == 'image']
|
|
self.assertGreater(len(image_calls), 0, "No image was drawn")
|
|
|
|
def assert_cache_get(self, key: Optional[str] = None):
|
|
"""
|
|
Assert that cache.get was called.
|
|
|
|
Args:
|
|
key: Optional key to check for
|
|
"""
|
|
self.assertGreater(len(self.cache_manager.get_calls), 0, "cache.get was not called")
|
|
if key:
|
|
keys = [c['key'] for c in self.cache_manager.get_calls]
|
|
self.assertIn(key, keys, f"Key '{key}' not found in cache.get calls: {keys}")
|
|
|
|
def assert_cache_set(self, key: Optional[str] = None):
|
|
"""
|
|
Assert that cache.set was called.
|
|
|
|
Args:
|
|
key: Optional key to check for
|
|
"""
|
|
self.assertGreater(len(self.cache_manager.set_calls), 0, "cache.set was not called")
|
|
if key:
|
|
keys = [c['key'] for c in self.cache_manager.set_calls]
|
|
self.assertIn(key, keys, f"Key '{key}' not found in cache.set calls: {keys}")
|
|
|
|
def get_mock_config(self, **overrides) -> Dict[str, Any]:
|
|
"""
|
|
Get mock configuration with optional overrides.
|
|
|
|
Args:
|
|
**overrides: Config values to override
|
|
|
|
Returns:
|
|
Configuration dictionary
|
|
"""
|
|
config = self.plugin_config.copy()
|
|
config.update(overrides)
|
|
return config
|
|
|