Skip to main content
Glama

MCP Stock Details Server

by whdghk1907
test_shareholder_info.pyβ€’13.2 kB
""" Unit tests for Shareholder Info functionality TDD Red Phase: Write failing tests for shareholder analysis """ import pytest import asyncio from unittest.mock import Mock, AsyncMock, patch from datetime import datetime, date, timedelta from typing import Any, Dict, List, Optional # Test imports - initially will fail (TDD Red phase) from src.server import MCPStockDetailsServer from src.tools.shareholder_tools import ShareholderAnalyzer from src.exceptions import MCPStockDetailsError, InsufficientDataError class TestShareholderInfo: """Test cases for shareholder info functionality""" @pytest.fixture def server_with_shareholder(self): """Create server instance with shareholder analyzer""" server = MCPStockDetailsServer() # This will fail initially - Red phase server.shareholder_analyzer = ShareholderAnalyzer() return server @pytest.fixture def sample_shareholder_data(self): """Sample shareholder data for testing""" return { "005930": { # Samsung Electronics "major_shareholders": [ { "name": "κ΅­λ―Όμ—°κΈˆκ³΅λ‹¨", "shares": 148_712_953, "percentage": 9.57, "type": "institutional" }, { "name": "μ‚Όμ„±λ¬Όμ‚° μ£Όμ‹νšŒμ‚¬", "shares": 133_284_628, "percentage": 8.58, "type": "corporate" }, { "name": "이재용", "shares": 18_352_174, "percentage": 1.18, "type": "individual" } ], "ownership_structure": { "free_float": 82.45, "foreign_ownership": 52.37, "institutional_ownership": 31.28, "individual_ownership": 16.35 }, "dividend_history": [ {"year": 2023, "dividend_per_share": 1640, "dividend_yield": 2.18, "payout_ratio": 23.5}, {"year": 2022, "dividend_per_share": 1444, "dividend_yield": 2.89, "payout_ratio": 28.7}, {"year": 2021, "dividend_per_share": 1378, "dividend_yield": 1.95, "payout_ratio": 22.1} ], "governance_metrics": { "board_independence": 45.5, "audit_committee_independence": 100.0, "compensation_committee_independence": 75.0, "board_diversity_ratio": 18.2, "avg_board_tenure": 4.2 } } } @pytest.mark.asyncio async def test_get_shareholder_info_tool_registration(self, server_with_shareholder): """Test that get_shareholder_info tool is properly registered""" tools = await server_with_shareholder.list_tools() tool_names = [tool.name for tool in tools] assert "get_shareholder_info" in tool_names # Check tool description and parameters shareholder_tool = next(tool for tool in tools if tool.name == "get_shareholder_info") assert "shareholder" in shareholder_tool.description.lower() assert "ownership" in shareholder_tool.description.lower() assert "company_code" in shareholder_tool.inputSchema["properties"] @pytest.mark.asyncio async def test_major_shareholders_analysis(self, server_with_shareholder, sample_shareholder_data): """Test major shareholders analysis""" company_code = "005930" with patch.object(server_with_shareholder.shareholder_analyzer, 'get_shareholder_data') as mock_data: mock_data.return_value = sample_shareholder_data[company_code] result = await server_with_shareholder._handle_get_shareholder_info({ "company_code": company_code, "include_major_shareholders": True }) assert result is not None assert len(result) > 0 content = result[0].text # Should include major shareholders assert "MAJOR SHAREHOLDERS" in content assert "κ΅­λ―Όμ—°κΈˆκ³΅λ‹¨" in content assert "μ‚Όμ„±λ¬Όμ‚°" in content assert "9.57%" in content or "9.6%" in content assert "8.58%" in content or "8.6%" in content @pytest.mark.asyncio async def test_ownership_structure_analysis(self, server_with_shareholder, sample_shareholder_data): """Test ownership structure analysis""" company_code = "005930" with patch.object(server_with_shareholder.shareholder_analyzer, 'get_shareholder_data') as mock_data: mock_data.return_value = sample_shareholder_data[company_code] result = await server_with_shareholder._handle_get_shareholder_info({ "company_code": company_code, "include_ownership_structure": True }) assert result is not None content = result[0].text # Should include ownership structure assert "OWNERSHIP STRUCTURE" in content assert "Free Float" in content assert "Foreign Ownership" in content assert "Institutional" in content assert "82.45%" in content or "82.5%" in content assert "52.37%" in content or "52.4%" in content @pytest.mark.asyncio async def test_dividend_history_analysis(self, server_with_shareholder, sample_shareholder_data): """Test dividend history analysis""" company_code = "005930" with patch.object(server_with_shareholder.shareholder_analyzer, 'get_shareholder_data') as mock_data: mock_data.return_value = sample_shareholder_data[company_code] result = await server_with_shareholder._handle_get_shareholder_info({ "company_code": company_code, "include_dividend_history": True }) assert result is not None content = result[0].text # Should include dividend history assert "DIVIDEND HISTORY" in content assert "2023" in content assert "1640" in content or "β‚©1,640" in content assert "2.18%" in content assert "Payout Ratio" in content @pytest.mark.asyncio async def test_governance_metrics_analysis(self, server_with_shareholder, sample_shareholder_data): """Test governance metrics analysis""" company_code = "005930" with patch.object(server_with_shareholder.shareholder_analyzer, 'get_shareholder_data') as mock_data: mock_data.return_value = sample_shareholder_data[company_code] result = await server_with_shareholder._handle_get_shareholder_info({ "company_code": company_code, "include_governance_metrics": True }) assert result is not None content = result[0].text # Should include governance metrics assert "GOVERNANCE METRICS" in content assert "Board Independence" in content assert "Audit Committee" in content assert "45.5%" in content assert "100.0%" in content @pytest.mark.asyncio async def test_shareholder_concentration_analysis(self, server_with_shareholder, sample_shareholder_data): """Test shareholder concentration analysis""" company_code = "005930" result = await server_with_shareholder._handle_get_shareholder_info({ "company_code": company_code, "include_concentration_analysis": True }) assert result is not None content = result[0].text # Should include concentration analysis assert "CONCENTRATION ANALYSIS" in content assert "HHI" in content or "Herfindahl" in content assert "Top 5 Concentration" in content assert "Top 10 Concentration" in content @pytest.mark.asyncio async def test_voting_rights_analysis(self, server_with_shareholder): """Test voting rights analysis""" company_code = "005930" result = await server_with_shareholder._handle_get_shareholder_info({ "company_code": company_code, "include_voting_rights": True }) assert result is not None content = result[0].text # Should include voting rights analysis assert "VOTING RIGHTS" in content assert "Voting Power" in content assert "Control Assessment" in content @pytest.mark.asyncio async def test_insider_trading_analysis(self, server_with_shareholder): """Test insider trading analysis""" company_code = "005930" result = await server_with_shareholder._handle_get_shareholder_info({ "company_code": company_code, "include_insider_trading": True }) assert result is not None content = result[0].text # Should include insider trading analysis assert "INSIDER TRADING" in content assert "Recent Transactions" in content assert "Net Buy/Sell" in content @pytest.mark.asyncio async def test_comprehensive_shareholder_report(self, server_with_shareholder, sample_shareholder_data): """Test comprehensive shareholder report generation""" company_code = "005930" with patch.object(server_with_shareholder.shareholder_analyzer, 'get_shareholder_data') as mock_data: mock_data.return_value = sample_shareholder_data[company_code] result = await server_with_shareholder._handle_get_shareholder_info({ "company_code": company_code, "analysis_type": "comprehensive" }) assert result is not None content = result[0].text # Should include comprehensive analysis assert "COMPREHENSIVE SHAREHOLDER ANALYSIS" in content assert "Ownership Summary" in content assert "Key Insights" in content assert "Governance Assessment" in content @pytest.mark.asyncio async def test_shareholder_change_tracking(self, server_with_shareholder): """Test shareholder change tracking""" company_code = "005930" result = await server_with_shareholder._handle_get_shareholder_info({ "company_code": company_code, "include_change_tracking": True, "tracking_period": "1Y" }) assert result is not None content = result[0].text # Should include change tracking assert "SHAREHOLDER CHANGES" in content assert "Period" in content assert "Net Change" in content assert "New Entries" in content assert "Exits" in content @pytest.mark.asyncio async def test_dividend_sustainability_analysis(self, server_with_shareholder, sample_shareholder_data): """Test dividend sustainability analysis""" company_code = "005930" with patch.object(server_with_shareholder.shareholder_analyzer, 'get_shareholder_data') as mock_data: mock_data.return_value = sample_shareholder_data[company_code] result = await server_with_shareholder._handle_get_shareholder_info({ "company_code": company_code, "include_dividend_sustainability": True }) assert result is not None content = result[0].text # Should include dividend sustainability assert "DIVIDEND SUSTAINABILITY" in content assert "Coverage Ratio" in content assert "Sustainability Score" in content assert "Future Outlook" in content @pytest.mark.asyncio async def test_shareholder_info_error_handling(self, server_with_shareholder): """Test error handling for shareholder info""" # Test invalid company code with pytest.raises(MCPStockDetailsError): await server_with_shareholder._handle_get_shareholder_info({ "company_code": "", "include_major_shareholders": True }) # Test insufficient shareholder data with pytest.raises(InsufficientDataError): await server_with_shareholder._handle_get_shareholder_info({ "company_code": "999999", # Non-existent company "include_major_shareholders": True })

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/whdghk1907/mcp-stock-details'

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