import json import httpx import pytest from app.checkmk import (COLUMNS, FAILURE_UNEXPECTED, FAILURE_UNREACHABLE, HOST_COLUMNS, CheckmkClient, CheckmkError, CheckmkUnreachable) from app.models import HOST_SERVICE_NAME BASE = "https://cmk.local/prod/check_mk/api/1.0" def make_client(handler): return CheckmkClient("https://cmk.local", "prod", "bot", "s3cret", transport=httpx.MockTransport(handler)) def host_item(name="srv1", state=1, **extra): data = {"name": name, "state": state, "plugin_output": "DOWN - no route", "last_state_change": 42, "scheduled_downtime_depth": 0, "acknowledged": 0} data.update(extra) return {"extensions": data} async def test_problem_hosts_posts_query_and_columns(): seen = {} def handler(request): seen["url"] = str(request.url) seen["body"] = json.loads(request.content) return httpx.Response(200, json={"value": [host_item()]}) hosts = await make_client(handler).problem_hosts() assert seen["url"] == f"{BASE}/domain-types/host/collections/all" assert seen["body"]["columns"] == HOST_COLUMNS assert seen["body"]["query"] == {"op": "!=", "left": "state", "right": "0"} assert [(h.host, h.state, h.output, h.last_state_change) for h in hosts] == [("srv1", 2, "DOWN - no route", 42)] async def test_sample_host_get_fallback_repeats_columns_param(): seen = [] def handler(request): seen.append(request) if request.method == "POST": return httpx.Response(400, text="no") return httpx.Response(200, json={"value": [host_item()]}) row = await make_client(handler).sample_host() assert row["name"] == "srv1" assert seen[-1].method == "GET" and seen[-1].url.params.get_list("columns") == HOST_COLUMNS async def test_host_states_map_to_internal_numbers(): body = {"value": [host_item("a", 1), host_item("b", 2)]} hosts = await make_client(lambda r: httpx.Response(200, json=body)).problem_hosts() assert [h.state for h in hosts] == [2, 3] async def test_host_downtime_and_ack_flags(): body = {"value": [host_item(scheduled_downtime_depth=1, acknowledged=1)]} (host,) = await make_client(lambda r: httpx.Response(200, json=body)).problem_hosts() assert host.in_downtime and host.acknowledged async def test_host_as_service_uses_host_service_name_and_is_hard(): (host,) = await make_client(lambda r: httpx.Response(200, json={"value": [host_item(acknowledged=1)]}) ).problem_hosts() service = host.as_service() assert service.service == HOST_SERVICE_NAME and service.hard and service.acknowledged and not service.flapping async def test_service_columns_include_acknowledged_and_flag_is_parsed(): assert "acknowledged" in COLUMNS item = {"extensions": {"host_name": "h", "description": "s", "state": 2, "state_type": 1, "plugin_output": "x", "last_state_change": 1, "scheduled_downtime_depth": 0, "is_flapping": 0, "acknowledged": 1}} (service,) = await make_client(lambda r: httpx.Response(200, json={"value": [item]})).problem_services() assert service.acknowledged is True async def test_missing_acknowledged_column_means_not_acknowledged(): item = {"extensions": {"host_name": "h", "description": "s", "state": 2}} (service,) = await make_client(lambda r: httpx.Response(200, json={"value": [item]})).problem_services() assert service.acknowledged is False def failing(error): def handler(request): raise error return handler async def test_transport_error_is_unreachable(): with pytest.raises(CheckmkUnreachable) as caught: await make_client(failing(httpx.ConnectError("boom"))).problem_services() assert caught.value.kind == FAILURE_UNREACHABLE and caught.value.brief == "ConnectError" async def test_timeout_is_unreachable(): with pytest.raises(CheckmkError) as caught: await make_client(failing(httpx.ReadTimeout("slow"))).problem_hosts() assert caught.value.kind == FAILURE_UNREACHABLE async def test_http_5xx_is_unreachable_and_4xx_is_unexpected(): with pytest.raises(CheckmkError) as caught: await make_client(lambda r: httpx.Response(503, text="busy")).problem_services() assert caught.value.kind == FAILURE_UNREACHABLE and caught.value.brief == "HTTP 503" with pytest.raises(CheckmkError) as caught: await make_client(lambda r: httpx.Response(401, text="geheim: s3cret")).problem_services() assert caught.value.kind == FAILURE_UNEXPECTED and caught.value.brief == "HTTP 401" assert "s3cret" not in caught.value.brief async def test_non_json_and_wrong_schema_are_unexpected(): with pytest.raises(CheckmkError) as caught: await make_client(lambda r: httpx.Response(200, text="")).problem_services() assert caught.value.kind == FAILURE_UNEXPECTED with pytest.raises(CheckmkError) as caught: await make_client(lambda r: httpx.Response(200, json={"value": [{"extensions": {"foo": 1}}]}) ).problem_services() assert caught.value.kind == FAILURE_UNEXPECTED and caught.value.brief == "Antwortformat unerwartet" with pytest.raises(CheckmkError): await make_client(lambda r: httpx.Response(200, json=[1, 2])).problem_hosts()