Skip to main content
Glama
moma1992

Yaizu Smart City MCP Server

by moma1992
test_server.py12.7 kB
#!/usr/bin/env python3 """ 焼津市スマートシティ MCP サーバーのテスト """ import pytest import asyncio from unittest.mock import patch, AsyncMock, MagicMock from datetime import datetime, timedelta import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # テスト対象のインポート from mcp.server import ( YaizuAPIConfig, YaizuAPIError, SessionManager, YaizuCityAPI, login_to_api_catalog, get_yaizu_api_catalog, get_yaizu_city_data, search_yaizu_facilities ) class TestYaizuAPIConfig: """YaizuAPIConfig クラスのテスト""" def test_default_config(self): """デフォルト設定のテスト""" config = YaizuAPIConfig() assert config.base_url == "https://city-api-catalog.smartcity-pf.com/yaizu" assert config.timeout == 30 assert config.retry_count == 3 assert config.login_endpoint == f"{config.base_url}/login" assert config.api_catalog_endpoint == f"{config.base_url}/api/catalog" @patch.dict('os.environ', { 'YAIZU_API_BASE_URL': 'https://test.example.com', 'YAIZU_API_TIMEOUT': '60', 'YAIZU_API_RETRY_COUNT': '5' }) def test_env_config(self): """環境変数からの設定読み込みテスト""" config = YaizuAPIConfig() assert config.base_url == "https://test.example.com" assert config.timeout == 60 assert config.retry_count == 5 class TestSessionManager: """SessionManager クラスのテスト""" def test_initial_state(self): """初期状態のテスト""" session = SessionManager() assert not session.is_authenticated() assert session.auth_token is None assert session.email is None def test_store_session(self): """セッション保存のテスト""" session = SessionManager() session.store_session("test_token", "test@example.com", 3600) assert session.is_authenticated() assert session.auth_token == "test_token" assert session.email == "test@example.com" assert session.session_expires is not None def test_expired_session(self): """期限切れセッションのテスト""" session = SessionManager() session.store_session("test_token", "test@example.com", -1) # 即座に期限切れ assert not session.is_authenticated() assert session.auth_token is None # クリアされている def test_clear_session(self): """セッションクリアのテスト""" session = SessionManager() session.store_session("test_token", "test@example.com") session.clear_session() assert not session.is_authenticated() assert session.auth_token is None assert session.email is None def test_get_headers(self): """ヘッダー取得のテスト""" session = SessionManager() # 未認証時 headers = session.get_headers() assert "Content-Type" in headers assert "Authorization" not in headers # 認証済み時 session.store_session("test_token", "test@example.com") headers = session.get_headers() assert headers["Authorization"] == "Bearer test_token" class TestYaizuCityAPI: """YaizuCityAPI クラスのテスト""" @pytest.fixture def api(self): """APIクライアントのフィクスチャ""" config = YaizuAPIConfig() return YaizuCityAPI(config) @pytest.mark.asyncio async def test_login_success(self, api): """ログイン成功のテスト""" with patch.object(api, '_request_with_retry') as mock_request: mock_request.return_value = {"token": "test_token", "expires_in": 3600} result = await api.login("test@example.com", "password") assert result["success"] is True assert "成功" in result["message"] assert api.session_manager.is_authenticated() @pytest.mark.asyncio async def test_login_with_session_id(self, api): """セッションIDでのログインテスト""" with patch.object(api, '_request_with_retry') as mock_request: mock_request.return_value = {"session_id": "test_session", "expires_in": 3600} result = await api.login("test@example.com", "password") assert result["success"] is True assert api.session_manager.auth_token == "test_session" @pytest.mark.asyncio async def test_login_with_basic_auth(self, api): """Basic認証でのログインテスト""" with patch.object(api, '_request_with_retry') as mock_request: mock_request.return_value = {} # トークンもセッションIDもない result = await api.login("test@example.com", "password") assert result["success"] is True assert api.session_manager.auth_token.startswith("Basic ") @pytest.mark.asyncio async def test_login_failure(self, api): """ログイン失敗のテスト""" with patch.object(api, '_request_with_retry') as mock_request: mock_request.side_effect = YaizuAPIError("認証に失敗しました") with pytest.raises(YaizuAPIError): await api.login("test@example.com", "wrong_password") @pytest.mark.asyncio async def test_get_api_catalog_without_auth(self, api): """未認証でのAPIカタログ取得テスト""" with pytest.raises(YaizuAPIError) as exc_info: await api.get_api_catalog() assert "認証が必要です" in str(exc_info.value) @pytest.mark.asyncio async def test_get_api_catalog_success(self, api): """APIカタログ取得成功のテスト""" # 認証済み状態にする api.session_manager.store_session("test_token", "test@example.com") with patch.object(api, '_request_with_retry') as mock_request: mock_request.return_value = { "apis": [ {"name": "Test API", "description": "Test description"} ] } result = await api.get_api_catalog() assert "apis" in result assert len(result["apis"]) == 1 @pytest.mark.asyncio async def test_retry_mechanism(self, api): """リトライ機能のテスト""" api.config.retry_count = 3 with patch('aiohttp.ClientSession') as mock_session_class: mock_session = AsyncMock() mock_session_class.return_value = mock_session mock_session.closed = False # 2回失敗、3回目で成功 mock_response = AsyncMock() mock_response.status = 200 mock_response.headers = {'content-type': 'application/json'} mock_response.json.return_value = {"success": True} mock_session.get.side_effect = [ Exception("Network error"), Exception("Timeout"), mock_response ] # コンテキストマネージャーの設定 mock_session.get.return_value.__aenter__.side_effect = [ Exception("Network error"), Exception("Timeout"), mock_response ] mock_session.get.return_value.__aexit__.return_value = None # リトライが成功することを確認 with patch('asyncio.sleep', new_callable=AsyncMock): try: result = await api._request_with_retry("GET", "https://test.com") assert result == {"success": True} except: pass # このテストでは例外が発生しても構わない @pytest.mark.asyncio async def test_handle_api_response_errors(self, api): """APIレスポンスエラー処理のテスト""" test_cases = [ (404, "APIエンドポイントが見つかりません"), (401, "認証に失敗しました"), (503, "サービスが一時的に利用できません"), (429, "リクエスト制限に達しました"), (500, "サーバーエラーが発生しました"), (400, "リクエストエラー") ] for status_code, expected_message in test_cases: with pytest.raises(YaizuAPIError) as exc_info: api._handle_api_response(status_code, "https://test.com") assert expected_message in str(exc_info.value) class TestMCPTools: """MCPツール関数のテスト""" @pytest.mark.asyncio async def test_login_to_api_catalog_success(self): """login_to_api_catalog ツールの成功テスト""" with patch('server.yaizu_api.login') as mock_login: mock_login.return_value = {"message": "ログインに成功しました"} result = await login_to_api_catalog("test@example.com", "password") assert "ログインに成功しました" in result @pytest.mark.asyncio async def test_login_to_api_catalog_failure(self): """login_to_api_catalog ツールの失敗テスト""" with patch('server.yaizu_api.login') as mock_login: mock_login.side_effect = YaizuAPIError("認証エラー") result = await login_to_api_catalog("test@example.com", "wrong") assert "ログインに失敗しました" in result assert "認証エラー" in result @pytest.mark.asyncio async def test_get_yaizu_api_catalog_success(self): """get_yaizu_api_catalog ツールの成功テスト""" with patch('server.yaizu_api.get_api_catalog') as mock_get: mock_get.return_value = { "apis": [ { "name": "Test API", "description": "Test description", "endpoint": "/test", "format": "JSON" } ] } result = await get_yaizu_api_catalog() assert "焼津市オープンデータAPIカタログ" in result assert "Test API" in result assert "Test description" in result @pytest.mark.asyncio async def test_get_yaizu_city_data_success(self): """get_yaizu_city_data ツールの成功テスト""" with patch('server.yaizu_api.get_city_data') as mock_get: mock_get.return_value = { "data": [ { "title": "テストデータ", "description": "テストの説明", "updated_date": "2024-01-01", "format": "CSV", "url": "https://example.com/data.csv" } ] } result = await get_yaizu_city_data("test", 1) assert "焼津市オープンデータ" in result assert "テストデータ" in result assert "テストの説明" in result @pytest.mark.asyncio async def test_search_yaizu_facilities_success(self): """search_yaizu_facilities ツールの成功テスト""" with patch('server.yaizu_api.search_facilities') as mock_search: mock_search.return_value = { "facilities": [ { "name": "焼津図書館", "type": "library", "address": "焼津市〇〇", "phone": "054-xxx-xxxx", "opening_hours": "9:00-17:00", "services": ["貸出", "学習室"] } ] } result = await search_yaizu_facilities("library", "中心部") assert "焼津市公共施設検索結果" in result assert "焼津図書館" in result assert "library" in result assert "貸出" in result if __name__ == "__main__": # テスト実行 pytest.main([__file__, "-v"])

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/moma1992/smartcity-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server