7beba44b4b
- Add lib/state.py: in-memory state store with subsystem collectors (firewall, dnsmasq, nginx, acme, wireguard) - Refactor all handlers: read from state on GET, call refresh_state() after mutations instead of invoking subprocesses per request - daemon/server.py: add refresh_state(), /status/all, /status/refresh; populate state at startup - webui/api/certs.py: async step-by-step ACME issuance (validate, issue with request_id, poll status) replacing blocking endpoint - webui/server.py: render pages from state instead of direct lib calls - Update templates, JS for async cert issuance with polling UI - Update tests for state-based mocking; add test_state.py - Fix SIM105 lint issue (contextlib.suppress) - Add TODO.md with certificate issuance issue tracking Resolves: WebUI 30s timeout freeze during cert issuance (Problem 1)
98 lines
2.7 KiB
Python
98 lines
2.7 KiB
Python
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
from webui.server import app
|
|
|
|
app.config["TESTING"] = True
|
|
return app.test_client()
|
|
|
|
|
|
class TestTemplateFilters:
|
|
@pytest.fixture
|
|
def env(self):
|
|
from webui.server import app
|
|
|
|
return app.jinja_env
|
|
|
|
def test_timestamp_filter_valid(self, env):
|
|
result = env.filters["timestamp"]("2026-04-01T12:00:00Z")
|
|
assert "2026-04-01" in result
|
|
|
|
def test_timestamp_filter_empty(self, env):
|
|
assert env.filters["timestamp"]("") == ""
|
|
assert env.filters["timestamp"](None) == ""
|
|
|
|
def test_timestamp_filter_invalid(self, env):
|
|
result = env.filters["timestamp"]("not-a-date")
|
|
assert result == "not-a-date"
|
|
|
|
def test_bytes_filter_zero(self, env):
|
|
assert env.filters["bytes"](0) == "0.0 B"
|
|
|
|
def test_bytes_filter_kb(self, env):
|
|
result = env.filters["bytes"](1536)
|
|
assert "KB" in result
|
|
|
|
def test_bytes_filter_mb(self, env):
|
|
result = env.filters["bytes"](1500000)
|
|
assert "MB" in result
|
|
|
|
def test_bytes_filter_negative(self, env):
|
|
assert env.filters["bytes"](-1) == "0 B"
|
|
|
|
def test_bytes_filter_invalid(self, env):
|
|
assert env.filters["bytes"]("not-a-number") == "not-a-number"
|
|
|
|
def test_duration_filter_zero(self, env):
|
|
assert env.filters["duration"](0) == "0s"
|
|
|
|
def test_duration_filter_seconds(self, env):
|
|
assert env.filters["duration"](65) == "1m 5s"
|
|
|
|
def test_duration_filter_hours(self, env):
|
|
result = env.filters["duration"](3661)
|
|
assert "1h" in result
|
|
|
|
def test_duration_filter_days(self, env):
|
|
result = env.filters["duration"](90000)
|
|
assert "1d" in result
|
|
|
|
def test_duration_filter_invalid(self, env):
|
|
assert env.filters["duration"]("bad") == "bad"
|
|
|
|
def test_json_pretty_filter(self, env):
|
|
result = env.filters["json_pretty"]({"key": "value"})
|
|
assert '{"key": "value"}' in result or "key" in result
|
|
|
|
|
|
class TestSafelyHelper:
|
|
def test_returns_result(self):
|
|
from webui.server import _safely
|
|
|
|
result = _safely(lambda: 42)
|
|
assert result == 42
|
|
|
|
def test_returns_default_on_exception(self):
|
|
from webui.server import _safely
|
|
|
|
result = _safely(lambda: 1 / 0, default=None)
|
|
assert result is None
|
|
|
|
def test_returns_custom_default(self):
|
|
from webui.server import _safely
|
|
|
|
result = _safely(lambda: 1 / 0, default="fallback")
|
|
assert result == "fallback"
|
|
|
|
|
|
class TestPageRoutes:
|
|
@patch("webui.server.get")
|
|
def test_dashboard_no_crash(self, mock_get, client):
|
|
mock_get.return_value = {}
|
|
resp = client.get("/dashboard")
|
|
assert resp.status_code == 200
|