mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 14:25:08 +00:00
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>
This commit is contained in:
@@ -9,7 +9,6 @@ from pathlib import Path
|
||||
|
||||
from src.plugin_system.operation_queue import PluginOperationQueue
|
||||
from src.plugin_system.operation_types import OperationType
|
||||
from src.plugin_system.state_manager import PluginStateManager
|
||||
from src.plugin_system.operation_history import OperationHistory
|
||||
|
||||
|
||||
@@ -22,12 +21,10 @@ class TestPluginOperationsIntegration(unittest.TestCase):
|
||||
|
||||
# Initialize components
|
||||
self.operation_queue = PluginOperationQueue(max_history=100)
|
||||
|
||||
self.state_manager = PluginStateManager(
|
||||
state_file=str(self.temp_dir / "state.json"),
|
||||
auto_save=True
|
||||
)
|
||||
|
||||
|
||||
# No state manager: installed / enabled / version are read from disk
|
||||
# and config.json, not from a plugin_state.json record.
|
||||
|
||||
self.operation_history = OperationHistory(
|
||||
history_file=str(self.temp_dir / "history.json"),
|
||||
max_records=100
|
||||
@@ -64,22 +61,15 @@ class TestPluginOperationsIntegration(unittest.TestCase):
|
||||
operation_id=operation_id
|
||||
)
|
||||
self.assertIsNotNone(history_id)
|
||||
|
||||
# Update state manager
|
||||
self.state_manager.set_plugin_installed(plugin_id, "1.0.0")
|
||||
|
||||
# Verify state
|
||||
state = self.state_manager.get_plugin_state(plugin_id)
|
||||
self.assertIsNotNone(state)
|
||||
self.assertEqual(state.version, "1.0.0")
|
||||
|
||||
|
||||
# Verify history
|
||||
history = self.operation_history.get_history(plugin_id=plugin_id)
|
||||
self.assertEqual([r.operation_type for r in history], ["install"])
|
||||
|
||||
def test_update_operation_flow(self):
|
||||
"""Test complete update operation flow."""
|
||||
plugin_id = "test-plugin"
|
||||
|
||||
# First, mark as installed
|
||||
self.state_manager.set_plugin_installed(plugin_id, "1.0.0")
|
||||
|
||||
# Enqueue update operation
|
||||
operation_id = self.operation_queue.enqueue_operation(
|
||||
OperationType.UPDATE,
|
||||
@@ -97,20 +87,14 @@ class TestPluginOperationsIntegration(unittest.TestCase):
|
||||
operation_id=operation_id
|
||||
)
|
||||
|
||||
# Update state
|
||||
self.state_manager.update_plugin_state(plugin_id, {"version": "2.0.0"})
|
||||
|
||||
# Verify state
|
||||
state = self.state_manager.get_plugin_state(plugin_id)
|
||||
self.assertEqual(state.version, "2.0.0")
|
||||
|
||||
# Verify history
|
||||
history = self.operation_history.get_history(plugin_id=plugin_id)
|
||||
self.assertEqual([r.operation_type for r in history], ["update"])
|
||||
|
||||
def test_uninstall_operation_flow(self):
|
||||
"""Test complete uninstall operation flow."""
|
||||
plugin_id = "test-plugin"
|
||||
|
||||
# First, mark as installed
|
||||
self.state_manager.set_plugin_installed(plugin_id, "1.0.0")
|
||||
|
||||
# Enqueue uninstall operation
|
||||
operation_id = self.operation_queue.enqueue_operation(
|
||||
OperationType.UNINSTALL,
|
||||
@@ -127,13 +111,10 @@ class TestPluginOperationsIntegration(unittest.TestCase):
|
||||
operation_id=operation_id
|
||||
)
|
||||
|
||||
# Update state - remove plugin state
|
||||
self.state_manager.remove_plugin_state(plugin_id)
|
||||
|
||||
# Verify state
|
||||
state = self.state_manager.get_plugin_state(plugin_id)
|
||||
self.assertIsNone(state)
|
||||
|
||||
# Verify history
|
||||
history = self.operation_history.get_history(plugin_id=plugin_id)
|
||||
self.assertEqual([r.operation_type for r in history], ["uninstall"])
|
||||
|
||||
def test_operation_history_tracking(self):
|
||||
"""Test that operations are tracked in history."""
|
||||
plugin_id = "test-plugin"
|
||||
|
||||
Reference in New Issue
Block a user