8f10353f0c
CI / lint (push) Has been cancelled
CI / js-syntax (push) Successful in 10m24s
CI / deps-audit (push) Has been cancelled
CI / test (push) Failing after 24m55s
CI / sast-bandit (push) Failing after 11m13s
CI / trivy (push) Failing after 9m32s
Docker Publish / build-and-push (push) Failing after 34m3s
117 lines
4.2 KiB
Python
117 lines
4.2 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.core import settings as settings_mod
|
|
from app.main import _is_host_request, _is_loopback, app
|
|
|
|
MOBILE_UA = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) Mobile/15E148"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"host,expected",
|
|
[
|
|
("127.0.0.1", True),
|
|
("::1", True),
|
|
("localhost", True),
|
|
("::ffff:127.0.0.1", True),
|
|
("127.0.1.1", True),
|
|
("192.168.1.14", False),
|
|
("10.0.0.5", False),
|
|
("", False),
|
|
(None, False),
|
|
],
|
|
)
|
|
def test_is_loopback(host, expected):
|
|
assert _is_loopback(host) is expected
|
|
|
|
|
|
def test_host_request_recognizes_own_lan_ip(monkeypatch):
|
|
# The host reaching itself via its LAN address must count as local, so
|
|
# turning network access off never cuts the host off from its own server.
|
|
monkeypatch.setattr("app.main._local_ips", lambda: frozenset({"192.168.1.14"}))
|
|
assert _is_host_request("192.168.1.14") is True # the host's own IP
|
|
assert _is_host_request("127.0.0.1") is True # loopback
|
|
assert _is_host_request("192.168.1.99") is False # a different device
|
|
|
|
|
|
def test_default_is_off_in_desktop_mode(monkeypatch):
|
|
# Desktop keeps network off; the user opts in via the UI toggle.
|
|
monkeypatch.delenv("STEMDECK_ALLOW_NETWORK", raising=False)
|
|
monkeypatch.setenv("STEMDECK_DESKTOP", "1")
|
|
assert settings_mod._default_allow_network() is False
|
|
|
|
|
|
def test_default_is_on_in_server_mode(monkeypatch):
|
|
# Server/Docker deployments open the gate by default.
|
|
monkeypatch.delenv("STEMDECK_ALLOW_NETWORK", raising=False)
|
|
monkeypatch.delenv("STEMDECK_DESKTOP", raising=False)
|
|
assert settings_mod._default_allow_network() is True
|
|
|
|
|
|
def test_env_var_pre_enables(monkeypatch):
|
|
monkeypatch.setenv("STEMDECK_ALLOW_NETWORK", "1")
|
|
assert settings_mod._default_allow_network() is True
|
|
|
|
|
|
def test_runtime_settings_round_trip_and_clamp():
|
|
with TestClient(app) as c:
|
|
r = c.post("/api/settings", json={"max_duration_sec": 600, "video_max_height": 1080})
|
|
assert r.status_code == 200
|
|
body = r.json()
|
|
assert body["max_duration_sec"] == 600
|
|
assert body["video_max_height"] == 1080
|
|
# GET reflects the new values.
|
|
assert c.get("/api/settings").json()["max_duration_sec"] == 600
|
|
|
|
# Out-of-range values are clamped, not rejected.
|
|
assert settings_mod.set_max_duration_sec(5) == 60 # floor
|
|
assert settings_mod.set_max_duration_sec(99999) == 1200 # ceiling = 20 min
|
|
assert settings_mod.set_video_max_height(99999) == 2160 # ceil
|
|
|
|
|
|
def test_port_default_and_clamp():
|
|
assert settings_mod.get_port() == 8000 # default
|
|
with TestClient(app) as c:
|
|
assert c.post("/api/settings", json={"port": 9000}).json()["port"] == 9000
|
|
assert settings_mod.set_port(80) == 1024 # floor (privileged ports rejected)
|
|
assert settings_mod.set_port(70000) == 65535 # ceil
|
|
|
|
|
|
def test_settings_reject_non_integer():
|
|
with TestClient(app) as c:
|
|
assert c.post("/api/settings", json={"max_duration_sec": "abc"}).status_code == 422
|
|
|
|
|
|
def test_gate_blocks_non_loopback_when_off():
|
|
settings_mod.set_allow_network(False)
|
|
# TestClient's client host ("testclient") is treated as non-loopback.
|
|
with TestClient(app) as c:
|
|
r = c.get("/", headers={"user-agent": MOBILE_UA})
|
|
assert r.status_code == 403
|
|
|
|
|
|
def test_gate_allows_everyone_when_on():
|
|
settings_mod.set_allow_network(True)
|
|
with TestClient(app) as c:
|
|
assert c.get("/api/health").status_code == 200
|
|
|
|
|
|
def test_loopback_always_allowed_even_when_off(monkeypatch):
|
|
settings_mod.set_allow_network(False)
|
|
monkeypatch.setattr("app.main._is_loopback", lambda _host: True)
|
|
with TestClient(app) as c:
|
|
assert c.get("/api/health").status_code == 200
|
|
|
|
|
|
def test_post_toggles_off_then_blocks():
|
|
settings_mod.set_allow_network(True) # so the non-loopback client can reach POST
|
|
with TestClient(app) as c:
|
|
r = c.post("/api/settings", json={"allow_network": False})
|
|
assert r.status_code == 200
|
|
assert r.json()["allow_network"] is False
|
|
# Now off → a non-loopback client is blocked from everything.
|
|
with TestClient(app) as c:
|
|
assert c.get("/api/settings").status_code == 403
|