Files
site/WESP_REL/tests/test_network_settings_api.py
T
2026-07-17 12:57:18 +03:00

186 lines
7.9 KiB
Python

import json
import os
import tempfile
import unittest
from unittest.mock import patch
from werkzeug.security import generate_password_hash
from app import create_app, db
from app.models import WebUser
from config import TestingConfig
class NetworkSettingsApiTests(unittest.TestCase):
def setUp(self) -> None:
self._tmp = tempfile.mkdtemp(prefix="wesp-network-settings-")
self._net_path = os.path.join(self._tmp, "wesp_network_settings.json")
self._sec_path = os.path.join(self._tmp, "wesp_security_settings.json")
self._periph_path = os.path.join(self._tmp, "wesp-peripherals.jsonl")
os.environ["WESP_NETWORK_SETTINGS_PATH"] = self._net_path
os.environ["WESP_SECURITY_SETTINGS_PATH"] = self._sec_path
os.environ["WESP_ADMIN_PERIPHERAL_LOG_PATH"] = self._periph_path
self._prev_hostname = os.environ.pop("WESP_NETWORK_LOCAL_HOSTNAME", None)
self._prev_pub = os.environ.pop("WESP_KIOSK_PUBLIC_BASE_URL", None)
self._prev_mdns = os.environ.pop("WESP_MDNS_ENABLED", None)
class C(TestingConfig):
BASE_DIR = self._tmp
DATA_DIR = os.path.join(self._tmp, "data")
SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.join(self._tmp, 'recipes_test.db')}"
SQLALCHEMY_BINDS = {"reports": f"sqlite:///{os.path.join(self._tmp, 'reports_test.db')}"}
AUTH_LOGIN = "admin"
AUTH_PASSWORD = "admin-secret"
SECRET_KEY = "unit-test-secret-key-not-default-32chars"
self.app = create_app(C)
self.app.config["WESP_ADMIN_PERIPHERAL_LOG_PATH"] = self._periph_path
self.client = self.app.test_client()
self.ctx = self.app.app_context()
self.ctx.push()
os.makedirs(C.DATA_DIR, exist_ok=True)
db.create_all()
db.session.add(
WebUser(
login="admin",
password_hash=generate_password_hash("admin-secret"),
is_superuser=True,
)
)
db.session.commit()
self._login()
def tearDown(self) -> None:
db.session.remove()
db.drop_all()
self.ctx.pop()
os.environ.pop("WESP_NETWORK_SETTINGS_PATH", None)
os.environ.pop("WESP_SECURITY_SETTINGS_PATH", None)
os.environ.pop("WESP_ADMIN_PERIPHERAL_LOG_PATH", None)
if self._prev_hostname is not None:
os.environ["WESP_NETWORK_LOCAL_HOSTNAME"] = self._prev_hostname
else:
os.environ.pop("WESP_NETWORK_LOCAL_HOSTNAME", None)
if self._prev_pub is not None:
os.environ["WESP_KIOSK_PUBLIC_BASE_URL"] = self._prev_pub
else:
os.environ.pop("WESP_KIOSK_PUBLIC_BASE_URL", None)
if self._prev_mdns is not None:
os.environ["WESP_MDNS_ENABLED"] = self._prev_mdns
else:
os.environ.pop("WESP_MDNS_ENABLED", None)
def _login(self) -> None:
r = self.client.post("/api/auth/login", json={"login": "admin", "password": "admin-secret"})
self.assertEqual(r.status_code, 200, r.get_data(as_text=True))
def test_get_network_defaults(self) -> None:
r = self.client.get("/api/admin/network-settings")
self.assertEqual(r.status_code, 200, r.get_data(as_text=True))
body = r.get_json() or {}
net = body.get("network") or {}
self.assertEqual(net.get("default_local_hostname"), "komton_srv_1.local")
self.assertIn("komton_srv_1.local", net.get("local_hostname") or "")
self.assertIn("detected", net)
@patch("app.routes.admin.reload_mdns")
def test_patch_network_settings_persists_and_applies(self, mock_reload) -> None:
r = self.client.patch(
"/api/admin/network-settings",
json={
"local_hostname": "komton_srv_2.local",
"public_base_url": "http://komton_srv_2.local",
"mdns_enabled": True,
},
)
self.assertEqual(r.status_code, 200, r.get_data(as_text=True))
self.assertEqual(self.app.config.get("KIOSK_PUBLIC_BASE_URL"), "http://komton_srv_2.local")
self.assertEqual(self.app.config.get("WESP_NETWORK_LOCAL_HOSTNAME"), "komton_srv_2.local")
self.assertTrue(self.app.config.get("WESP_MDNS_ENABLED"))
mock_reload.assert_called_once()
with open(self._net_path, encoding="utf-8") as f:
stored = json.load(f)
self.assertEqual(stored.get("local_hostname"), "komton_srv_2.local")
@patch("app.routes.admin.reload_mdns")
def test_migrate_public_url_from_security_file(self, _mock_reload) -> None:
with open(self._sec_path, "w", encoding="utf-8") as f:
json.dump({"kiosk_public_base_url": "http://legacy.local:5000"}, f)
r = self.client.get("/api/admin/network-settings")
net = (r.get_json() or {}).get("network") or {}
self.assertEqual(net.get("public_base_url"), "http://legacy.local:5000")
@patch("app.routes.admin.reload_mdns")
def test_patch_writes_network_journal(self, mock_reload) -> None:
r = self.client.patch(
"/api/admin/network-settings",
json={"local_hostname": "komton_srv_1.local", "mdns_enabled": True},
)
self.assertEqual(r.status_code, 200, r.get_data(as_text=True))
self.assertTrue(os.path.isfile(self._periph_path))
with open(self._periph_path, encoding="utf-8") as f:
body = f.read()
self.assertIn("network_settings_saved", body)
ev = self.client.get("/api/admin/peripheral-events?component=network")
self.assertEqual(ev.status_code, 200)
events = (ev.get_json() or {}).get("events") or []
self.assertTrue(any(e.get("code") == "network_settings_saved" for e in events))
@patch("app.routes.admin.reload_mdns")
def test_invalid_hostname_rejected(self, _mock_reload) -> None:
r = self.client.patch(
"/api/admin/network-settings",
json={"local_hostname": "1"},
)
self.assertEqual(r.status_code, 400)
self.assertIn("цифр", (r.get_json() or {}).get("message", ""))
@patch("app.routes.admin.reload_mdns")
def test_patch_public_url_only_skips_mdns_reload(self, mock_reload) -> None:
r = self.client.patch(
"/api/admin/network-settings",
json={"public_base_url": "http://komton_srv_1.local"},
)
self.assertEqual(r.status_code, 200, r.get_data(as_text=True))
mock_reload.assert_not_called()
@patch("app.routes.admin.reload_mdns")
def test_clear_network_journal_only(self, _mock_reload) -> None:
from app.services.admin_peripheral_monitor import append_peripheral_event
append_peripheral_event(
self.app,
component="network",
code="mdns_started",
message="net event",
force=True,
)
append_peripheral_event(
self.app,
component="hx711",
code="hx711_read_error",
message="hw event",
force=True,
)
r = self.client.delete("/api/admin/peripheral-events?component=network")
self.assertEqual(r.status_code, 200, r.get_data(as_text=True))
ev = self.client.get("/api/admin/peripheral-events?component=network")
self.assertEqual(ev.status_code, 200)
net_events = (ev.get_json() or {}).get("events") or []
self.assertEqual(net_events, [])
all_ev = self.client.get("/api/admin/peripheral-events?limit=20")
codes = [e.get("code") for e in (all_ev.get_json() or {}).get("events") or []]
self.assertIn("hx711_read_error", codes)
def test_env_lock_blocks_patch(self) -> None:
os.environ["WESP_KIOSK_PUBLIC_BASE_URL"] = "http://env.local:5000"
r = self.client.patch(
"/api/admin/network-settings",
json={"public_base_url": "http://other.local:5000"},
)
self.assertEqual(r.status_code, 400)
if __name__ == "__main__":
unittest.main()