Files
LEDMatrix/test/test_plugin_system_utf8_reads.py
ChuckandClaude Opus 5.5 b09434a418 refactor(plugins): the display publishes plugin runtime state; retire plugin_state.json (#690)
Stage 2 of the web plugin catalog, after #688.

- The display publishes a plugin runtime snapshot (plugin_runtime.py) to
  the shared cache: per plugin loaded, lifecycle state, a short redacted
  error summary, the version it loaded and when, plus published_at /
  stale_after / running. Written on change (throttled to 10 s; the
  RUNNING/ENABLED flip of an ordinary update is not a change) and once a
  minute otherwise; cleanup() publishes running: false.
- The web reads it back and restores loaded / state / error_info in
  /api/v3/plugins/installed (plus loaded_version, loaded_at and
  data.runtime). Only a live snapshot counts; stale, stopped or missing
  answers null and says which.
- data/plugin_state.json is retired: every reader and writer moved to
  config + disk (desired) or the snapshot (observed). Nothing in it was
  non-derivable, so nothing is migrated and an existing file is left
  unread. The web-side PluginStateManager (state_manager.py) is removed;
  the display's plugin_state.PluginStateManager is the only state machine.
- StateReconciliation compares config + disk with the snapshot, reporting
  enabled-but-not-loaded and older-version-loaded as no_action findings.
- Backups list installed manifests with enabled from config.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 10:48:14 -04:00

50 lines
1.9 KiB
Python

"""Plugin-system JSON files are read as UTF-8, whatever the locale says.
store_manager (install_from_url's manifest, the secrets file) opened text
files without an encoding, so the platform default applied. (So did the
retired plugin_state.json reader.) A manifest written in UTF-8 with a non-ASCII
name then read as mojibake -- or raised UnicodeDecodeError -- on a host
whose locale encoding is not UTF-8 (Windows' cp1252; a Pi with LANG=C).
On a UTF-8 host these pass either way; they fail on old code where the
default encoding differs.
"""
import json
import pytest
from src.plugin_system.store_manager import PluginStoreManager
NAME = "Météo Á" # "Á" is C3 81 in UTF-8; 0x81 is undefined in cp1252
@pytest.fixture
def store(tmp_path, monkeypatch):
mgr = PluginStoreManager(
plugins_dir=str(tmp_path / "plugins"),
uninstalled_registry_path=str(tmp_path / "uninstalled.json"))
mgr.plugins_dir.mkdir(parents=True, exist_ok=True)
monkeypatch.setattr(mgr, "_install_dependencies", lambda *a, **k: True)
def fake_clone(repo_url, target, branches):
target = type(mgr.plugins_dir)(target)
target.mkdir(parents=True, exist_ok=True)
manifest = {"id": "meteo", "name": NAME, "class_name": "P",
"display_modes": ["meteo"], "version": "1.0.0"}
(target / "manifest.json").write_bytes(
json.dumps(manifest, ensure_ascii=False).encode("utf-8"))
(target / "manager.py").write_text("class P: pass\n")
return "main"
monkeypatch.setattr(mgr, "_install_via_git", fake_clone)
return mgr
def test_install_from_url_reads_a_utf8_manifest(store):
result = store.install_from_url("https://github.com/x/y")
assert result["success"] is True
assert result["name"] == NAME
written = json.loads(
(store.plugins_dir / "meteo" / "manifest.json").read_bytes().decode("utf-8"))
assert written["name"] == NAME