"""Tests for schema validation""" import pytest from sentiment_engine.schemas.payload import NormalizedPayload, SourceType, AssetMention, EngagementMetrics from sentiment_engine.schemas.processed import ProcessedItem, EntityExtraction, SentimentScores, EmotionScores, EventClassification, EventType from sentiment_engine.schemas.output import AssetSentiment, MarketSentiment, PumpDumpScore, VelocityMetrics, EventFlag class TestPayloadSchemas: """Test payload schema validation""" def test_normalized_payload_valid(self): payload = NormalizedPayload( source_id="test", source_type=SourceType.NEWS, source_credibility_base=0.8, ingest_ts=1724262300.0, raw_text="BTC surges to new highs", asset_mentions=[AssetMention(asset_id="BTC", mention_span=(0, 3), confidence=0.9, source_text="BTC", mention_type="ticker")], content_length=25, language="en" ) assert payload.source_id == "test" assert payload.has_assets is True assert payload.get_assets() == ["BTC"] def test_normalized_payload_empty_text_raises(self): with pytest.raises(ValueError, match="raw_text cannot be empty"): NormalizedPayload( source_id="test", source_type=SourceType.NEWS, source_credibility_base=0.8, ingest_ts=1724262300.0, raw_text="", content_length=0, language="en" ) def test_engagement_metrics_total(self): metrics = EngagementMetrics(retweets=10, likes=50, replies=5, upvotes=100, comments=20) assert metrics.total_engagement() == 185 class TestProcessedSchemas: """Test processed item schemas""" def test_sentiment_scores_bounds(self): scores = SentimentScores( polarity=0.5, confidence=0.8, positive_prob=0.7, negative_prob=0.1, neutral_prob=0.2 ) assert -1.0 <= scores.polarity <= 1.0 assert 0.0 <= scores.confidence <= 1.0 def test_emotion_scores_bounds(self): emotions = EmotionScores( joy=0.8, fear=0.1, anger=0.05, greed=0.7, sadness=0.05, intensity=0.75 ) for val in [emotions.joy, emotions.fear, emotions.anger, emotions.greed, emotions.sadness, emotions.intensity]: assert 0.0 <= val <= 1.0 def test_event_classification(self): event = EventClassification( event_type=EventType.LISTING, confidence=0.8, assets_involved=["BTC"], key_details={"exchange": "Binance"}, severity=0.7 ) assert event.event_type == EventType.LISTING assert 0.0 <= event.severity <= 1.0 class TestOutputSchemas: """Test output schemas""" def test_asset_sentiment_acb_signals(self): asset = AssetSentiment( asset_id="BTC", fear_state=20.0, greed_state=80.0, sentiment_polarity=60.0, pump_dump=PumpDumpScore(asset_id="BTC", pump_score=75.0, dump_score=15.0, pump_confidence=0.8, dump_confidence=0.7, last_update_ts=1724262305.0), last_update_ts=1724262305.0 ) # Test ACB signal extraction market = MarketSentiment( fear_state=25.0, greed_state=75.0, sentiment_index=50.0, hype_velocity=65.0, pub_velocity=55.0, aggregate_pump_risk=75.0, aggregate_dump_risk=20.0, last_update_ts=1724262305.0 ) from sentiment_engine.schemas.output import SentimentOutput output = SentimentOutput(timestamp=1724262305.0, market=market, assets={"BTC": asset}) acb = output.get_acb_signals() assert "market_sentiment_state" in acb assert "aggregate_pump_risk" in acb assert -1.0 <= acb["market_sentiment_state"] <= 1.0 assert 0.0 <= acb["aggregate_pump_risk"] <= 1.0 def test_book_health_veto(self): asset = AssetSentiment( asset_id="BTC", fear_state=20.0, greed_state=80.0, sentiment_polarity=60.0, pump_dump=PumpDumpScore(asset_id="BTC", pump_score=80.0, dump_score=15.0, pump_confidence=0.8, dump_confidence=0.7, last_update_ts=1724262305.0), last_update_ts=1724262305.0 ) from sentiment_engine.schemas.output import SentimentOutput, MarketSentiment market = MarketSentiment( fear_state=25.0, greed_state=75.0, sentiment_index=50.0, hype_velocity=65.0, pub_velocity=55.0, aggregate_pump_risk=75.0, aggregate_dump_risk=20.0, last_update_ts=1724262305.0 ) output = SentimentOutput(timestamp=1724262305.0, market=market, assets={"BTC": asset}) veto = output.get_book_health_veto(threshold=75.0) assert "BTC" in veto veto_low = output.get_book_health_veto(threshold=85.0) assert "BTC" not in veto_low def test_exit_context(self): asset = AssetSentiment( asset_id="BTC", fear_state=85.0, greed_state=15.0, sentiment_polarity=-70.0, pump_dump=PumpDumpScore(asset_id="BTC", pump_score=10.0, dump_score=80.0, pump_confidence=0.8, dump_confidence=0.7, last_update_ts=1724262305.0), last_update_ts=1724262305.0 ) from sentiment_engine.schemas.output import SentimentOutput, MarketSentiment market = MarketSentiment( fear_state=80.0, greed_state=20.0, sentiment_index=-60.0, hype_velocity=30.0, pub_velocity=40.0, aggregate_pump_risk=15.0, aggregate_dump_risk=80.0, last_update_ts=1724262305.0 ) output = SentimentOutput(timestamp=1724262305.0, market=market, assets={"BTC": asset}) ctx = output.get_exit_context(dump_threshold=70.0, fear_threshold=80.0) assert "BTC" in ctx["high_dump_assets"] assert "BTC" in ctx["high_fear_assets"] assert ctx["market_dump_risk"] == 80.0 assert ctx["market_fear"] == 80.0