Skip to main content
Glama
knishioka

IB Analytics MCP Server

by knishioka
test_base.py10.2 kB
""" Tests for Issue #7: Base Sentiment Analyzer Acceptance Criteria: - [ ] SentimentScore model with score (-1.0 to 1.0) and confidence (0.0 to 1.0) as Decimal - [ ] BaseSentimentAnalyzer abstract base class - [ ] Abstract analyze_sentiment(symbol: str) -> SentimentScore method - [ ] Validate score and confidence ranges - [ ] Maintain Decimal precision throughout """ from datetime import datetime from decimal import Decimal import pytest # These imports will fail until implementation exists try: from ib_sec_mcp.analyzers.sentiment.base import ( BaseSentimentAnalyzer, SentimentScore, ) except ImportError: # Allow tests to be discovered even without implementation BaseSentimentAnalyzer = None SentimentScore = None pytestmark = pytest.mark.skipif( BaseSentimentAnalyzer is None, reason="BaseSentimentAnalyzer not implemented yet (TDD red phase)", ) class TestSentimentScore: """Test suite for SentimentScore model""" def test_sentiment_score_basic_creation(self): """ Acceptance Criterion: SentimentScore model with score and confidence Expected: Valid model creation with Decimal values """ score = SentimentScore( score=Decimal("0.75"), confidence=Decimal("0.85"), timestamp=datetime.now() ) assert isinstance(score.score, Decimal) assert isinstance(score.confidence, Decimal) assert score.score == Decimal("0.75") assert score.confidence == Decimal("0.85") def test_sentiment_score_range_validation_positive(self): """ Acceptance Criterion: Validate score range (-1.0 to 1.0) Test positive sentiment within valid range """ score = SentimentScore( score=Decimal("1.0"), confidence=Decimal("0.9"), timestamp=datetime.now() ) assert Decimal("-1.0") <= score.score <= Decimal("1.0") def test_sentiment_score_range_validation_negative(self): """ Acceptance Criterion: Validate score range (-1.0 to 1.0) Test negative sentiment within valid range """ score = SentimentScore( score=Decimal("-1.0"), confidence=Decimal("0.9"), timestamp=datetime.now() ) assert Decimal("-1.0") <= score.score <= Decimal("1.0") def test_sentiment_score_range_validation_neutral(self): """ Acceptance Criterion: Validate score range (-1.0 to 1.0) Test neutral sentiment (zero) """ score = SentimentScore( score=Decimal("0.0"), confidence=Decimal("0.5"), timestamp=datetime.now() ) assert score.score == Decimal("0.0") def test_sentiment_score_invalid_range_too_high(self): """ Edge Case: Score exceeds maximum (1.0) Should raise validation error """ with pytest.raises((ValueError, Exception)): # Pydantic ValidationError SentimentScore( score=Decimal("1.5"), confidence=Decimal("0.9"), timestamp=datetime.now() ) def test_sentiment_score_invalid_range_too_low(self): """ Edge Case: Score below minimum (-1.0) Should raise validation error """ with pytest.raises((ValueError, Exception)): SentimentScore( score=Decimal("-1.5"), confidence=Decimal("0.9"), timestamp=datetime.now() ) def test_confidence_range_validation_valid(self): """ Acceptance Criterion: Validate confidence range (0.0 to 1.0) Test confidence within valid range """ score = SentimentScore( score=Decimal("0.5"), confidence=Decimal("1.0"), timestamp=datetime.now() ) assert Decimal("0.0") <= score.confidence <= Decimal("1.0") def test_confidence_invalid_range_too_high(self): """ Edge Case: Confidence exceeds maximum (1.0) Should raise validation error """ with pytest.raises((ValueError, Exception)): SentimentScore( score=Decimal("0.5"), confidence=Decimal("1.5"), timestamp=datetime.now() ) def test_confidence_invalid_range_negative(self): """ Edge Case: Confidence is negative Should raise validation error """ with pytest.raises((ValueError, Exception)): SentimentScore( score=Decimal("0.5"), confidence=Decimal("-0.1"), timestamp=datetime.now() ) def test_sentiment_score_decimal_precision(self): """ Financial Code Requirement: Maintain Decimal precision Ensure no float conversion occurs """ score = SentimentScore( score=Decimal("0.123456789"), confidence=Decimal("0.987654321"), timestamp=datetime.now(), ) # Verify Decimal type is preserved assert isinstance(score.score, Decimal) assert isinstance(score.confidence, Decimal) # Verify precision is maintained assert str(score.score) == "0.123456789" assert str(score.confidence) == "0.987654321" def test_sentiment_score_serialization(self): """ Integration Test: JSON serialization for MCP tools Should serialize to JSON-compatible format """ score = SentimentScore( score=Decimal("0.75"), confidence=Decimal("0.85"), timestamp=datetime.now() ) # Pydantic v2 model_dump() returns dict data = score.model_dump() assert "score" in data assert "confidence" in data assert "timestamp" in data @pytest.mark.parametrize( "score_value,confidence_value", [ (Decimal("0.0"), Decimal("0.0")), (Decimal("0.5"), Decimal("0.5")), (Decimal("1.0"), Decimal("1.0")), (Decimal("-1.0"), Decimal("1.0")), (Decimal("-0.5"), Decimal("0.75")), ], ) def test_sentiment_score_various_valid_combinations(self, score_value, confidence_value): """ Parametrized Test: Various valid score and confidence combinations All combinations should be valid """ score = SentimentScore( score=score_value, confidence=confidence_value, timestamp=datetime.now() ) assert Decimal("-1.0") <= score.score <= Decimal("1.0") assert Decimal("0.0") <= score.confidence <= Decimal("1.0") class TestBaseSentimentAnalyzer: """Test suite for BaseSentimentAnalyzer abstract base class""" def test_base_analyzer_is_abstract(self): """ Acceptance Criterion: BaseSentimentAnalyzer is abstract Should not be instantiable directly """ with pytest.raises(TypeError): BaseSentimentAnalyzer() def test_base_analyzer_requires_analyze_sentiment(self): """ Acceptance Criterion: Abstract analyze_sentiment method Subclasses must implement analyze_sentiment """ # Create concrete implementation class IncompleteSentimentAnalyzer(BaseSentimentAnalyzer): pass with pytest.raises(TypeError): IncompleteSentimentAnalyzer() def test_base_analyzer_concrete_implementation(self): """ Integration Test: Concrete analyzer implementation Should be instantiable when analyze_sentiment is implemented """ class ConcreteSentimentAnalyzer(BaseSentimentAnalyzer): async def analyze_sentiment(self, symbol: str) -> SentimentScore: return SentimentScore( score=Decimal("0.5"), confidence=Decimal("0.8"), timestamp=datetime.now() ) analyzer = ConcreteSentimentAnalyzer() assert analyzer is not None @pytest.mark.asyncio async def test_base_analyzer_analyze_sentiment_signature(self): """ Acceptance Criterion: analyze_sentiment(symbol: str) -> SentimentScore Verify method signature and return type """ class TestAnalyzer(BaseSentimentAnalyzer): async def analyze_sentiment(self, symbol: str) -> SentimentScore: return SentimentScore( score=Decimal("0.0"), confidence=Decimal("1.0"), timestamp=datetime.now() ) analyzer = TestAnalyzer() result = await analyzer.analyze_sentiment("AAPL") assert isinstance(result, SentimentScore) assert isinstance(result.score, Decimal) assert isinstance(result.confidence, Decimal) @pytest.mark.asyncio async def test_base_analyzer_returns_decimal_types(self): """ Financial Code Requirement: Return Decimal values Ensure analyzer returns Decimal, not float """ class TestAnalyzer(BaseSentimentAnalyzer): async def analyze_sentiment(self, symbol: str) -> SentimentScore: return SentimentScore( score=Decimal("0.123"), confidence=Decimal("0.987"), timestamp=datetime.now() ) analyzer = TestAnalyzer() result = await analyzer.analyze_sentiment("AAPL") # Verify types assert isinstance(result.score, Decimal) assert isinstance(result.confidence, Decimal) # Verify not float assert not isinstance(result.score, float) assert not isinstance(result.confidence, float) @pytest.mark.asyncio async def test_base_analyzer_symbol_parameter(self): """ Acceptance Criterion: Symbol-based analysis Analyzer should accept stock symbol string """ class TestAnalyzer(BaseSentimentAnalyzer): async def analyze_sentiment(self, symbol: str) -> SentimentScore: # Verify symbol is passed correctly assert isinstance(symbol, str) assert symbol == "TSLA" return SentimentScore( score=Decimal("0.0"), confidence=Decimal("0.0"), timestamp=datetime.now() ) analyzer = TestAnalyzer() await analyzer.analyze_sentiment("TSLA")

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/knishioka/ib-sec-mcp'

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