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:
@@ -40,6 +40,11 @@ def _ne(func, **kw):
|
||||
return _patch(f"webui.api.network.{func}", **kw)
|
||||
|
||||
|
||||
def _st(func, **kw):
|
||||
"""Patch daemon.client.{func} in the status blueprint namespace."""
|
||||
return _patch(f"webui.api.status.{func}", **kw)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
from flask import Flask
|
||||
@@ -883,3 +888,55 @@ class TestNetworkApplyAll:
|
||||
mock_post.side_effect = RuntimeError("apply failed")
|
||||
resp = client.post("/api/network/apply")
|
||||
assert resp.status_code == 500
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Status
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def status_client():
|
||||
from flask import Flask
|
||||
|
||||
from webui.api.status import bp as status_bp
|
||||
|
||||
app = Flask(__name__)
|
||||
app.register_blueprint(status_bp, url_prefix="/api/status")
|
||||
return app.test_client()
|
||||
|
||||
|
||||
class TestStatusRefresh:
|
||||
def test_filtered_subsystems_passed_through(self, status_client):
|
||||
"""The subsystem body is forwarded to the daemon POST endpoint."""
|
||||
from daemon.iface import POST_STATUS_REFRESH
|
||||
|
||||
with _st("post") as mock_post:
|
||||
mock_post.return_value = {"firewall": {"zones": {}}}
|
||||
resp = status_client.post(
|
||||
"/api/status/refresh", json={"subsystems": ["firewall"]}
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data["ok"] is True
|
||||
assert data["data"] == {"firewall": {"zones": {}}}
|
||||
mock_post.assert_called_once_with(
|
||||
POST_STATUS_REFRESH, {"subsystems": ["firewall"]}
|
||||
)
|
||||
|
||||
def test_empty_body_forwards_empty_dict(self, status_client):
|
||||
"""An empty body becomes {} (daemon-side 'all subsystems' default)."""
|
||||
from daemon.iface import POST_STATUS_REFRESH
|
||||
|
||||
with _st("post") as mock_post:
|
||||
mock_post.return_value = {}
|
||||
resp = status_client.post("/api/status/refresh")
|
||||
assert resp.status_code == 200
|
||||
mock_post.assert_called_once_with(POST_STATUS_REFRESH, {})
|
||||
|
||||
@_st("post")
|
||||
def test_runtime_error(self, mock_post, status_client):
|
||||
mock_post.side_effect = RuntimeError("no daemon")
|
||||
resp = status_client.post("/api/status/refresh", json={})
|
||||
assert resp.status_code == 500
|
||||
assert resp.get_json()["ok"] is False
|
||||
|
||||
Reference in New Issue
Block a user