ws: migrate push stream to data streaming
- daemon: send full snapshot on connect; versions/tick now carry the full state of one subsystem (subsystem + data); no legacy updated/subsystems payloads; refresh_state and POST /status/refresh broadcast per-subsystem versions with data - client: modelSet() patches models in place; onMessage/topic refresh retired; 3s initial-load fallback via new POST /api/status/refresh - schema: lib/schema.py TypedDicts + hoover/schema.js defaults + docs/state-model.md as single source of truth for state shapes - system: poll at 1s, volatile metrics registered, dashboard uses a dedicated system model (status model removed) - firewall: refuse to strip both https and ssh from the default zone (409, force override via UI confirm); set_zone_services persists services to the declarative config; collector exposes default_zone - UI: pages migrate to flat state shapes; post-mutation modelFetch refreshes removed (WS delta covers it) - tests: ws snapshot/delta/broadcast, refresh-state, schema types, model-set/js ws handler and reconnect fallback
This commit is contained in:
+17
-9
@@ -14,6 +14,11 @@ class TestPollIntervals:
|
||||
assert _POLL_INTERVALS["wireguard"] == 10
|
||||
assert _POLL_INTERVALS["dnsmasq"] == 10
|
||||
assert _POLL_INTERVALS["networkd"] == 10
|
||||
# Phase 5: real-time system metrics poll at 1s.
|
||||
assert _POLL_INTERVALS["system"] == 1
|
||||
# nginx/acme derive from config files; poll for drift self-heal.
|
||||
assert _POLL_INTERVALS["nginx"] == 60
|
||||
assert _POLL_INTERVALS["acme"] == 300
|
||||
|
||||
def test_env_override(self):
|
||||
"""VACUUM_WALL_POLL_INTERVALS env var can override values."""
|
||||
@@ -33,28 +38,31 @@ class TestPollIntervals:
|
||||
|
||||
class TestBroadcastTick:
|
||||
def test_sends_tick_message(self):
|
||||
from daemon.server import _ws_subscribers, broadcast_tick
|
||||
import daemon.server as server
|
||||
|
||||
mock_ws = AsyncMock()
|
||||
mock_ws.send_str = AsyncMock()
|
||||
_ws_subscribers.add(mock_ws)
|
||||
server._ws_subscribers.add(mock_ws)
|
||||
try:
|
||||
asyncio.run(broadcast_tick(["firewall", "wireguard"]))
|
||||
with patch.object(server.state_store, "get", return_value={"up": True}):
|
||||
asyncio.run(server.broadcast_tick("firewall"))
|
||||
mock_ws.send_str.assert_called_once()
|
||||
sent = json.loads(mock_ws.send_str.call_args[0][0])
|
||||
assert sent["type"] == "tick"
|
||||
assert sent["subsystems"] == ["firewall", "wireguard"]
|
||||
assert sent["subsystem"] == "firewall"
|
||||
assert sent["data"] == {"up": True}
|
||||
finally:
|
||||
_ws_subscribers.discard(mock_ws)
|
||||
server._ws_subscribers.discard(mock_ws)
|
||||
|
||||
def test_prunes_dead_subscribers(self):
|
||||
from daemon.server import _ws_subscribers, broadcast_tick
|
||||
import daemon.server as server
|
||||
|
||||
mock_ws = AsyncMock()
|
||||
mock_ws.send_str = AsyncMock(side_effect=Exception("broken"))
|
||||
_ws_subscribers.add(mock_ws)
|
||||
asyncio.run(broadcast_tick(["firewall"]))
|
||||
assert mock_ws not in _ws_subscribers
|
||||
server._ws_subscribers.add(mock_ws)
|
||||
with patch.object(server.state_store, "get", return_value={"up": True}):
|
||||
asyncio.run(server.broadcast_tick("firewall"))
|
||||
assert mock_ws not in server._ws_subscribers
|
||||
|
||||
|
||||
class TestPollTasks:
|
||||
|
||||
Reference in New Issue
Block a user