mirror of
https://github.com/krkn-chaos/krkn.git
synced 2026-04-07 11:17:23 +00:00
244 lines
7.6 KiB
Python
244 lines
7.6 KiB
Python
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Test suite for krkn.prometheus.client module
|
|
|
|
Validates that alert key validation and metric query routing
|
|
use sorted() correctly (fixes #1182).
|
|
|
|
Usage:
|
|
python -m unittest tests/test_prometheus_client.py -v
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import MagicMock
|
|
|
|
from krkn.prometheus import client
|
|
|
|
|
|
class TestAlertsKeyValidation(unittest.TestCase):
|
|
"""Tests for alert key validation in the alerts() function."""
|
|
|
|
def setUp(self):
|
|
self.prom_cli = MagicMock()
|
|
self.elastic = MagicMock()
|
|
self.run_uuid = "test-uuid"
|
|
self.start_time = 1000000.0
|
|
self.end_time = 1000060.0
|
|
self.elastic_alerts_index = "test-index"
|
|
|
|
def _write_alert_profile(self, content):
|
|
"""Write a YAML alert profile to a temp file and return its path."""
|
|
f = tempfile.NamedTemporaryFile(
|
|
mode="w", suffix=".yaml", delete=False
|
|
)
|
|
f.write(content)
|
|
f.close()
|
|
return f.name
|
|
|
|
def test_valid_alert_keys_are_processed(self):
|
|
"""Alerts with correct keys (expr, description, severity) should be processed."""
|
|
profile_path = self._write_alert_profile(
|
|
'- expr: "up == 0"\n'
|
|
' description: "target down"\n'
|
|
' severity: "critical"\n'
|
|
)
|
|
try:
|
|
self.prom_cli.process_alert.return_value = (
|
|
self.start_time,
|
|
"test-alert",
|
|
)
|
|
self.elastic.push_alert.return_value = 0
|
|
|
|
client.alerts(
|
|
self.prom_cli,
|
|
self.elastic,
|
|
self.run_uuid,
|
|
self.start_time,
|
|
self.end_time,
|
|
profile_path,
|
|
self.elastic_alerts_index,
|
|
)
|
|
|
|
self.prom_cli.process_alert.assert_called_once()
|
|
finally:
|
|
os.unlink(profile_path)
|
|
|
|
def test_invalid_alert_keys_are_skipped(self):
|
|
"""Alerts with wrong keys should be skipped and not processed."""
|
|
profile_path = self._write_alert_profile(
|
|
'- wrong_key: "up == 0"\n'
|
|
' another_bad_key: "test"\n'
|
|
' third_bad_key: "info"\n'
|
|
)
|
|
try:
|
|
client.alerts(
|
|
self.prom_cli,
|
|
self.elastic,
|
|
self.run_uuid,
|
|
self.start_time,
|
|
self.end_time,
|
|
profile_path,
|
|
self.elastic_alerts_index,
|
|
)
|
|
|
|
self.prom_cli.process_alert.assert_not_called()
|
|
finally:
|
|
os.unlink(profile_path)
|
|
|
|
def test_mixed_valid_and_invalid_alerts(self):
|
|
"""Only alerts with correct keys should be processed; invalid ones skipped."""
|
|
profile_path = self._write_alert_profile(
|
|
'- wrong_key: "bad"\n'
|
|
' other: "bad"\n'
|
|
' extra: "bad"\n'
|
|
'- expr: "up == 0"\n'
|
|
' description: "target down"\n'
|
|
' severity: "critical"\n'
|
|
)
|
|
try:
|
|
self.prom_cli.process_alert.return_value = (None, None)
|
|
|
|
client.alerts(
|
|
self.prom_cli,
|
|
self.elastic,
|
|
self.run_uuid,
|
|
self.start_time,
|
|
self.end_time,
|
|
profile_path,
|
|
self.elastic_alerts_index,
|
|
)
|
|
|
|
self.assertEqual(self.prom_cli.process_alert.call_count, 1)
|
|
finally:
|
|
os.unlink(profile_path)
|
|
|
|
def test_alert_keys_different_count_are_skipped(self):
|
|
"""Alerts with too many or too few keys should be skipped."""
|
|
profile_path = self._write_alert_profile(
|
|
'- expr: "up == 0"\n'
|
|
' description: "target down"\n'
|
|
)
|
|
try:
|
|
client.alerts(
|
|
self.prom_cli,
|
|
self.elastic,
|
|
self.run_uuid,
|
|
self.start_time,
|
|
self.end_time,
|
|
profile_path,
|
|
self.elastic_alerts_index,
|
|
)
|
|
|
|
self.prom_cli.process_alert.assert_not_called()
|
|
finally:
|
|
os.unlink(profile_path)
|
|
|
|
|
|
class TestMetricsQueryRouting(unittest.TestCase):
|
|
"""Tests for metric query routing in the metrics() function."""
|
|
|
|
def setUp(self):
|
|
self.prom_cli = MagicMock()
|
|
self.elastic = MagicMock()
|
|
self.run_uuid = "test-uuid"
|
|
self.start_time = 1000000.0
|
|
self.end_time = 1000060.0
|
|
self.elastic_metrics_index = "test-metrics-index"
|
|
self.telemetry_json = json.dumps({
|
|
"scenarios": [],
|
|
"health_checks": [],
|
|
"virt_checks": [],
|
|
})
|
|
|
|
def _write_metrics_profile(self, metrics_list):
|
|
"""Write a YAML metrics profile to a temp file and return its path."""
|
|
import yaml
|
|
f = tempfile.NamedTemporaryFile(
|
|
mode="w", suffix=".yaml", delete=False
|
|
)
|
|
yaml.dump({"metrics": metrics_list}, f)
|
|
f.close()
|
|
return f.name
|
|
|
|
def test_range_query_with_query_and_metricName_keys(self):
|
|
"""Metric with only 'query' and 'metricName' keys should use range query."""
|
|
profile_path = self._write_metrics_profile([
|
|
{"query": "up", "metricName": "target_up"},
|
|
])
|
|
try:
|
|
self.prom_cli.process_prom_query_in_range.return_value = []
|
|
self.elastic.upload_metrics_to_elasticsearch.return_value = 0
|
|
|
|
client.metrics(
|
|
self.prom_cli,
|
|
self.elastic,
|
|
self.run_uuid,
|
|
self.start_time,
|
|
self.end_time,
|
|
profile_path,
|
|
self.elastic_metrics_index,
|
|
self.telemetry_json,
|
|
)
|
|
|
|
self.prom_cli.process_prom_query_in_range.assert_called_once()
|
|
self.prom_cli.process_query.assert_not_called()
|
|
finally:
|
|
os.unlink(profile_path)
|
|
|
|
def test_instant_query_with_instant_flag(self):
|
|
"""Metric with 'instant: true' should use instant query."""
|
|
profile_path = self._write_metrics_profile([
|
|
{"query": "up", "metricName": "target_up", "instant": True},
|
|
])
|
|
try:
|
|
self.prom_cli.process_query.return_value = []
|
|
self.elastic.upload_metrics_to_elasticsearch.return_value = 0
|
|
|
|
client.metrics(
|
|
self.prom_cli,
|
|
self.elastic,
|
|
self.run_uuid,
|
|
self.start_time,
|
|
self.end_time,
|
|
profile_path,
|
|
self.elastic_metrics_index,
|
|
self.telemetry_json,
|
|
)
|
|
|
|
self.prom_cli.process_query.assert_called_once()
|
|
self.prom_cli.process_prom_query_in_range.assert_not_called()
|
|
finally:
|
|
os.unlink(profile_path)
|
|
|
|
def test_extra_keys_skip_range_query(self):
|
|
"""Metric with extra unknown keys should not match range query branch."""
|
|
profile_path = self._write_metrics_profile([
|
|
{"query": "up", "metricName": "target_up", "unknownKey": "val"},
|
|
])
|
|
try:
|
|
self.elastic.upload_metrics_to_elasticsearch.return_value = 0
|
|
|
|
client.metrics(
|
|
self.prom_cli,
|
|
self.elastic,
|
|
self.run_uuid,
|
|
self.start_time,
|
|
self.end_time,
|
|
profile_path,
|
|
self.elastic_metrics_index,
|
|
self.telemetry_json,
|
|
)
|
|
|
|
self.prom_cli.process_prom_query_in_range.assert_not_called()
|
|
self.prom_cli.process_query.assert_not_called()
|
|
finally:
|
|
os.unlink(profile_path)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|