mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-04 18:28:06 +00:00
Found while sweeping devpi for issues. Nine of 27 installed plugins were reported degraded in the web UI -- including baseball-scoreboard and f1-scoreboard -- for using a documented core feature. The core reads three tuning keys out of each plugin's own config block: vegas_width_pct and vegas_overflow (vegas_mode/plugin_adapter.py) and vegas_max_width_screens (base_plugin.py). No plugin declares them, and 37 of the 42 published config schemas set "additionalProperties": false -- so schema validation reported them as violations. That is not just log noise. _validate_config_schema_soft sets `degraded` in the health tracker, which the web UI surfaces, so a user who tuned a core Vegas setting saw the plugin marked broken. The keys are stripped before validation. Fixing it plugin-side would mean 42 schema edits and 42 version bumps -- 42 store updates for a contract the core owns. Listed explicitly rather than matched on a `vegas_` prefix: vegas_mode is the opposite case, plugin-owned and declared in schemas, and a prefix rule would silently stop validating it. Verified on devpi: degraded went 9 of 27 -> 0 of 27, schema-mismatch warnings 9 -> 0, 22 plugins still load, no tracebacks. 800 core unit tests pass, 8 of them new -- including that a genuine violation is still reported, so the check has not been turned into a no-op, and that the caller's live config dict is never mutated. Claude-Session: https://claude.ai/code/session_01Udr6MfaFLUPhX5Fgo67Jf5 Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
99 lines
4.0 KiB
Python
99 lines
4.0 KiB
Python
"""The core's own tuning keys must not make a plugin look broken.
|
|
|
|
`vegas_width_pct`, `vegas_overflow` and `vegas_max_width_screens` are read by
|
|
the *core* out of each plugin's config block — `vegas_mode/plugin_adapter.py`
|
|
and `base_plugin.py`. No plugin declares them, and 37 of the 42 published
|
|
config schemas set `"additionalProperties": false`, so schema validation
|
|
reported them as violations.
|
|
|
|
That is not just log noise: `_validate_config_schema_soft` sets `degraded` in
|
|
the health tracker, which the web UI surfaces. Measured on a real device, **9
|
|
of 27 installed plugins** were flagged degraded purely for using a documented
|
|
core feature — including `baseball-scoreboard` and `f1-scoreboard`.
|
|
|
|
The fix strips those keys before validating. It deliberately does *not* match
|
|
on a `vegas_` prefix: `vegas_mode` is plugin-owned and declared in schemas, and
|
|
a prefix rule would silently stop validating it.
|
|
"""
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
from src.plugin_system.plugin_manager import PluginManager
|
|
|
|
|
|
STRICT_SCHEMA = {
|
|
"type": "object",
|
|
"additionalProperties": False,
|
|
"properties": {
|
|
"enabled": {"type": "boolean"},
|
|
"vegas_mode": {"type": "string"}, # plugin-owned, must stay validated
|
|
},
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def manager():
|
|
mgr = PluginManager.__new__(PluginManager) # skip the heavy constructor
|
|
mgr.logger = MagicMock()
|
|
mgr.schema_manager = MagicMock()
|
|
mgr._set_degraded_safe = MagicMock()
|
|
return mgr
|
|
|
|
|
|
class TestStripCoreOwnedKeys:
|
|
def test_removes_every_core_owned_key(self, manager):
|
|
cfg = {"enabled": True, "vegas_width_pct": 50,
|
|
"vegas_overflow": "wrap", "vegas_max_width_screens": 2}
|
|
assert manager._strip_core_owned_keys(cfg) == {"enabled": True}
|
|
|
|
def test_leaves_plugin_owned_vegas_mode_alone(self, manager):
|
|
"""A prefix rule would have eaten this one."""
|
|
cfg = {"enabled": True, "vegas_mode": "scroll"}
|
|
assert manager._strip_core_owned_keys(cfg) == cfg
|
|
|
|
def test_returns_the_same_object_when_nothing_to_strip(self, manager):
|
|
cfg = {"enabled": True}
|
|
assert manager._strip_core_owned_keys(cfg) is cfg
|
|
|
|
def test_does_not_mutate_the_caller_config(self, manager):
|
|
cfg = {"enabled": True, "vegas_width_pct": 50}
|
|
manager._strip_core_owned_keys(cfg)
|
|
assert "vegas_width_pct" in cfg, "the live plugin config was mutated"
|
|
|
|
def test_tolerates_a_non_dict(self, manager):
|
|
assert manager._strip_core_owned_keys(None) is None
|
|
|
|
|
|
class TestSoftValidation:
|
|
def _validate_with(self, manager, config, valid=True, errors=()):
|
|
manager.schema_manager.load_schema.return_value = STRICT_SCHEMA
|
|
manager.schema_manager.validate_config_against_schema.return_value = (
|
|
valid, list(errors))
|
|
manager._validate_config_schema_soft("baseball-scoreboard", config)
|
|
return manager.schema_manager.validate_config_against_schema.call_args
|
|
|
|
def test_core_keys_never_reach_the_validator(self, manager):
|
|
"""The regression: these keys reaching a strict schema is what flagged
|
|
9 of 27 plugins degraded."""
|
|
args = self._validate_with(
|
|
manager, {"enabled": True, "vegas_width_pct": 50})
|
|
validated = args[0][0]
|
|
assert "vegas_width_pct" not in validated
|
|
assert validated == {"enabled": True}
|
|
|
|
def test_plugin_owned_keys_still_reach_the_validator(self, manager):
|
|
args = self._validate_with(
|
|
manager, {"enabled": True, "vegas_mode": "scroll"})
|
|
assert args[0][0]["vegas_mode"] == "scroll"
|
|
|
|
def test_a_genuine_violation_is_still_reported(self, manager):
|
|
"""Stripping core keys must not turn the check into a no-op."""
|
|
self._validate_with(
|
|
manager, {"enabled": True, "typo_key": 1},
|
|
valid=False, errors=["Field root: 'typo_key' was unexpected"])
|
|
manager._set_degraded_safe.assert_called()
|
|
reason = manager._set_degraded_safe.call_args[0][1]
|
|
assert reason and "typo_key" in reason
|