/
/
/
1"""Tests for the URL used to reach the webserver's own API from this host."""
2
3from typing import TYPE_CHECKING, cast
4from unittest.mock import MagicMock
5
6import pytest
7
8from music_assistant.controllers.webserver.controller import CONF_BASE_URL, WebserverController
9
10if TYPE_CHECKING:
11 from music_assistant_models.config_entries import CoreConfig
12
13
14@pytest.fixture
15def webserver(mock_mass: MagicMock) -> WebserverController:
16 """Create a WebserverController carrying the state that setup() resolves."""
17 webserver = WebserverController(mock_mass)
18 config = MagicMock()
19 config.get_value.return_value = False
20 webserver.config = cast("CoreConfig", config)
21 webserver.publish_port = 8095
22 return webserver
23
24
25@pytest.mark.parametrize(
26 ("bind_ip", "publish_ip", "expected"),
27 [
28 # pinned to a single interface: loopback would not reach the server
29 ("192.168.1.5", "192.168.1.5", "http://192.168.1.5:8095"),
30 ("fd00::5", "fd00::5", "http://[fd00::5]:8095"),
31 # wildcard bind: loopback reaches the server, whatever is advertised.
32 # a publish_ip that only exists outside a container/NAT must never be dialed
33 ("0.0.0.0", "203.0.113.10", "http://127.0.0.1:8095"),
34 ("::", "192.168.1.5", "http://127.0.0.1:8095"),
35 ("0.0.0.0", "fd00::1", "http://[::1]:8095"),
36 ("::", "fd00::1", "http://[::1]:8095"),
37 ("", "192.168.1.5", "http://127.0.0.1:8095"),
38 (None, "192.168.1.5", "http://127.0.0.1:8095"),
39 ],
40)
41def test_url_follows_the_bind_address(
42 webserver: WebserverController,
43 bind_ip: str | None,
44 publish_ip: str,
45 expected: str,
46) -> None:
47 """Verify the URL resolves to an address that exists on this host."""
48 webserver.bind_ip = bind_ip
49 webserver.publish_ip = publish_ip
50
51 assert webserver.internal_base_url == expected
52
53
54def test_url_ignores_a_configured_base_url(webserver: WebserverController) -> None:
55 """Verify an external base URL is not dialed back in through DNS and a reverse proxy."""
56 cast("MagicMock", webserver.config).get_value.side_effect = lambda key, default=None: {
57 CONF_BASE_URL: "https://ma.example.com",
58 }.get(key, default)
59 webserver.bind_ip = "0.0.0.0"
60 webserver.publish_ip = "192.168.1.5"
61
62 assert webserver.base_url == "https://ma.example.com"
63 assert webserver.internal_base_url == "http://127.0.0.1:8095"
64
65
66def test_url_uses_tls_when_ssl_is_active(webserver: WebserverController) -> None:
67 """Verify the scheme matches what the webserver serves."""
68 webserver._ssl_active = True
69 webserver.bind_ip = "0.0.0.0"
70 webserver.publish_ip = "192.168.1.5"
71
72 assert webserver.internal_base_url == "https://127.0.0.1:8095"
73