"""Unit tests for ``_get_webhook_uri`` priority order and the ``webhook_auth_pair`` registration helper. Cloud deployments register the webhook URI returned by this function with Nextcloud. ECS Fargate also exposes ``/.dockerenv``, so an explicit public URL must win over the docker auto-detection branch. The URL fields are read via dynaconf (``Settings``), so tests patch ``get_settings`` directly. The docker-detection markers (``/.dockerenv``, ``DOCKER_CONTAINER``, ``NEXTCLOUD_MCP_SERVICE_NAME``, ``NEXTCLOUD_MCP_PORT``) remain on ``os.getenv`` and are exercised via env-var monkeypatching. """ import pytest from nextcloud_mcp_server.auth import webhook_routes from nextcloud_mcp_server.auth.webhook_routes import ( _get_webhook_uri, webhook_auth_pair, ) from nextcloud_mcp_server.config import Settings DOCKER_ENV_VARS = ( "NEXTCLOUD_MCP_SERVICE_NAME", "NEXTCLOUD_MCP_PORT", "DOCKER_CONTAINER", ) @pytest.fixture(autouse=True) def _clean_env(monkeypatch): for name in DOCKER_ENV_VARS: monkeypatch.delenv(name, raising=False) def _patch_settings(monkeypatch, **overrides) -> None: """Make ``get_settings()`` (as called inside webhook_routes) return a Settings instance with the given URL/secret fields set; everything else falls back to the dataclass defaults.""" monkeypatch.setattr( webhook_routes, "get_settings", lambda: Settings(**overrides), ) def _no_docker_markers(monkeypatch): monkeypatch.setattr( "nextcloud_mcp_server.auth.webhook_routes.os.path.exists", lambda _path: False, ) def _docker_markers(monkeypatch): monkeypatch.setattr( "nextcloud_mcp_server.auth.webhook_routes.os.path.exists", lambda path: path == "/.dockerenv", ) @pytest.mark.unit def test_webhook_internal_url_wins_over_everything(monkeypatch): _patch_settings( monkeypatch, webhook_internal_url="https://internal.example.com", nextcloud_mcp_server_url="https://public.example.com", ) _docker_markers(monkeypatch) assert _get_webhook_uri() == "https://internal.example.com/webhooks/nextcloud" @pytest.mark.unit def test_public_url_wins_over_docker_detection(monkeypatch): """The bug-fix case: ECS containers have /.dockerenv but a public URL is set. Docker auto-detection must NOT clobber the explicit public URL.""" _patch_settings( monkeypatch, nextcloud_mcp_server_url="https://holy-bluegill.astrolabecloud.com", ) _docker_markers(monkeypatch) assert ( _get_webhook_uri() == "https://holy-bluegill.astrolabecloud.com/webhooks/nextcloud" ) @pytest.mark.unit def test_docker_detection_used_when_no_public_url(monkeypatch): """docker-compose dev: no public URL set, /.dockerenv exists → use the docker-compose service name.""" _patch_settings(monkeypatch) _docker_markers(monkeypatch) assert _get_webhook_uri() == "http://mcp:8000/webhooks/nextcloud" @pytest.mark.unit def test_docker_detection_honors_service_name_and_port_overrides(monkeypatch): _patch_settings(monkeypatch) monkeypatch.setenv("NEXTCLOUD_MCP_SERVICE_NAME", "mcp-login-flow") monkeypatch.setenv("NEXTCLOUD_MCP_PORT", "8004") _docker_markers(monkeypatch) assert _get_webhook_uri() == "http://mcp-login-flow:8004/webhooks/nextcloud" @pytest.mark.unit def test_docker_container_env_var_triggers_docker_branch(monkeypatch): _patch_settings(monkeypatch) monkeypatch.setenv("DOCKER_CONTAINER", "true") _no_docker_markers(monkeypatch) assert _get_webhook_uri() == "http://mcp:8000/webhooks/nextcloud" @pytest.mark.unit def test_localhost_fallback_when_nothing_set(monkeypatch): _patch_settings(monkeypatch) _no_docker_markers(monkeypatch) assert _get_webhook_uri() == "http://localhost:8000/webhooks/nextcloud" # --- webhook_auth_pair() -------------------------------------------------- @pytest.mark.unit def test_auth_pair_returns_none_when_secret_unset(monkeypatch): _patch_settings(monkeypatch, webhook_secret=None) assert webhook_auth_pair() == ("none", None) @pytest.mark.unit def test_auth_pair_emits_bearer_header_when_secret_set(monkeypatch): _patch_settings(monkeypatch, webhook_secret="supersecret") assert webhook_auth_pair() == ( "header", {"Authorization": "Bearer supersecret"}, )