mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-05 23:05:10 +00:00
fix(plugins): address review -- no manifest fallback for live plugins, keep mode names whole, send registered spelling
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
6dbec2e0b1
commit
b440a27cdb
@@ -244,6 +244,8 @@ class PluginCatalog:
|
||||
if live and any(m.lower() == wanted for m in live):
|
||||
return plugin_id
|
||||
for plugin_id, manifest in manifests.items():
|
||||
if self._live_display_modes(plugin_id):
|
||||
continue # the display's list is the truth for this plugin
|
||||
modes = manifest.get('display_modes')
|
||||
if isinstance(modes, list) and any(
|
||||
isinstance(m, str) and m.lower() == wanted for m in modes):
|
||||
|
||||
@@ -161,7 +161,9 @@ def _published_modes(modes: Any) -> Optional[List[str]]:
|
||||
"""The registered display modes as a snapshot carries them, or None."""
|
||||
if not isinstance(modes, list):
|
||||
return None
|
||||
return [_clip(m, _ID_CHARS) for m in modes[:_MAX_MODES] if isinstance(m, str)]
|
||||
# A name is a key the display matches exactly: drop one too long to
|
||||
# carry whole rather than clip it into a different name.
|
||||
return [m for m in modes if isinstance(m, str) and len(m) <= _ID_CHARS][:_MAX_MODES]
|
||||
|
||||
|
||||
def build_runtime_snapshot(state_manager: Any, *, started_at: float,
|
||||
|
||||
@@ -7,7 +7,7 @@ manifest.json off disk and reimplemented PluginManager's own fallbacks.
|
||||
"""
|
||||
|
||||
import json
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -155,3 +155,14 @@ class TestOneBadConfigSectionDoesNotBlankTheList:
|
||||
side_effect=RuntimeError("GET https://x/y?api_key=SEC123 failed"))
|
||||
body = api_v3_client.get('/api/v3/display/modes').get_json()
|
||||
assert 'SEC123' not in json.dumps(body)
|
||||
|
||||
|
||||
class TestOnDemandUsesTheRegisteredSpelling:
|
||||
def test_a_mode_differing_in_case_is_sent_as_registered(self, client):
|
||||
with patch('web_interface.blueprints.api_v3.display._deliver_on_demand',
|
||||
return_value=('socket', None)) as deliver:
|
||||
response = client.post('/api/v3/display/on-demand/start',
|
||||
json={'plugin_id': 'football-scoreboard',
|
||||
'mode': 'NFL_LIVE', 'start_service': False})
|
||||
assert response.status_code == 200, response.get_json()
|
||||
assert deliver.call_args.args[0]['mode'] == 'nfl_live'
|
||||
|
||||
@@ -165,6 +165,12 @@ class TestSnapshotAndView:
|
||||
published = snapshot["plugins"]["soccer-scoreboard"]["modes"]
|
||||
assert len(published) == rt._MAX_MODES
|
||||
|
||||
def test_a_mode_name_is_kept_whole_or_dropped(self):
|
||||
long_mode = "x" * (rt._ID_CHARS + 1)
|
||||
snapshot = build_runtime_snapshot(_loaded_states(["ok", long_mode]),
|
||||
started_at=1.0, now=self.NOW)
|
||||
assert snapshot["plugins"]["soccer-scoreboard"]["modes"] == ["ok"]
|
||||
|
||||
def test_non_strings_from_a_hand_made_snapshot_are_dropped(self):
|
||||
snapshot = {"schema": rt.SNAPSHOT_SCHEMA, "running": True,
|
||||
"published_at": self.NOW, "plugins": {
|
||||
@@ -202,6 +208,13 @@ class TestCatalog:
|
||||
assert catalog.get_plugin_display_modes("clock-simple") == ["clock"]
|
||||
assert catalog.find_plugin_for_mode("clock") == "clock-simple"
|
||||
|
||||
def test_a_mode_the_display_dropped_does_not_resolve_by_manifest(
|
||||
self, plugins_dir, shared_cache):
|
||||
display_cache, web_cache = shared_cache
|
||||
PluginRuntimePublisher(display_cache, _loaded_states(CUSTOM)).tick()
|
||||
catalog = self._catalog(plugins_dir, web_cache)
|
||||
assert catalog.find_plugin_for_mode("soccer_eng.1_live") is None
|
||||
|
||||
def test_a_stopped_display_falls_back_to_manifests(self, plugins_dir, shared_cache):
|
||||
display_cache, web_cache = shared_cache
|
||||
publisher = PluginRuntimePublisher(display_cache, _loaded_states(REGISTERED))
|
||||
|
||||
@@ -279,6 +279,15 @@ def start_on_demand_display():
|
||||
if not resolved_plugin:
|
||||
return jsonify({'status': 'error', 'message': f'Mode {resolved_mode} not found'}), 404
|
||||
|
||||
# The display matches mode names exactly: pass the registered spelling
|
||||
# when the caller's differs only in case.
|
||||
if api_v3.plugin_catalog and resolved_plugin and resolved_mode:
|
||||
wanted = resolved_mode.strip().lower()
|
||||
for registered in api_v3.plugin_catalog.get_plugin_display_modes(resolved_plugin):
|
||||
if isinstance(registered, str) and registered.lower() == wanted:
|
||||
resolved_mode = registered
|
||||
break
|
||||
|
||||
# On-demand works with disabled plugins: the running display loads one
|
||||
# for the session and unloads it afterwards, leaving config.json alone
|
||||
# (DisplayController._load_plugin_for_on_demand). Logged for debugging.
|
||||
|
||||
Reference in New Issue
Block a user