mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-07 03:38:06 +00:00
The unit-tests CI job ran an explicit 24-file allowlist that had rotted: 63 of 90 test files (display, vegas, store manager, web API, web_interface) never ran on a PR. The job now runs all of test/ (minus test/plugins, which the plugin-safety job owns) so new test files are enrolled by default and any exclusion needs a visible, commented --ignore. The plugin-safety job was a green no-op: plugins/ is empty in CI, so every test skipped with 'Manifest not found'. It now renders a bundled deterministic fixture plugin (test/fixtures/plugins/ci-fixture-plugin, golden images included for all 8 default sizes) via LEDMATRIX_PLUGINS_DIR, and sets LEDMATRIX_REQUIRE_PLUGINS=1 so discovering zero plugins fails loudly instead of skipping green. The per-plugin suites document that they target dev machines with real plugins installed. Coverage is now measured and enforced in exactly one place — the CI unit-tests step (--cov=src --cov=web_interface --cov-fail-under=45, from a measured 47% baseline). pytest.ini previously declared --cov-fail-under=30 but CI always passed --no-cov, so the gate had never run anywhere; local pytest is now coverage-free and fast. Enabling the 63 unenrolled files surfaced three cases of test rot, fixed here: test_display_controller_vegas_tick.py could not collect without the hardware rgbmatrix module (now uses the emulator convention), the state-reconciliation unrecoverable-cache tests broke when production added the is_plugin_uninstalled tombstone check (bare Mock returned truthy), and test_get_system_status assumed the optional psutil dependency (now installed via requirements-test.txt and guarded by importorskip). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NohXi78cwsAKtN1sCfxjUh
95 lines
3.5 KiB
Python
95 lines
3.5 KiB
Python
"""
|
|
Integration tests for soccer-scoreboard plugin.
|
|
|
|
Requires the real plugin to be installed (plugins/ or plugin-repos/,
|
|
or the dir named by LEDMATRIX_PLUGINS_DIR) — on machines without it,
|
|
every test here skips by design. CI covers plugin safety with the
|
|
bundled fixture plugin via test_plugin_matrix.py instead.
|
|
"""
|
|
|
|
import pytest
|
|
from test.plugins.test_plugin_base import PluginTestBase
|
|
|
|
|
|
class TestSoccerScoreboardPlugin(PluginTestBase):
|
|
"""Test soccer-scoreboard plugin integration."""
|
|
|
|
@pytest.fixture
|
|
def plugin_id(self):
|
|
return 'soccer-scoreboard'
|
|
|
|
def test_manifest_exists(self, plugin_id):
|
|
"""Test that plugin manifest exists."""
|
|
super().test_manifest_exists(plugin_id)
|
|
|
|
def test_manifest_has_required_fields(self, plugin_id):
|
|
"""Test that manifest has all required fields."""
|
|
super().test_manifest_has_required_fields(plugin_id)
|
|
|
|
def test_plugin_can_be_loaded(self, plugin_id):
|
|
"""Test that plugin module can be loaded."""
|
|
super().test_plugin_can_be_loaded(plugin_id)
|
|
|
|
def test_plugin_class_exists(self, plugin_id):
|
|
"""Test that plugin class exists."""
|
|
super().test_plugin_class_exists(plugin_id)
|
|
|
|
def test_plugin_can_be_instantiated(self, plugin_id):
|
|
"""Test that plugin can be instantiated."""
|
|
super().test_plugin_can_be_instantiated(plugin_id)
|
|
|
|
def test_plugin_has_required_methods(self, plugin_id):
|
|
"""Test that plugin has required methods."""
|
|
super().test_plugin_has_required_methods(plugin_id)
|
|
|
|
def test_plugin_update_method(self, plugin_id):
|
|
"""Test that plugin update() method works."""
|
|
super().test_plugin_update_method(plugin_id)
|
|
|
|
def test_plugin_display_method(self, plugin_id):
|
|
"""Test that plugin display() method works."""
|
|
super().test_plugin_display_method(plugin_id)
|
|
|
|
def test_plugin_has_display_modes(self, plugin_id):
|
|
"""Test that plugin has display modes."""
|
|
manifest = self.load_plugin_manifest(plugin_id)
|
|
assert 'display_modes' in manifest
|
|
assert 'soccer_live' in manifest['display_modes']
|
|
assert 'soccer_recent' in manifest['display_modes']
|
|
assert 'soccer_upcoming' in manifest['display_modes']
|
|
|
|
def test_plugin_has_get_display_modes(self, plugin_id):
|
|
"""Test that plugin can return display modes."""
|
|
manifest = self.load_plugin_manifest(plugin_id)
|
|
plugin_dir = self.plugins_dir / plugin_id
|
|
entry_point = manifest['entry_point']
|
|
class_name = manifest['class_name']
|
|
|
|
module = self.plugin_loader.load_module(
|
|
plugin_id=plugin_id,
|
|
plugin_dir=plugin_dir,
|
|
entry_point=entry_point
|
|
)
|
|
|
|
plugin_class = self.plugin_loader.get_plugin_class(
|
|
plugin_id=plugin_id,
|
|
module=module,
|
|
class_name=class_name
|
|
)
|
|
|
|
config = self.base_config.copy()
|
|
plugin_instance = self.plugin_loader.instantiate_plugin(
|
|
plugin_id=plugin_id,
|
|
plugin_class=plugin_class,
|
|
config=config,
|
|
display_manager=self.mock_display_manager,
|
|
cache_manager=self.mock_cache_manager,
|
|
plugin_manager=self.mock_plugin_manager
|
|
)
|
|
|
|
# Check if plugin has get_display_modes method
|
|
if hasattr(plugin_instance, 'get_display_modes'):
|
|
modes = plugin_instance.get_display_modes()
|
|
assert isinstance(modes, list)
|
|
assert len(modes) > 0
|