"""Tests for auth configuration validation and startup routes."""
import json
import sys
from unittest.mock import MagicMock
import pytest
from core.config import config
from starlette.requests import Request
from starlette.responses import JSONResponse
@pytest.fixture(autouse=True)
def _restore_config(monkeypatch):
"""Prevent main() side effects on the global config from leaking to other tests."""
monkeypatch.setattr(config, "http_remote_hosted", config.http_remote_hosted)
monkeypatch.setattr(config, "api_key_validation_url", config.api_key_validation_url)
monkeypatch.setattr(config, "api_key_login_url", config.api_key_login_url)
monkeypatch.setattr(config, "api_key_cache_ttl", config.api_key_cache_ttl)
monkeypatch.setattr(config, "api_key_service_token_header", config.api_key_service_token_header)
monkeypatch.setattr(config, "api_key_service_token", config.api_key_service_token)
class TestStartupConfigValidation:
def test_remote_hosted_flag_without_validation_url_exits(self, monkeypatch):
"""--http-remote-hosted without --api-key-validation-url should SystemExit(1)."""
monkeypatch.setattr(
sys,
"argv",
[
"main",
"--transport", "http",
"--http-remote-hosted",
# Deliberately omit --api-key-validation-url
],
)
monkeypatch.delenv("UNITY_MCP_API_KEY_VALIDATION_URL", raising=False)
monkeypatch.delenv("UNITY_MCP_HTTP_REMOTE_HOSTED", raising=False)
from main import main
with pytest.raises(SystemExit) as exc_info:
main()
assert exc_info.value.code == 1
def test_remote_hosted_env_var_without_validation_url_exits(self, monkeypatch):
"""UNITY_MCP_HTTP_REMOTE_HOSTED=true without validation URL should SystemExit(1)."""
monkeypatch.setattr(
sys,
"argv",
[
"main",
"--transport", "http",
# No --http-remote-hosted flag
],
)
monkeypatch.setenv("UNITY_MCP_HTTP_REMOTE_HOSTED", "true")
monkeypatch.delenv("UNITY_MCP_API_KEY_VALIDATION_URL", raising=False)
from main import main
with pytest.raises(SystemExit) as exc_info:
main()
assert exc_info.value.code == 1
class TestLoginUrlEndpoint:
"""Test the /api/auth/login-url route handler logic.
These tests replicate the handler inline to avoid full MCP server construction.
The logic mirrors main.py's auth_login_url route exactly.
"""
@staticmethod
async def _auth_login_url(_request):
"""Replicate the route handler from main.py."""
if not config.api_key_login_url:
return JSONResponse(
{
"success": False,
"error": "API key management not configured. Contact your server administrator.",
},
status_code=404,
)
return JSONResponse({
"success": True,
"login_url": config.api_key_login_url,
})
@pytest.mark.asyncio
async def test_login_url_returns_url_when_configured(self, monkeypatch):
monkeypatch.setattr(config, "api_key_login_url",
"https://app.example.com/keys")
response = await self._auth_login_url(MagicMock(spec=Request))
assert response.status_code == 200
body = json.loads(response.body.decode())
assert body["success"] is True
assert body["login_url"] == "https://app.example.com/keys"
@pytest.mark.asyncio
async def test_login_url_returns_404_when_not_configured(self, monkeypatch):
monkeypatch.setattr(config, "api_key_login_url", None)
response = await self._auth_login_url(MagicMock(spec=Request))
assert response.status_code == 404
body = json.loads(response.body.decode())
assert body["success"] is False
assert "not configured" in body["error"]