# -*- coding: utf-8 -*- """ Tests für Satellite Metrics System. """ import pytest from datetime import datetime, timedelta from trixy_core.satellite.metrics import ( SatelliteMetricsCollector, MetricType, MetricValue, SatelliteMetrics, MetricsAggregator, AggregationType, AggregatedMetric, TimeWindow, ) class TestMetricType: """Tests für MetricType.""" def test_metric_types(self): """Testet Metrik-Typen.""" assert MetricType.COUNTER.name == "COUNTER" assert MetricType.GAUGE.name == "GAUGE" assert MetricType.HISTOGRAM.name == "HISTOGRAM" assert MetricType.TIMING.name == "TIMING" class TestMetricValue: """Tests für MetricValue.""" def test_value_creation(self): """Testet Value-Erstellung.""" value = MetricValue( name="latency", value=15.5, metric_type=MetricType.TIMING, unit="ms", ) assert value.name == "latency" assert value.value == 15.5 assert value.metric_type == MetricType.TIMING assert value.unit == "ms" def test_value_with_labels(self): """Testet Value mit Labels.""" value = MetricValue( name="requests", value=100, labels={"endpoint": "/api/data"}, ) assert value.labels["endpoint"] == "/api/data" def test_to_dict(self): """Testet Dictionary-Konvertierung.""" value = MetricValue( name="test", value=42.0, metric_type=MetricType.GAUGE, ) d = value.to_dict() assert d["name"] == "test" assert d["value"] == 42.0 assert d["type"] == "GAUGE" class TestSatelliteMetrics: """Tests für SatelliteMetrics.""" @pytest.fixture def metrics(self): """Erstellt SatelliteMetrics.""" return SatelliteMetrics(satellite_id="sat-001") def test_initial_metrics(self, metrics): """Testet initiale Metriken.""" assert metrics.satellite_id == "sat-001" assert metrics.latency_ms == 0.0 assert metrics.messages_sent == 0 assert metrics.error_count == 0 def test_record_latency(self, metrics): """Testet Latenz-Aufzeichnung.""" metrics.record_latency(15.5) assert metrics.latency_ms == 15.5 assert len(metrics.latency_history) == 1 def test_record_error(self, metrics): """Testet Fehler-Aufzeichnung.""" metrics.record_error("Connection timeout") assert metrics.error_count == 1 assert metrics.last_error == "Connection timeout" assert metrics.last_error_time is not None def test_get_avg_latency(self, metrics): """Testet Durchschnitts-Latenz.""" metrics.record_latency(10.0) metrics.record_latency(20.0) metrics.record_latency(30.0) avg = metrics.get_avg_latency(300) assert avg == 20.0 def test_to_dict(self, metrics): """Testet Dictionary-Konvertierung.""" metrics.latency_ms = 15.0 metrics.messages_sent = 100 d = metrics.to_dict() assert d["satellite_id"] == "sat-001" assert d["network"]["latency_ms"] == 15.0 assert d["network"]["messages_sent"] == 100 class TestSatelliteMetricsCollector: """Tests für SatelliteMetricsCollector.""" @pytest.fixture def collector(self): """Erstellt MetricsCollector.""" return SatelliteMetricsCollector() def test_record_latency(self, collector): """Testet Latenz-Aufzeichnung.""" collector.record_latency("sat-001", 15.5) metrics = collector.get_metrics("sat-001") assert metrics is not None assert metrics.latency_ms == 15.5 def test_record_message_sent(self, collector): """Testet gesendete Nachricht.""" collector.record_message_sent("sat-001", 256) metrics = collector.get_metrics("sat-001") assert metrics.messages_sent == 1 assert metrics.bytes_sent == 256 def test_record_message_received(self, collector): """Testet empfangene Nachricht.""" collector.record_message_received("sat-001", 512) metrics = collector.get_metrics("sat-001") assert metrics.messages_received == 1 assert metrics.bytes_received == 512 def test_record_heartbeat(self, collector): """Testet Heartbeat-Aufzeichnung.""" collector.record_heartbeat("sat-001") metrics = collector.get_metrics("sat-001") assert metrics.last_heartbeat is not None def test_record_reconnect(self, collector): """Testet Reconnect-Aufzeichnung.""" collector.record_reconnect("sat-001") metrics = collector.get_metrics("sat-001") assert metrics.reconnect_count == 1 def test_record_error(self, collector): """Testet Fehler-Aufzeichnung.""" collector.record_error("sat-001", "Connection lost") metrics = collector.get_metrics("sat-001") assert metrics.error_count == 1 def test_record_packet_loss(self, collector): """Testet Paketverlust-Aufzeichnung.""" collector.record_packet_loss("sat-001", 2.5) metrics = collector.get_metrics("sat-001") assert metrics.packet_loss_percent == 2.5 def test_record_bandwidth(self, collector): """Testet Bandbreiten-Aufzeichnung.""" collector.record_bandwidth("sat-001", 1000.0) metrics = collector.get_metrics("sat-001") assert metrics.bandwidth_kbps == 1000.0 def test_record_custom(self, collector): """Testet benutzerdefinierte Metrik.""" collector.record_custom( "sat-001", "cpu_usage", 45.5, metric_type=MetricType.GAUGE, unit="%", ) history = collector.get_custom_metric("sat-001", "cpu_usage") assert len(history) == 1 assert history[0].value == 45.5 def test_get_all_metrics(self, collector): """Testet Abruf aller Metriken.""" collector.record_latency("sat-001", 10.0) collector.record_latency("sat-002", 20.0) all_metrics = collector.get_all_metrics() assert len(all_metrics) == 2 assert "sat-001" in all_metrics assert "sat-002" in all_metrics def test_remove_satellite(self, collector): """Testet Satellite-Entfernung.""" collector.record_latency("sat-001", 10.0) assert collector.remove_satellite("sat-001") assert collector.get_metrics("sat-001") is None def test_get_summary(self, collector): """Testet Zusammenfassung.""" collector.record_latency("sat-001", 10.0) collector.record_latency("sat-002", 20.0) collector.record_error("sat-001", "Error") summary = collector.get_summary() assert summary["satellite_count"] == 2 assert summary["avg_latency_ms"] == 15.0 assert summary["total_errors"] == 1 def test_on_metric_callback(self, collector): """Testet Metrik-Callback.""" received = [] def on_metric(sat_id, value): received.append((sat_id, value.name)) collector.on_metric(on_metric) collector.record_latency("sat-001", 10.0) assert ("sat-001", "latency") in received def test_clear_all(self, collector): """Testet Löschen aller Metriken.""" collector.record_latency("sat-001", 10.0) collector.record_latency("sat-002", 20.0) collector.clear_all() assert collector.satellite_count == 0 class TestTimeWindow: """Tests für TimeWindow.""" def test_window_values(self): """Testet Window-Werte.""" assert TimeWindow.MINUTE.value == 60 assert TimeWindow.FIVE_MINUTES.value == 300 assert TimeWindow.HOUR.value == 3600 assert TimeWindow.DAY.value == 86400 class TestAggregationType: """Tests für AggregationType.""" def test_aggregation_types(self): """Testet Aggregations-Typen.""" assert AggregationType.SUM.name == "SUM" assert AggregationType.AVG.name == "AVG" assert AggregationType.MIN.name == "MIN" assert AggregationType.MAX.name == "MAX" assert AggregationType.P95.name == "P95" class TestAggregatedMetric: """Tests für AggregatedMetric.""" def test_metric_creation(self): """Testet Metrik-Erstellung.""" metric = AggregatedMetric( name="latency", aggregation=AggregationType.AVG, value=15.5, window=TimeWindow.FIVE_MINUTES, sample_count=100, start_time=datetime.now() - timedelta(minutes=5), end_time=datetime.now(), ) assert metric.name == "latency" assert metric.value == 15.5 assert metric.sample_count == 100 def test_to_dict(self): """Testet Dictionary-Konvertierung.""" metric = AggregatedMetric( name="test", aggregation=AggregationType.AVG, value=42.0, window=TimeWindow.MINUTE, sample_count=10, start_time=datetime.now(), end_time=datetime.now(), ) d = metric.to_dict() assert d["name"] == "test" assert d["aggregation"] == "AVG" assert d["value"] == 42.0 class TestMetricsAggregator: """Tests für MetricsAggregator.""" @pytest.fixture def collector(self): """Erstellt Collector mit Testdaten.""" collector = SatelliteMetricsCollector() # Testdaten hinzufügen collector.record_latency("sat-001", 10.0) collector.record_latency("sat-002", 20.0) collector.record_latency("sat-003", 30.0) collector.record_error("sat-001", "Error 1") collector.record_error("sat-002", "Error 2") return collector @pytest.fixture def aggregator(self, collector): """Erstellt Aggregator.""" return MetricsAggregator(collector) def test_aggregate_avg(self, aggregator): """Testet Durchschnitts-Aggregation.""" result = aggregator.aggregate( "latency", AggregationType.AVG, TimeWindow.HOUR, ) assert result.value == 20.0 assert result.sample_count == 3 def test_aggregate_sum(self, aggregator): """Testet Summen-Aggregation.""" result = aggregator.aggregate( "latency", AggregationType.SUM, TimeWindow.HOUR, ) assert result.value == 60.0 def test_aggregate_min(self, aggregator): """Testet Minimum-Aggregation.""" result = aggregator.aggregate( "latency", AggregationType.MIN, TimeWindow.HOUR, ) assert result.value == 10.0 def test_aggregate_max(self, aggregator): """Testet Maximum-Aggregation.""" result = aggregator.aggregate( "latency", AggregationType.MAX, TimeWindow.HOUR, ) assert result.value == 30.0 def test_aggregate_count(self, aggregator): """Testet Zähl-Aggregation.""" result = aggregator.aggregate( "latency", AggregationType.COUNT, TimeWindow.HOUR, ) assert result.value == 3.0 def test_aggregate_median(self, aggregator): """Testet Median-Aggregation.""" result = aggregator.aggregate( "latency", AggregationType.MEDIAN, TimeWindow.HOUR, ) assert result.value == 20.0 def test_aggregate_multiple(self, aggregator): """Testet mehrfache Aggregation.""" results = aggregator.aggregate_multiple( ["latency", "error_count"], [AggregationType.AVG, AggregationType.SUM], ) assert "latency" in results assert "error_count" in results assert "AVG" in results["latency"] assert "SUM" in results["latency"] def test_get_top_satellites(self, aggregator): """Testet Top-Satellites.""" top = aggregator.get_top_satellites("latency", limit=2) assert len(top) == 2 # Höchste Latenz zuerst assert top[0][1] == 30.0 def test_get_top_satellites_ascending(self, aggregator): """Testet Top-Satellites aufsteigend.""" top = aggregator.get_top_satellites( "latency", limit=2, ascending=True, ) # Niedrigste Latenz zuerst assert top[0][1] == 10.0 def test_get_metric_distribution(self, aggregator): """Testet Metrik-Verteilung.""" dist = aggregator.get_metric_distribution( "latency", buckets=3, ) assert "buckets" in dist assert dist["min"] == 10.0 assert dist["max"] == 30.0 assert dist["count"] == 3 def test_get_satellite_comparison(self, aggregator): """Testet Satellite-Vergleich.""" comparison = aggregator.get_satellite_comparison( "latency", ["sat-001", "sat-002"], ) assert "sat-001" in comparison assert "sat-002" in comparison assert comparison["sat-001"]["current"] == 10.0 assert comparison["sat-002"]["current"] == 20.0 def test_get_health_summary(self, aggregator): """Testet Gesundheits-Übersicht.""" summary = aggregator.get_health_summary() assert "status" in summary assert "metrics" in summary assert "satellite_count" in summary def test_cache_functionality(self, aggregator): """Testet Cache-Funktionalität.""" # Erste Abfrage result1 = aggregator.aggregate("latency", AggregationType.AVG) # Zweite Abfrage (sollte aus Cache kommen) result2 = aggregator.aggregate("latency", AggregationType.AVG) assert result1.value == result2.value def test_clear_cache(self, aggregator): """Testet Cache-Löschen.""" aggregator.aggregate("latency", AggregationType.AVG) assert len(aggregator._cache) > 0 aggregator.clear_cache() assert len(aggregator._cache) == 0