Files
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

536 lines
21 KiB
Python

"""Alertmanager E2E tests verifying integration with investigation pipeline.
Tests:
- Alertmanager config resolution from store and env
- Alertmanager verification (connectivity, status check)
- Alertmanager source detection in investigation context
- Alertmanager tools availability and structure
- Alert fixture validity (Alertmanager webhook format)
"""
from __future__ import annotations
import json
from pathlib import Path
from unittest.mock import patch
from integrations.catalog import (
classify_integrations as _classify_integrations,
)
from integrations.catalog import (
load_env_integrations as _load_env_integrations,
)
from tests.e2e.source_helpers import resolve_available_tool_sources
class TestAlertmanagerIntegrationResolution:
"""Test Alertmanager config resolution from multiple sources."""
def test_alertmanager_resolution_from_store(self):
"""Alertmanager integration correctly resolved from local store."""
integrations = [
{
"id": "alertmanager-prod",
"service": "alertmanager",
"status": "active",
"credentials": {
"base_url": "http://alertmanager.monitoring.svc:9093",
"bearer_token": "",
"username": "",
"password": "",
},
}
]
resolved = _classify_integrations(integrations)
assert "alertmanager" in resolved
assert resolved["alertmanager"]["base_url"] == "http://alertmanager.monitoring.svc:9093"
def test_alertmanager_resolution_with_bearer_token(self):
"""Alertmanager integration with bearer token is correctly resolved."""
integrations = [
{
"id": "alertmanager-auth",
"service": "alertmanager",
"status": "active",
"credentials": {
"base_url": "https://alertmanager.example.com",
"bearer_token": "my-secret-token",
},
}
]
resolved = _classify_integrations(integrations)
assert "alertmanager" in resolved
assert resolved["alertmanager"]["base_url"] == "https://alertmanager.example.com"
assert resolved["alertmanager"]["bearer_token"] == "my-secret-token"
def test_alertmanager_resolution_with_basic_auth(self):
"""Alertmanager integration with basic auth is correctly resolved."""
integrations = [
{
"id": "alertmanager-basic",
"service": "alertmanager",
"status": "active",
"credentials": {
"base_url": "https://alertmanager.example.com",
"username": "admin",
"password": "secret",
},
}
]
resolved = _classify_integrations(integrations)
assert "alertmanager" in resolved
assert resolved["alertmanager"]["username"] == "admin"
assert resolved["alertmanager"]["password"] == "secret"
def test_alertmanager_empty_base_url_skipped(self):
"""Alertmanager integration without base_url is safely skipped."""
integrations = [
{
"id": "bad-alertmanager",
"service": "alertmanager",
"status": "active",
"credentials": {
"base_url": "",
},
}
]
resolved = _classify_integrations(integrations)
assert "alertmanager" not in resolved
def test_alertmanager_inactive_integration_skipped(self):
"""Inactive Alertmanager integration is not resolved."""
integrations = [
{
"id": "alertmanager-inactive",
"service": "alertmanager",
"status": "inactive",
"credentials": {
"base_url": "http://alertmanager.example.com:9093",
},
}
]
resolved = _classify_integrations(integrations)
assert "alertmanager" not in resolved
def test_alertmanager_url_trailing_slash_normalized(self):
"""Trailing slash is stripped from Alertmanager base_url."""
integrations = [
{
"id": "alertmanager-slash",
"service": "alertmanager",
"status": "active",
"credentials": {
"base_url": "http://alertmanager.example.com:9093/",
},
}
]
resolved = _classify_integrations(integrations)
assert "alertmanager" in resolved
assert resolved["alertmanager"]["base_url"] == "http://alertmanager.example.com:9093"
class TestAlertmanagerEnvResolution:
"""Test Alertmanager config resolution from environment variables."""
def test_alertmanager_resolved_from_env(self, monkeypatch):
"""Alertmanager integration resolved from ALERTMANAGER_URL env var."""
monkeypatch.setenv("ALERTMANAGER_URL", "http://alertmanager.monitoring.svc:9093")
env_integrations = _load_env_integrations()
alertmanager_records = [i for i in env_integrations if i["service"] == "alertmanager"]
assert len(alertmanager_records) == 1
creds = alertmanager_records[0]["credentials"]
assert creds["base_url"] == "http://alertmanager.monitoring.svc:9093"
def test_alertmanager_bearer_token_from_env(self, monkeypatch):
"""Alertmanager bearer token loaded from env var."""
monkeypatch.setenv("ALERTMANAGER_URL", "https://alertmanager.example.com")
monkeypatch.setenv("ALERTMANAGER_BEARER_TOKEN", "env-token-123")
env_integrations = _load_env_integrations()
alertmanager_records = [i for i in env_integrations if i["service"] == "alertmanager"]
assert len(alertmanager_records) == 1
assert alertmanager_records[0]["credentials"]["bearer_token"] == "env-token-123"
def test_alertmanager_not_loaded_when_url_missing(self, monkeypatch):
"""Alertmanager integration is not loaded from env when ALERTMANAGER_URL is unset."""
monkeypatch.delenv("ALERTMANAGER_URL", raising=False)
env_integrations = _load_env_integrations()
alertmanager_records = [i for i in env_integrations if i["service"] == "alertmanager"]
assert len(alertmanager_records) == 0
class TestAlertmanagerToolSourceAvailability:
"""Test Alertmanager source availability in the tool-registry investigation path."""
def test_alertmanager_detected_when_configured(self):
"""Alertmanager source is available when integration is configured."""
resolved_integrations = {
"alertmanager": {
"base_url": "http://alertmanager.monitoring.svc:9093",
"bearer_token": "",
"username": "",
"password": "",
}
}
sources = resolve_available_tool_sources(resolved_integrations)
assert "alertmanager" in sources
assert sources["alertmanager"]["base_url"] == "http://alertmanager.monitoring.svc:9093"
def test_alertmanager_source_preserves_configured_filter_labels(self):
"""Alertmanager tool params preserve filter labels from resolved config."""
resolved_integrations = {
"alertmanager": {
"base_url": "http://alertmanager.monitoring.svc:9093",
"filter_labels": ['alertname="HighErrorRate"'],
}
}
sources = resolve_available_tool_sources(resolved_integrations)
assert "alertmanager" in sources
assert 'alertname="HighErrorRate"' in sources["alertmanager"]["filter_labels"]
def test_alertmanager_not_detected_when_unconfigured(self):
"""Alertmanager source is not included when integration is not configured."""
resolved_integrations = {}
sources = resolve_available_tool_sources(resolved_integrations)
assert "alertmanager" not in sources
def test_alertmanager_not_detected_when_base_url_empty(self):
"""Alertmanager source is not included when base_url is empty."""
resolved_integrations = {
"alertmanager": {
"base_url": "",
}
}
sources = resolve_available_tool_sources(resolved_integrations)
assert "alertmanager" not in sources
class TestAlertmanagerVerification:
"""Test Alertmanager integration verification flow."""
@patch("integrations.alertmanager.client.AlertmanagerClient.get_status")
def test_verify_alertmanager_success(self, mock_get_status, monkeypatch):
"""Alertmanager verification passes when status endpoint responds successfully."""
monkeypatch.setenv("ALERTMANAGER_URL", "http://alertmanager.monitoring.svc:9093")
mock_get_status.return_value = {
"success": True,
"status": {
"cluster": {"status": "ready"},
"versionInfo": {"version": "0.27.0"},
},
}
from integrations.verify import verify_integrations
results = verify_integrations(service="alertmanager")
assert len(results) >= 1
am_result = next((r for r in results if r["service"] == "alertmanager"), None)
assert am_result is not None
assert am_result["status"] == "passed"
assert "alertmanager" in am_result["detail"].lower()
@patch("integrations.alertmanager.client.AlertmanagerClient.get_status")
def test_verify_alertmanager_failure(self, mock_get_status, monkeypatch):
"""Alertmanager verification fails when status endpoint is unreachable."""
monkeypatch.setenv("ALERTMANAGER_URL", "http://alertmanager.monitoring.svc:9093")
mock_get_status.return_value = {
"success": False,
"error": "Connection refused",
}
from integrations.verify import verify_integrations
results = verify_integrations(service="alertmanager")
am_result = next((r for r in results if r["service"] == "alertmanager"), None)
assert am_result is not None
assert am_result["status"] == "failed"
def test_verify_alertmanager_missing_when_not_configured(self, monkeypatch):
"""Alertmanager verification returns 'missing' when not configured."""
monkeypatch.delenv("ALERTMANAGER_URL", raising=False)
from integrations.verify import verify_integrations
results = verify_integrations(service="alertmanager")
am_result = next((r for r in results if r["service"] == "alertmanager"), None)
assert am_result is not None
assert am_result["status"] == "missing"
def test_verify_integrations_result_structure(self, monkeypatch):
"""Verify integrations result has expected fields for alertmanager."""
monkeypatch.delenv("ALERTMANAGER_URL", raising=False)
from integrations.verify import verify_integrations
results = verify_integrations(service="alertmanager")
assert isinstance(results, list)
for result in results:
if result["service"] == "alertmanager":
assert "service" in result
assert "source" in result
assert "status" in result
assert "detail" in result
assert result["status"] in ("passed", "missing", "failed")
class TestAlertmanagerToolsAvailability:
"""Test Alertmanager tools are importable and structurally correct."""
def test_alertmanager_alerts_tool_importable(self):
"""AlertmanagerAlertsTool can be imported and is correctly typed."""
from integrations.alertmanager.tools import AlertmanagerAlertsTool, alertmanager_alerts
assert alertmanager_alerts is not None
assert isinstance(alertmanager_alerts, AlertmanagerAlertsTool)
assert alertmanager_alerts.name == "alertmanager_alerts"
assert alertmanager_alerts.source == "alertmanager"
def test_alertmanager_silences_tool_importable(self):
"""AlertmanagerSilencesTool can be imported and is correctly typed."""
from integrations.alertmanager.tools import (
AlertmanagerSilencesTool,
alertmanager_silences,
)
assert alertmanager_silences is not None
assert isinstance(alertmanager_silences, AlertmanagerSilencesTool)
assert alertmanager_silences.name == "alertmanager_silences"
assert alertmanager_silences.source == "alertmanager"
def test_alertmanager_alerts_tool_not_available_without_source(self):
"""AlertmanagerAlertsTool reports unavailable when source has no connection_verified."""
from integrations.alertmanager.tools import alertmanager_alerts
assert not alertmanager_alerts.is_available({})
assert not alertmanager_alerts.is_available({"alertmanager": {}})
assert not alertmanager_alerts.is_available(
{"alertmanager": {"connection_verified": False}}
)
def test_alertmanager_alerts_tool_available_with_verified_source(self):
"""AlertmanagerAlertsTool is available when alertmanager source is connection_verified."""
from integrations.alertmanager.tools import alertmanager_alerts
sources = {"alertmanager": {"connection_verified": True, "base_url": "http://am:9093"}}
assert alertmanager_alerts.is_available(sources)
def test_alertmanager_alerts_tool_extract_params(self):
"""AlertmanagerAlertsTool.extract_params returns correct fields from source."""
from integrations.alertmanager.tools import alertmanager_alerts
sources = {
"alertmanager": {
"base_url": "http://alertmanager.monitoring.svc:9093",
"bearer_token": "tok",
"username": "",
"password": "",
"filter_labels": ['alertname="HighErrorRate"'],
"connection_verified": True,
}
}
params = alertmanager_alerts.extract_params(sources)
assert params["base_url"] == "http://alertmanager.monitoring.svc:9093"
assert params["bearer_token"] == "tok"
assert params["active"] is True
@patch("integrations.alertmanager.client.AlertmanagerClient.list_alerts")
def test_alertmanager_alerts_tool_run_success(self, mock_list_alerts):
"""AlertmanagerAlertsTool.run returns structured result on success."""
from integrations.alertmanager.tools import alertmanager_alerts
mock_list_alerts.return_value = {
"success": True,
"alerts": [
{
"fingerprint": "abc123",
"status": "active",
"labels": {"alertname": "HighErrorRate", "job": "api"},
"annotations": {"summary": "High error rate"},
"starts_at": "2024-01-15T10:25:00Z",
"ends_at": "0001-01-01T00:00:00Z",
"generator_url": "http://prometheus:9090/graph",
}
],
"total": 1,
}
result = alertmanager_alerts.run(
base_url="http://alertmanager.monitoring.svc:9093",
)
assert result["available"] is True
assert result["source"] == "alertmanager"
assert result["total"] == 1
assert len(result["alerts"]) == 1
assert len(result["firing_alerts"]) == 1
def test_alertmanager_alerts_tool_run_missing_base_url(self):
"""AlertmanagerAlertsTool.run returns unavailable when base_url is empty."""
from integrations.alertmanager.tools import alertmanager_alerts
result = alertmanager_alerts.run(base_url="")
assert result["available"] is False
assert "not configured" in result["error"]
@patch("integrations.alertmanager.client.AlertmanagerClient.list_silences")
def test_alertmanager_silences_tool_run_success(self, mock_list_silences):
"""AlertmanagerSilencesTool.run returns structured result on success."""
from integrations.alertmanager.tools import alertmanager_silences
mock_list_silences.return_value = {
"success": True,
"silences": [
{
"id": "silence-123",
"status": "active",
"matchers": [{"name": "alertname", "value": "HighErrorRate", "isEqual": True}],
"comment": "Planned maintenance",
"created_by": "oncall-engineer",
"starts_at": "2024-01-15T08:00:00Z",
"ends_at": "2024-01-15T12:00:00Z",
}
],
"active_silences": [
{
"id": "silence-123",
"status": "active",
"matchers": [{"name": "alertname", "value": "HighErrorRate", "isEqual": True}],
"comment": "Planned maintenance",
"created_by": "oncall-engineer",
"starts_at": "2024-01-15T08:00:00Z",
"ends_at": "2024-01-15T12:00:00Z",
}
],
"total": 1,
}
result = alertmanager_silences.run(
base_url="http://alertmanager.monitoring.svc:9093",
)
assert result["available"] is True
assert result["source"] == "alertmanager_silences"
assert result["total"] == 1
assert len(result["active_silences"]) == 1
class TestAlertmanagerAlertFixture:
"""Test the Alertmanager alert fixture is valid and parseable."""
def test_alertmanager_alert_fixture_is_valid_json(self):
"""Alertmanager alert fixture is valid JSON."""
fixture_path = Path(__file__).parent / "alertmanager_alert.json"
assert fixture_path.exists(), f"Alert fixture not found at {fixture_path}"
with fixture_path.open() as f:
alert = json.load(f)
assert isinstance(alert, dict)
def test_alertmanager_alert_fixture_has_required_webhook_fields(self):
"""Alert fixture contains Alertmanager webhook format fields."""
fixture_path = Path(__file__).parent / "alertmanager_alert.json"
with fixture_path.open() as f:
alert = json.load(f)
assert "version" in alert
assert "status" in alert
assert "commonLabels" in alert
assert "commonAnnotations" in alert
assert "alerts" in alert
assert isinstance(alert["alerts"], list)
assert len(alert["alerts"]) > 0
def test_alertmanager_alert_fixture_individual_alerts_have_fingerprint(self):
"""Each alert in the fixture has Prometheus-specific fingerprint and generatorURL."""
fixture_path = Path(__file__).parent / "alertmanager_alert.json"
with fixture_path.open() as f:
alert = json.load(f)
for individual_alert in alert["alerts"]:
assert "fingerprint" in individual_alert
assert "generatorURL" in individual_alert
assert "startsAt" in individual_alert
def test_alertmanager_alert_fixture_has_service_labels(self):
"""Alert fixture contains service-level labels useful for investigation context."""
fixture_path = Path(__file__).parent / "alertmanager_alert.json"
with fixture_path.open() as f:
alert = json.load(f)
common_labels = alert.get("commonLabels", {})
assert "alertname" in common_labels
assert "severity" in common_labels
assert "job" in common_labels
class TestAlertmanagerIntegrationConfig:
"""Test AlertmanagerIntegrationConfig model validation."""
def test_alertmanager_config_creation(self):
"""AlertmanagerIntegrationConfig validates correctly with all fields."""
from integrations.config_models import AlertmanagerIntegrationConfig
config = AlertmanagerIntegrationConfig(
base_url="http://alertmanager.monitoring.svc:9093",
bearer_token="test-token",
integration_id="test-id",
)
assert config.base_url == "http://alertmanager.monitoring.svc:9093"
assert config.bearer_token == "test-token"
assert config.integration_id == "test-id"
assert config.username == ""
assert config.password == ""
def test_alertmanager_config_url_normalization(self):
"""AlertmanagerIntegrationConfig strips whitespace and trailing slash from base_url."""
from integrations.config_models import AlertmanagerIntegrationConfig
config = AlertmanagerIntegrationConfig(
base_url=" http://alertmanager.example.com:9093/ ",
)
assert config.base_url == "http://alertmanager.example.com:9093"
def test_alertmanager_config_basic_auth(self):
"""AlertmanagerIntegrationConfig stores basic auth credentials."""
from integrations.config_models import AlertmanagerIntegrationConfig
config = AlertmanagerIntegrationConfig(
base_url="http://alertmanager.example.com:9093",
username="admin",
password="secret",
)
assert config.username == "admin"
assert config.password == "secret"
assert config.bearer_token == ""