From a12f7f336288220ade5b84e5257b06c8c68fe569 Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Wed, 11 Feb 2026 07:13:01 +0100 Subject: [PATCH 1/2] Add tests --- pyproject.toml | 12 +++++ tests/__snapshots__/test_metrics.ambr | 77 +++++++++++++++++++++++++++ tests/conftest.py | 31 +++++++++++ tests/fixtures/metrics.txt | 61 +++++++++++++++++++++ tests/test_metrics.py | 16 ++++++ 5 files changed, 197 insertions(+) create mode 100644 tests/__snapshots__/test_metrics.ambr create mode 100644 tests/conftest.py create mode 100644 tests/fixtures/metrics.txt create mode 100644 tests/test_metrics.py diff --git a/pyproject.toml b/pyproject.toml index 1683da2..0be1622 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,9 @@ include = [ "/pythonkuma", ] +[[tool.hatch.envs.hatch-test.matrix]] +python = ["3.14", "3.13", "3.12"] + [tool.hatch.envs.default] dependencies = [ "ruff==0.15.1", @@ -45,6 +48,10 @@ dependencies = [ "mashumaro==3.20", "mkdocs-material==9.7.1", "mkdocstrings[python]==1.0.3", + "pytest-asyncio==1.3.0", + "pytest==9.0.2", + "pytest-cov==7.0.0", + "syrupy==5.1.0", ] [tool.hatch.envs.hatch-static-analysis] @@ -63,6 +70,8 @@ pythonpath = ["pythonkuma"] [tool.hatch.envs.hatch-test] extra-dependencies = [ "pytest-cov==7.0.0", + "pytest-asyncio==1.3.0", + "syrupy==5.1.0", ] [tool.hatch.envs.default.scripts] @@ -83,6 +92,9 @@ indent-style = "space" select = ["ALL"] ignore = ["TRY003", "COM812", "N818", "C901"] +[tool.ruff.lint.per-file-ignores] +"tests/*" = ["S101", "TC002", "TC003"] + [lint.per-file-ignores] "**/scripts/*" = [ "INP001", diff --git a/tests/__snapshots__/test_metrics.ambr b/tests/__snapshots__/test_metrics.ambr new file mode 100644 index 0000000..dd1531e --- /dev/null +++ b/tests/__snapshots__/test_metrics.ambr @@ -0,0 +1,77 @@ +# serializer version: 1 +# name: test_metrics + dict({ + 1: dict({ + 'monitor_cert_days_remaining': 80, + 'monitor_cert_is_valid': True, + 'monitor_hostname': None, + 'monitor_id': 1, + 'monitor_name': 'Home Assistant', + 'monitor_port': None, + 'monitor_response_time': 85, + 'monitor_response_time_seconds_1d': 0.10396079958463136, + 'monitor_response_time_seconds_30d': 0.10284582478851578, + 'monitor_response_time_seconds_365d': 0.10957428212662089, + 'monitor_status': 1, + 'monitor_type': 'http', + 'monitor_uptime_ratio_1d': 1.0, + 'monitor_uptime_ratio_30d': 0.999247554552295, + 'monitor_uptime_ratio_365d': 0.9944324016912971, + 'monitor_url': 'https://home.example.com:8123', + }), + 2: dict({ + 'monitor_cert_days_remaining': 80, + 'monitor_cert_is_valid': True, + 'monitor_hostname': None, + 'monitor_id': 2, + 'monitor_name': 'FritzBox', + 'monitor_port': None, + 'monitor_response_time': 2725, + 'monitor_response_time_seconds_1d': 2.339521038961039, + 'monitor_response_time_seconds_30d': 2.3636583723629956, + 'monitor_response_time_seconds_365d': 2.3783335690116663, + 'monitor_status': 1, + 'monitor_type': 'http', + 'monitor_uptime_ratio_1d': 0.9992213859330392, + 'monitor_uptime_ratio_30d': 0.9998319004850872, + 'monitor_uptime_ratio_365d': 0.9947252084798553, + 'monitor_url': 'https://home.example.com', + }), + 3: dict({ + 'monitor_cert_days_remaining': 46, + 'monitor_cert_is_valid': True, + 'monitor_hostname': None, + 'monitor_id': 3, + 'monitor_name': 'Jellyfin', + 'monitor_port': None, + 'monitor_response_time': 85, + 'monitor_response_time_seconds_1d': 0.10102960288808664, + 'monitor_response_time_seconds_30d': 0.09908259629443207, + 'monitor_response_time_seconds_365d': 0.10429958790526786, + 'monitor_status': 1, + 'monitor_type': 'keyword', + 'monitor_uptime_ratio_1d': 1.0, + 'monitor_uptime_ratio_30d': 0.9993293252532994, + 'monitor_uptime_ratio_365d': 0.9941631600380073, + 'monitor_url': 'https://home.example.com:8920/health', + }), + 8: dict({ + 'monitor_cert_days_remaining': 81, + 'monitor_cert_is_valid': True, + 'monitor_hostname': None, + 'monitor_id': 8, + 'monitor_name': 'Nextcloud', + 'monitor_port': None, + 'monitor_response_time': 150, + 'monitor_response_time_seconds_1d': 0.16155477855477854, + 'monitor_response_time_seconds_30d': 0.3391915450984161, + 'monitor_response_time_seconds_365d': 0.34379255863250385, + 'monitor_status': 1, + 'monitor_type': 'json-query', + 'monitor_uptime_ratio_1d': 1.0, + 'monitor_uptime_ratio_30d': 0.9991115593334294, + 'monitor_uptime_ratio_365d': 0.9994703389830508, + 'monitor_url': 'https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json', + }), + }) +# --- diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..d3aa1dd --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,31 @@ +"""Fixtures for pythonkuma.""" + +from collections.abc import Generator +from functools import lru_cache +import pathlib +from unittest.mock import AsyncMock + +from aiohttp import ClientResponse +import pytest + + +@lru_cache +def load_fixture(filename: str) -> str: + """Load a fixture.""" + return ( + pathlib.Path(__file__) + .parent.joinpath("fixtures", filename) + .read_text(encoding="utf-8") + ) + + +@pytest.fixture +def mock_session() -> Generator[AsyncMock]: + """Mock aiohttp ClientSession.""" + mock_session = AsyncMock() + mock_response = AsyncMock(spec=ClientResponse, status=200) + mock_response.text.return_value = load_fixture("metrics.txt") + + mock_session.get.return_value = mock_response + + return mock_session diff --git a/tests/fixtures/metrics.txt b/tests/fixtures/metrics.txt new file mode 100644 index 0000000..366d058 --- /dev/null +++ b/tests/fixtures/metrics.txt @@ -0,0 +1,61 @@ +# HELP monitor_cert_days_remaining The number of days remaining until the certificate expires +# TYPE monitor_cert_days_remaining gauge +monitor_cert_days_remaining{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null"} 80 +monitor_cert_days_remaining{monitor_id="1",monitor_name="Home Assistant",monitor_type="http",monitor_url="https://home.example.com:8123",monitor_hostname="null",monitor_port="null"} 80 +monitor_cert_days_remaining{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",monitor_type="keyword",monitor_url="https://home.example.com:8920/health",monitor_hostname="null",monitor_port="null"} 46 +monitor_cert_days_remaining{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null"} 81 + +# HELP monitor_cert_is_valid Is the certificate still valid? (1 = Yes, 0= No) +# TYPE monitor_cert_is_valid gauge +monitor_cert_is_valid{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null"} 1 +monitor_cert_is_valid{monitor_id="1",monitor_name="Home Assistant",monitor_type="http",monitor_url="https://home.example.com:8123",monitor_hostname="null",monitor_port="null"} 1 +monitor_cert_is_valid{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",monitor_type="keyword",monitor_url="https://home.example.com:8920/health",monitor_hostname="null",monitor_port="null"} 1 +monitor_cert_is_valid{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null"} 1 + +# HELP monitor_uptime_ratio Uptime ratio calculated over sliding window specified by the 'window' label. (0.0 - 1.0) +# TYPE monitor_uptime_ratio gauge +monitor_uptime_ratio{monitor_id="1",monitor_name="Home Assistant",monitor_type="http",monitor_url="https://home.example.com:8123",monitor_hostname="null",monitor_port="null",window="1d"} 1 +monitor_uptime_ratio{monitor_id="1",monitor_name="Home Assistant",monitor_type="http",monitor_url="https://home.example.com:8123",monitor_hostname="null",monitor_port="null",window="30d"} 0.999247554552295 +monitor_uptime_ratio{monitor_id="1",monitor_name="Home Assistant",monitor_type="http",monitor_url="https://home.example.com:8123",monitor_hostname="null",monitor_port="null",window="365d"} 0.9944324016912971 +monitor_uptime_ratio{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null",window="1d"} 0.9992213859330392 +monitor_uptime_ratio{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null",window="30d"} 0.9998319004850872 +monitor_uptime_ratio{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null",window="365d"} 0.9947252084798553 +monitor_uptime_ratio{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",monitor_type="keyword",monitor_url="https://home.example.com:8920/health",monitor_hostname="null",monitor_port="null",window="1d"} 1 +monitor_uptime_ratio{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",monitor_type="keyword",monitor_url="https://home.example.com:8920/health",monitor_hostname="null",monitor_port="null",window="30d"} 0.9993293252532994 +monitor_uptime_ratio{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",monitor_type="keyword",monitor_url="https://home.example.com:8920/health",monitor_hostname="null",monitor_port="null",window="365d"} 0.9941631600380073 +monitor_uptime_ratio{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null",window="1d"} 1 +monitor_uptime_ratio{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null",window="30d"} 0.9991115593334294 +monitor_uptime_ratio{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null",window="365d"} 0.9994703389830508 + +# HELP monitor_response_time_seconds Average response time in seconds calculated over sliding window specified by the 'window' label +# TYPE monitor_response_time_seconds gauge +monitor_response_time_seconds{monitor_id="1",monitor_name="Home Assistant",monitor_type="http",monitor_url="https://home.example.com:8123",monitor_hostname="null",monitor_port="null",window="1d"} 0.10396079958463136 +monitor_response_time_seconds{monitor_id="1",monitor_name="Home Assistant",monitor_type="http",monitor_url="https://home.example.com:8123",monitor_hostname="null",monitor_port="null",window="30d"} 0.10284582478851578 +monitor_response_time_seconds{monitor_id="1",monitor_name="Home Assistant",monitor_type="http",monitor_url="https://home.example.com:8123",monitor_hostname="null",monitor_port="null",window="365d"} 0.10957428212662089 +monitor_response_time_seconds{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null",window="1d"} 2.339521038961039 +monitor_response_time_seconds{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null",window="30d"} 2.3636583723629956 +monitor_response_time_seconds{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null",window="365d"} 2.3783335690116663 +monitor_response_time_seconds{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",monitor_type="keyword",monitor_url="https://home.example.com:8920/health",monitor_hostname="null",monitor_port="null",window="1d"} 0.10102960288808664 +monitor_response_time_seconds{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",monitor_type="keyword",monitor_url="https://home.example.com:8920/health",monitor_hostname="null",monitor_port="null",window="30d"} 0.09908259629443207 +monitor_response_time_seconds{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",monitor_type="keyword",monitor_url="https://home.example.com:8920/health",monitor_hostname="null",monitor_port="null",window="365d"} 0.10429958790526786 +monitor_response_time_seconds{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null",window="1d"} 0.16155477855477854 +monitor_response_time_seconds{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null",window="30d"} 0.3391915450984161 +monitor_response_time_seconds{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null",window="365d"} 0.34379255863250385 + +# HELP monitor_response_time Monitor Response Time (ms) +# TYPE monitor_response_time gauge +monitor_response_time{monitor_id="1",monitor_name="Home Assistant",monitor_type="http",monitor_url="https://home.example.com:8123",monitor_hostname="null",monitor_port="null"} 85 +monitor_response_time{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null"} 2725 +monitor_response_time{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",monitor_type="keyword",monitor_url="https://home.example.com:8920/health",monitor_hostname="null",monitor_port="null"} 85 +monitor_response_time{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null"} 150 + +# HELP monitor_status Monitor Status (1 = UP, 0= DOWN, 2= PENDING, 3= MAINTENANCE) +# TYPE monitor_status gauge +monitor_status{monitor_id="1",monitor_name="Home Assistant",monitor_type="http",monitor_url="https://home.example.com:8123",monitor_hostname="null",monitor_port="null"} 1 +monitor_status{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null"} 1 +monitor_status{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",monitor_type="keyword",monitor_url="https://home.example.com:8920/health",monitor_hostname="null",monitor_port="null"} 1 +monitor_status{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null"} 1 + +# HELP app_version The service version by package.json +# TYPE app_version gauge +app_version{version="2.1.0",major="2",minor="1",patch="0"} 1 diff --git a/tests/test_metrics.py b/tests/test_metrics.py new file mode 100644 index 0000000..9efcd33 --- /dev/null +++ b/tests/test_metrics.py @@ -0,0 +1,16 @@ +"""Tests for pythonkuma.""" + +from unittest.mock import AsyncMock + +from syrupy.assertion import SnapshotAssertion + +from pythonkuma import UptimeKuma + + +async def test_metrics(mock_session: AsyncMock, snapshot: SnapshotAssertion) -> None: + """Test metrics.""" + uptime_kuma = UptimeKuma(mock_session, "http://uptime.example.com", "test-apikey") + + response = await uptime_kuma.metrics() + + assert {k: v.to_dict() for k, v in response.items()} == snapshot From 3bdd0d4bfd8e86baa512277328c992ab2dce0df5 Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Sat, 14 Feb 2026 12:05:35 +0100 Subject: [PATCH 2/2] Add unit tests --- tests/__snapshots__/test_metrics.ambr | 30 ++++++++++++ tests/fixtures/metrics.txt | 8 +++ tests/test_metrics.py | 70 ++++++++++++++++++++++++++- 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/tests/__snapshots__/test_metrics.ambr b/tests/__snapshots__/test_metrics.ambr index dd1531e..7c92e08 100644 --- a/tests/__snapshots__/test_metrics.ambr +++ b/tests/__snapshots__/test_metrics.ambr @@ -13,6 +13,8 @@ 'monitor_response_time_seconds_30d': 0.10284582478851578, 'monitor_response_time_seconds_365d': 0.10957428212662089, 'monitor_status': 1, + 'monitor_tags': list([ + ]), 'monitor_type': 'http', 'monitor_uptime_ratio_1d': 1.0, 'monitor_uptime_ratio_30d': 0.999247554552295, @@ -31,6 +33,8 @@ 'monitor_response_time_seconds_30d': 2.3636583723629956, 'monitor_response_time_seconds_365d': 2.3783335690116663, 'monitor_status': 1, + 'monitor_tags': list([ + ]), 'monitor_type': 'http', 'monitor_uptime_ratio_1d': 0.9992213859330392, 'monitor_uptime_ratio_30d': 0.9998319004850872, @@ -49,6 +53,10 @@ 'monitor_response_time_seconds_30d': 0.09908259629443207, 'monitor_response_time_seconds_365d': 0.10429958790526786, 'monitor_status': 1, + 'monitor_tags': list([ + 'Test', + 'Zuhause', + ]), 'monitor_type': 'keyword', 'monitor_uptime_ratio_1d': 1.0, 'monitor_uptime_ratio_30d': 0.9993293252532994, @@ -67,11 +75,33 @@ 'monitor_response_time_seconds_30d': 0.3391915450984161, 'monitor_response_time_seconds_365d': 0.34379255863250385, 'monitor_status': 1, + 'monitor_tags': list([ + ]), 'monitor_type': 'json-query', 'monitor_uptime_ratio_1d': 1.0, 'monitor_uptime_ratio_30d': 0.9991115593334294, 'monitor_uptime_ratio_365d': 0.9994703389830508, 'monitor_url': 'https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json', }), + 9: dict({ + 'monitor_cert_days_remaining': None, + 'monitor_cert_is_valid': None, + 'monitor_hostname': None, + 'monitor_id': 9, + 'monitor_name': 'Proxy', + 'monitor_port': None, + 'monitor_response_time': 19, + 'monitor_response_time_seconds_1d': 0.032, + 'monitor_response_time_seconds_30d': 0.032, + 'monitor_response_time_seconds_365d': 0.032, + 'monitor_status': 1, + 'monitor_tags': list([ + ]), + 'monitor_type': 'unknown', + 'monitor_uptime_ratio_1d': 0.6666666666666666, + 'monitor_uptime_ratio_30d': 0.6666666666666666, + 'monitor_uptime_ratio_365d': 0.6666666666666666, + 'monitor_url': 'https://', + }), }) # --- diff --git a/tests/fixtures/metrics.txt b/tests/fixtures/metrics.txt index 366d058..983ad1f 100644 --- a/tests/fixtures/metrics.txt +++ b/tests/fixtures/metrics.txt @@ -26,6 +26,9 @@ monitor_uptime_ratio{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",m monitor_uptime_ratio{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null",window="1d"} 1 monitor_uptime_ratio{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null",window="30d"} 0.9991115593334294 monitor_uptime_ratio{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null",window="365d"} 0.9994703389830508 +monitor_uptime_ratio{monitor_id="9",monitor_name="Proxy",monitor_type="laserping",monitor_url="https://",monitor_hostname="null",monitor_port="null",window="1d"} 0.6666666666666666 +monitor_uptime_ratio{monitor_id="9",monitor_name="Proxy",monitor_type="laserping",monitor_url="https://",monitor_hostname="null",monitor_port="null",window="30d"} 0.6666666666666666 +monitor_uptime_ratio{monitor_id="9",monitor_name="Proxy",monitor_type="laserping",monitor_url="https://",monitor_hostname="null",monitor_port="null",window="365d"} 0.6666666666666666 # HELP monitor_response_time_seconds Average response time in seconds calculated over sliding window specified by the 'window' label # TYPE monitor_response_time_seconds gauge @@ -41,6 +44,9 @@ monitor_response_time_seconds{Test="",Zuhause="",monitor_id="3",monitor_name="Je monitor_response_time_seconds{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null",window="1d"} 0.16155477855477854 monitor_response_time_seconds{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null",window="30d"} 0.3391915450984161 monitor_response_time_seconds{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null",window="365d"} 0.34379255863250385 +monitor_response_time_seconds{monitor_id="9",monitor_name="Proxy",monitor_type="laserping",monitor_url="https://",monitor_hostname="null",monitor_port="null",window="1d"} 0.032 +monitor_response_time_seconds{monitor_id="9",monitor_name="Proxy",monitor_type="laserping",monitor_url="https://",monitor_hostname="null",monitor_port="null",window="30d"} 0.032 +monitor_response_time_seconds{monitor_id="9",monitor_name="Proxy",monitor_type="laserping",monitor_url="https://",monitor_hostname="null",monitor_port="null",window="365d"} 0.032 # HELP monitor_response_time Monitor Response Time (ms) # TYPE monitor_response_time gauge @@ -48,6 +54,7 @@ monitor_response_time{monitor_id="1",monitor_name="Home Assistant",monitor_type= monitor_response_time{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null"} 2725 monitor_response_time{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",monitor_type="keyword",monitor_url="https://home.example.com:8920/health",monitor_hostname="null",monitor_port="null"} 85 monitor_response_time{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null"} 150 +monitor_response_time{monitor_id="9",monitor_name="Proxy",monitor_type="laserping",monitor_url="https://",monitor_hostname="null",monitor_port="null"} 19 # HELP monitor_status Monitor Status (1 = UP, 0= DOWN, 2= PENDING, 3= MAINTENANCE) # TYPE monitor_status gauge @@ -55,6 +62,7 @@ monitor_status{monitor_id="1",monitor_name="Home Assistant",monitor_type="http", monitor_status{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null"} 1 monitor_status{Test="",Zuhause="",monitor_id="3",monitor_name="Jellyfin",monitor_type="keyword",monitor_url="https://home.example.com:8920/health",monitor_hostname="null",monitor_port="null"} 1 monitor_status{monitor_id="8",monitor_name="Nextcloud",monitor_type="json-query",monitor_url="https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json",monitor_hostname="null",monitor_port="null"} 1 +monitor_status{monitor_id="9",monitor_name="Proxy",monitor_type="laserping",monitor_url="https://",monitor_hostname="null",monitor_port="null"} 1 # HELP app_version The service version by package.json # TYPE app_version gauge diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 9efcd33..4fdcb00 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -1,10 +1,19 @@ """Tests for pythonkuma.""" -from unittest.mock import AsyncMock +from http import HTTPStatus +from typing import Any +from unittest.mock import AsyncMock, Mock +from aiohttp import ClientError, ClientResponseError, ConnectionTimeoutError +import pytest from syrupy.assertion import SnapshotAssertion from pythonkuma import UptimeKuma +from pythonkuma.exceptions import ( + UptimeKumaAuthenticationException, + UptimeKumaConnectionException, + UptimeKumaParseException, +) async def test_metrics(mock_session: AsyncMock, snapshot: SnapshotAssertion) -> None: @@ -14,3 +23,62 @@ async def test_metrics(mock_session: AsyncMock, snapshot: SnapshotAssertion) -> response = await uptime_kuma.metrics() assert {k: v.to_dict() for k, v in response.items()} == snapshot + + assert uptime_kuma.version.version == "2.1.0" + assert uptime_kuma.version.major == "2" + assert uptime_kuma.version.minor == "1" + assert uptime_kuma.version.patch == "0" + + +@pytest.mark.parametrize( + ("exception", "expected_exception", "error_msg"), + [ + ( + ClientResponseError( + request_info=Mock(), history=(Mock()), status=HTTPStatus.NOT_FOUND + ), + UptimeKumaConnectionException, + ( + "Request for %s failed with status code %s", + "http://uptime.example.com/metrics", + HTTPStatus.NOT_FOUND, + ), + ), + ( + ClientResponseError( + request_info=Mock(), history=(Mock()), status=HTTPStatus.UNAUTHORIZED + ), + UptimeKumaAuthenticationException, + ("Authentication failed for %s", "http://uptime.example.com/metrics"), + ), + (ClientError, UptimeKumaConnectionException, ()), + ( + ConnectionTimeoutError, + UptimeKumaConnectionException, + ("Request timeout for %s", "http://uptime.example.com/metrics"), + ), + ], +) +async def test_exceptions( + mock_session: AsyncMock, + exception: Exception, + expected_exception: Exception, + error_msg: tuple[Any], +) -> None: + """Test request exceptions.""" + mock_session.get.side_effect = exception + uptime_kuma = UptimeKuma(mock_session, "http://uptime.example.com", "test-apikey") + + with pytest.raises(expected_exception) as e: + await uptime_kuma.metrics() + + assert e.value.args == error_msg + + +async def test_metrics_parse_exceptions(mock_session: AsyncMock) -> None: + """Test prometheus metrics parsing fails.""" + mock_session.get.return_value.text.return_value = "invalid metrics" + uptime_kuma = UptimeKuma(mock_session, "http://uptime.example.com", "test-apikey") + + with pytest.raises(UptimeKumaParseException): + await uptime_kuma.metrics()