"""Tests for output sinks""" import pytest from unittest.mock import AsyncMock, MagicMock, patch from sentiment_engine.output.hazelcast_sink import HazelcastSink from sentiment_engine.output.clickhouse_sink import ClickHouseSink from sentiment_engine.output.manager import OutputManager from sentiment_engine.schemas.output import SentimentOutput, AssetSentiment, MarketSentiment, IndustrySentiment, PumpDumpScore, VelocityMetrics, EventFlag class TestHazelcastSink: """Tests for HazelcastSink""" @pytest.fixture def sink(self): return HazelcastSink() @pytest.mark.asyncio async def test_publish_scores(self, sink): """Test publishing scores to Hazelcast""" # This would require a running Hazelcast instance # For now, just verify the sink can be instantiated assert sink is not None def test_prepare_exf_data(self, sink): """Test preparing ExF data structure""" from sentiment_engine.schemas.output import SentimentOutput, MarketSentiment from datetime import datetime 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, top_pump_assets=["BTC", "ETH"], top_dump_assets=[], dominant_events=[], industry_breakdown={}, last_update_ts=1234567890.0, total_sources=10, total_assets=50 ) output = SentimentOutput( timestamp=1234567890.0, market=market, assets={} ) # Verify ACB signals extraction acb = output.get_acb_signals() assert "market_sentiment_state" in acb assert "aggregate_pump_risk" in acb assert "fear_state" in acb assert "greed_state" in acb assert "hype_velocity" in acb class TestClickHouseSink: """Tests for ClickHouseSink""" @pytest.fixture def sink(self): return ClickHouseSink() def test_buffer_raw_item(self, sink): """Test buffering raw item for batch insert""" from sentiment_engine.schemas.payload import NormalizedPayload, SourceType, AssetMention, EngagementMetrics from datetime import datetime payload = NormalizedPayload( source_id="test", source_type=SourceType.NEWS, source_credibility_base=0.8, ingest_ts=1234567890.0, publish_ts=1234567880.0, asset_mentions=[AssetMention(asset_id="BTC", mention_span=(0, 3), confidence=0.9, source_text="BTC", mention_type="ticker")], raw_text="Test article", title="Test", url="https://test.com", author="Test", content_length=100, language="en", metadata={} ) # This should not raise an error sink.buffer_raw_item(payload) assert len(sink._batch_buffer) == 1 def test_buffer_processed_item(self, sink): """Test buffering processed item for batch insert""" from sentiment_engine.schemas.processed import ProcessedItem, EntityExtraction, SentimentScores, EmotionScores, EventClassification, EventType, TemporalAnchor, CredibilityScore from datetime import datetime item = ProcessedItem( payload_id="test_1", source_id="test", source_type="news", ingest_ts=1234567890.0, publish_ts=1234567880.0, entities=[EntityExtraction(asset_id="BTC", mention_span=(0, 3), confidence=0.9, entity_type="ticker", canonical_name="BTC")], sentiment_per_asset={"BTC": SentimentScores(polarity=0.7, confidence=0.85, positive_prob=0.8, negative_prob=0.1, neutral_prob=0.1)}, emotions_per_asset={"BTC": EmotionScores(joy=0.8, fear=0.1, anger=0.05, greed=0.7, sadness=0.05, intensity=0.75)}, events=[EventClassification(event_type=EventType.LISTING, confidence=0.7, assets_involved=["BTC"], key_details={}, severity=0.5)], temporal=TemporalAnchor(event_time=None, time_horizon="immediate", is_breaking=True, is_scheduled=False), credibility=CredibilityScore(source_base=0.8, content_quality=0.8, engagement_authenticity=0.7, cross_source_corroboration=0.6, historical_accuracy=0.8, composite=0.78), processed_ts=1234567895.0, processing_latency_ms=45.2, model_versions={} ) sink.buffer_processed_item(item) assert len(sink._batch_buffer) == 1 def test_buffer_score_output(self, sink): """Test buffering scored output""" from sentiment_engine.schemas.output import SentimentOutput, MarketSentiment, AssetSentiment, PumpDumpScore, VelocityMetrics import time 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, top_pump_assets=["BTC", "ETH"], top_dump_assets=[], dominant_events=[], industry_breakdown={}, last_update_ts=time.time(), total_sources=10, total_assets=50 ) 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=time.time()), last_update_ts=time.time() ) output = SentimentOutput( timestamp=time.time(), market=market, assets={"BTC": asset} ) sink.buffer_score_output(output) assert len(sink._batch_buffer) >= 2 # asset + market