import importlib.util
import logging
from pathlib import Path
import pytest
MODULE_PATH = Path(__file__).resolve().parents[1] / "enhanced_dash_server.py"
def test_configure_logging_creates_file(tmp_path, stub_modules):
class DummyServer:
def __init__(self, *_args, **_kwargs) -> None: # pragma: no cover - stub
pass
def list_tools(self): # pragma: no cover - stub
def decorator(func):
return func
return decorator
def call_tool(self): # pragma: no cover - stub
def decorator(func):
async def wrapper(*args, **kwargs):
return await func(*args, **kwargs)
return wrapper
return decorator
stub_modules["mcp.server"].Server = DummyServer # type: ignore[attr-defined]
spec = importlib.util.spec_from_file_location("enhanced_dash_server", MODULE_PATH)
assert spec and spec.loader
server_mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(server_mod)
log_file = tmp_path / "server.log"
server_mod.configure_logging(logging.INFO, str(log_file))
server_mod.logger.info("hello")
logging.shutdown()
assert log_file.exists()
assert "hello" in log_file.read_text()
@pytest.mark.asyncio
async def test_main_logs_startup_message(tmp_path, monkeypatch, stub_modules):
"""main should record a startup message to the log file."""
class DummyServer:
async def run(self, *_args, **_kwargs) -> None:
return
def list_tools(self):
def decorator(func):
return func
return decorator
def call_tool(self):
def decorator(func):
async def wrapper(*args, **kwargs):
return await func(*args, **kwargs)
return wrapper
return decorator
stub_modules["mcp.server"].Server = DummyServer # type: ignore[attr-defined]
spec = importlib.util.spec_from_file_location("enhanced_dash_server", MODULE_PATH)
assert spec and spec.loader
server_mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(server_mod)
log_file = tmp_path / "server.log"
server_mod.LOG_FILE = str(log_file)
server_mod.configure_logging(logging.INFO, str(log_file))
async def dummy_stdio_server():
class DummyStream:
async def send(self, _data: bytes) -> None:
pass
async def receive(self) -> bytes:
return b""
yield DummyStream(), DummyStream()
monkeypatch.setattr(server_mod, "stdio_server", dummy_stdio_server)
monkeypatch.setattr(server_mod, "server", DummyServer())
await server_mod.main()
logging.shutdown()
assert log_file.exists()
content = log_file.read_text()
assert "server starting" in content.lower()
assert "server stopped" in content.lower()
@pytest.mark.asyncio
async def test_main_logs_error(tmp_path, monkeypatch, stub_modules):
"""main should log an error when server.run fails."""
class FailingServer:
async def run(self, *_args, **_kwargs) -> None:
raise RuntimeError("boom")
def list_tools(self):
def decorator(func):
return func
return decorator
def call_tool(self):
def decorator(func):
async def wrapper(*args, **kwargs):
return await func(*args, **kwargs)
return wrapper
return decorator
stub_modules["mcp.server"].Server = FailingServer # type: ignore[attr-defined]
spec = importlib.util.spec_from_file_location("enhanced_dash_server", MODULE_PATH)
assert spec and spec.loader
server_mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(server_mod)
log_file = tmp_path / "server.log"
server_mod.LOG_FILE = str(log_file)
server_mod.configure_logging(logging.INFO, str(log_file))
async def dummy_stdio_server():
class DummyStream:
async def send(self, _data: bytes) -> None:
pass
async def receive(self) -> bytes:
return b""
yield DummyStream(), DummyStream()
monkeypatch.setattr(server_mod, "stdio_server", dummy_stdio_server)
monkeypatch.setattr(server_mod, "server", FailingServer())
with pytest.raises(RuntimeError):
await server_mod.main()
logging.shutdown()
content = log_file.read_text()
assert "error running server" in content.lower()