test_access_token.py•9.82 kB
"""
Tests for Personal Access Token functionality.
"""
import os
import pytest
from unittest.mock import Mock, patch, call
from wikipedia_mcp.wikipedia_client import WikipediaClient
from wikipedia_mcp.server import create_server
class TestAccessTokenClient:
"""Test WikipediaClient with access token functionality."""
def test_client_init_without_token(self):
"""Test client initialization without access token."""
client = WikipediaClient()
assert client.access_token is None
def test_client_init_with_token(self):
"""Test client initialization with access token."""
token = "test_token_123"
client = WikipediaClient(access_token=token)
assert client.access_token == token
def test_get_request_headers_without_token(self):
"""Test request headers without access token."""
client = WikipediaClient()
headers = client._get_request_headers()
assert "User-Agent" in headers
assert "Authorization" not in headers
def test_get_request_headers_with_token(self):
"""Test request headers with access token."""
token = "test_token_123"
client = WikipediaClient(access_token=token)
headers = client._get_request_headers()
assert "User-Agent" in headers
assert "Authorization" in headers
assert headers["Authorization"] == f"Bearer {token}"
@patch("wikipedia_mcp.wikipedia_client.requests.get")
def test_search_uses_auth_headers(self, mock_get):
"""Test that search method uses authentication headers when token is provided."""
token = "test_token_123"
client = WikipediaClient(access_token=token)
# Mock successful response
mock_response = Mock()
mock_response.raise_for_status.return_value = None
mock_response.json.return_value = {
"query": {
"search": [
{
"title": "Test Article",
"snippet": "Test snippet",
"pageid": 123,
"wordcount": 100,
"timestamp": "2023-01-01T00:00:00Z",
}
]
}
}
mock_get.return_value = mock_response
# Perform search
client.search("test query")
# Verify request was made with auth headers
mock_get.assert_called_once()
call_args = mock_get.call_args
# Check that headers include authorization
headers = call_args[1]["headers"]
assert "Authorization" in headers
assert headers["Authorization"] == f"Bearer {token}"
@patch("wikipedia_mcp.wikipedia_client.requests.get")
def test_get_coordinates_uses_auth_headers(self, mock_get):
"""Test that get_coordinates method uses authentication headers when token is provided."""
token = "test_token_123"
client = WikipediaClient(access_token=token)
# Mock successful response
mock_response = Mock()
mock_response.raise_for_status.return_value = None
mock_response.json.return_value = {
"query": {
"pages": {
"123": {
"pageid": 123,
"title": "Test Article",
"coordinates": [
{
"lat": 40.7128,
"lon": -74.0060,
"primary": True,
"globe": "earth",
}
],
}
}
}
}
mock_get.return_value = mock_response
# Perform get_coordinates
client.get_coordinates("Test Article")
# Verify request was made with auth headers
mock_get.assert_called_once()
call_args = mock_get.call_args
# Check that headers include authorization
headers = call_args[1]["headers"]
assert "Authorization" in headers
assert headers["Authorization"] == f"Bearer {token}"
def test_token_not_logged(self, caplog):
"""Test that access token is not logged for security."""
import logging
caplog.set_level(logging.DEBUG)
token = "secret_token_123"
WikipediaClient(access_token=token)
# Check that token doesn't appear in logs
for record in caplog.records:
assert token not in record.getMessage()
class TestAccessTokenServer:
"""Test server creation with access token."""
def test_create_server_without_token(self):
"""Test server creation without access token."""
server = create_server()
assert server is not None
def test_create_server_with_token(self):
"""Test server creation with access token."""
token = "test_token_123"
server = create_server(access_token=token)
assert server is not None
class TestAccessTokenCLI:
"""Test CLI argument and environment variable handling."""
@patch("wikipedia_mcp.__main__.create_server")
@patch("sys.argv", ["wikipedia-mcp", "--access-token", "cli_token_123"])
def test_cli_access_token_argument(self, mock_create_server):
"""Test CLI access token argument is passed to server."""
from wikipedia_mcp.__main__ import main
# Mock server to avoid actually starting it
mock_server = Mock()
mock_create_server.return_value = mock_server
mock_server.run = Mock()
try:
main()
except SystemExit:
pass # Expected when server.run() is mocked
# Verify create_server was called with the token
mock_create_server.assert_called_once()
call_args = mock_create_server.call_args
assert call_args[1]["access_token"] == "cli_token_123"
@patch("wikipedia_mcp.__main__.create_server")
@patch("sys.argv", ["wikipedia-mcp"])
@patch.dict(os.environ, {"WIKIPEDIA_ACCESS_TOKEN": "env_token_123"})
def test_cli_environment_variable(self, mock_create_server):
"""Test environment variable is used when CLI argument not provided."""
from wikipedia_mcp.__main__ import main
# Mock server to avoid actually starting it
mock_server = Mock()
mock_create_server.return_value = mock_server
mock_server.run = Mock()
try:
main()
except SystemExit:
pass # Expected when server.run() is mocked
# Verify create_server was called with the env var token
mock_create_server.assert_called_once()
call_args = mock_create_server.call_args
assert call_args[1]["access_token"] == "env_token_123"
@patch("wikipedia_mcp.__main__.create_server")
@patch("sys.argv", ["wikipedia-mcp", "--access-token", "cli_token_123"])
@patch.dict(os.environ, {"WIKIPEDIA_ACCESS_TOKEN": "env_token_123"})
def test_cli_argument_overrides_environment(self, mock_create_server):
"""Test CLI argument takes priority over environment variable."""
from wikipedia_mcp.__main__ import main
# Mock server to avoid actually starting it
mock_server = Mock()
mock_create_server.return_value = mock_server
mock_server.run = Mock()
try:
main()
except SystemExit:
pass # Expected when server.run() is mocked
# Verify create_server was called with CLI token, not env var
mock_create_server.assert_called_once()
call_args = mock_create_server.call_args
assert call_args[1]["access_token"] == "cli_token_123"
@patch("wikipedia_mcp.__main__.create_server")
@patch("sys.argv", ["wikipedia-mcp"])
def test_cli_no_token_provided(self, mock_create_server):
"""Test no token is passed when neither CLI arg nor env var is set."""
from wikipedia_mcp.__main__ import main
# Mock server to avoid actually starting it
mock_server = Mock()
mock_create_server.return_value = mock_server
mock_server.run = Mock()
# Ensure env var is not set
if "WIKIPEDIA_ACCESS_TOKEN" in os.environ:
del os.environ["WIKIPEDIA_ACCESS_TOKEN"]
try:
main()
except SystemExit:
pass # Expected when server.run() is mocked
# Verify create_server was called with None token
mock_create_server.assert_called_once()
call_args = mock_create_server.call_args
assert call_args[1]["access_token"] is None
class TestAccessTokenSecurity:
"""Test security aspects of access token handling."""
def test_token_in_repr(self):
"""Test that token doesn't appear in object representation."""
token = "secret_token_123"
client = WikipediaClient(access_token=token)
# Check that token doesn't appear in string representation
client_str = str(client)
assert token not in client_str
client_repr = repr(client)
assert token not in client_repr
@patch("wikipedia_mcp.wikipedia_client.requests.get")
def test_error_logging_no_token_exposure(self, mock_get, caplog):
"""Test that errors don't expose the access token."""
import logging
caplog.set_level(logging.ERROR)
token = "secret_token_123"
client = WikipediaClient(access_token=token)
# Mock request to raise an exception
mock_get.side_effect = Exception("API Error")
# Perform search that will fail
client.search("test query")
# Check that token doesn't appear in error logs
for record in caplog.records:
assert token not in record.getMessage()