Skip to content

Commit 840bdf8

Browse files
committed
Add tests
1 parent c10a2d5 commit 840bdf8

6 files changed

Lines changed: 198 additions & 1 deletion

File tree

pyproject.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ include = [
3737
"/pythonkuma",
3838
]
3939

40+
[[tool.hatch.envs.hatch-test.matrix]]
41+
python = ["3.14", "3.13", "3.12"]
42+
4043
[tool.hatch.envs.default]
4144
dependencies = [
4245
"ruff==0.15.0",
@@ -45,6 +48,10 @@ dependencies = [
4548
"mashumaro==3.20",
4649
"mkdocs-material==9.7.1",
4750
"mkdocstrings[python]==1.0.3",
51+
"pytest-asyncio==1.3.0",
52+
"pytest==9.0.2",
53+
"pytest-cov==7.0.0",
54+
"syrupy==5.1.0",
4855
]
4956

5057
[tool.hatch.envs.hatch-static-analysis]
@@ -63,6 +70,8 @@ pythonpath = ["pythonkuma"]
6370
[tool.hatch.envs.hatch-test]
6471
extra-dependencies = [
6572
"pytest-cov==7.0.0",
73+
"pytest-asyncio==1.3.0",
74+
"syrupy==5.1.0",
6675
]
6776

6877
[tool.hatch.envs.default.scripts]
@@ -83,6 +92,9 @@ indent-style = "space"
8392
select = ["ALL"]
8493
ignore = ["TRY003", "COM812", "N818", "C901"]
8594

95+
[tool.ruff.lint.per-file-ignores]
96+
"tests/*" = ["S101", "TC002", "TC003"]
97+
8698
[lint.per-file-ignores]
8799
"**/scripts/*" = [
88100
"INP001",

pythonkuma/uptimekuma.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ async def metrics(self) -> dict[str | int, UptimeKumaMonitor]:
9595
raise UptimeKumaConnectionException from e
9696

9797
try:
98-
metrics = set(text_string_to_metric_families(await request.text()))
98+
metrics = list(text_string_to_metric_families(await request.text()))
9999
except ValueError as e:
100100
raise UptimeKumaParseException from e
101101

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# serializer version: 1
2+
# name: test_metrics
3+
dict({
4+
1: dict({
5+
'monitor_cert_days_remaining': 80,
6+
'monitor_cert_is_valid': True,
7+
'monitor_hostname': None,
8+
'monitor_id': 1,
9+
'monitor_name': 'Home Assistant',
10+
'monitor_port': None,
11+
'monitor_response_time': 85,
12+
'monitor_response_time_seconds_1d': 0.10396079958463136,
13+
'monitor_response_time_seconds_30d': 0.10284582478851578,
14+
'monitor_response_time_seconds_365d': 0.10957428212662089,
15+
'monitor_status': 1,
16+
'monitor_type': 'http',
17+
'monitor_uptime_ratio_1d': 1.0,
18+
'monitor_uptime_ratio_30d': 0.999247554552295,
19+
'monitor_uptime_ratio_365d': 0.9944324016912971,
20+
'monitor_url': 'https://home.example.com:8123',
21+
}),
22+
2: dict({
23+
'monitor_cert_days_remaining': 80,
24+
'monitor_cert_is_valid': True,
25+
'monitor_hostname': None,
26+
'monitor_id': 2,
27+
'monitor_name': 'FritzBox',
28+
'monitor_port': None,
29+
'monitor_response_time': 2725,
30+
'monitor_response_time_seconds_1d': 2.339521038961039,
31+
'monitor_response_time_seconds_30d': 2.3636583723629956,
32+
'monitor_response_time_seconds_365d': 2.3783335690116663,
33+
'monitor_status': 1,
34+
'monitor_type': 'http',
35+
'monitor_uptime_ratio_1d': 0.9992213859330392,
36+
'monitor_uptime_ratio_30d': 0.9998319004850872,
37+
'monitor_uptime_ratio_365d': 0.9947252084798553,
38+
'monitor_url': 'https://home.example.com',
39+
}),
40+
3: dict({
41+
'monitor_cert_days_remaining': 46,
42+
'monitor_cert_is_valid': True,
43+
'monitor_hostname': None,
44+
'monitor_id': 3,
45+
'monitor_name': 'Jellyfin',
46+
'monitor_port': None,
47+
'monitor_response_time': 85,
48+
'monitor_response_time_seconds_1d': 0.10102960288808664,
49+
'monitor_response_time_seconds_30d': 0.09908259629443207,
50+
'monitor_response_time_seconds_365d': 0.10429958790526786,
51+
'monitor_status': 1,
52+
'monitor_type': 'keyword',
53+
'monitor_uptime_ratio_1d': 1.0,
54+
'monitor_uptime_ratio_30d': 0.9993293252532994,
55+
'monitor_uptime_ratio_365d': 0.9941631600380073,
56+
'monitor_url': 'https://home.example.com:8920/health',
57+
}),
58+
8: dict({
59+
'monitor_cert_days_remaining': 81,
60+
'monitor_cert_is_valid': True,
61+
'monitor_hostname': None,
62+
'monitor_id': 8,
63+
'monitor_name': 'Nextcloud',
64+
'monitor_port': None,
65+
'monitor_response_time': 150,
66+
'monitor_response_time_seconds_1d': 0.16155477855477854,
67+
'monitor_response_time_seconds_30d': 0.3391915450984161,
68+
'monitor_response_time_seconds_365d': 0.34379255863250385,
69+
'monitor_status': 1,
70+
'monitor_type': 'json-query',
71+
'monitor_uptime_ratio_1d': 1.0,
72+
'monitor_uptime_ratio_30d': 0.9991115593334294,
73+
'monitor_uptime_ratio_365d': 0.9994703389830508,
74+
'monitor_url': 'https://cloud.example.com/ocs/v2.php/apps/serverinfo/api/v1/info?format=json',
75+
}),
76+
})
77+
# ---

tests/conftest.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""Fixtures for pythonkuma."""
2+
3+
from collections.abc import Generator
4+
from functools import lru_cache
5+
import pathlib
6+
from unittest.mock import AsyncMock
7+
8+
from aiohttp import ClientResponse
9+
import pytest
10+
11+
12+
@lru_cache
13+
def load_fixture(filename: str) -> str:
14+
"""Load a fixture."""
15+
return (
16+
pathlib.Path(__file__)
17+
.parent.joinpath("fixtures", filename)
18+
.read_text(encoding="utf-8")
19+
)
20+
21+
22+
@pytest.fixture
23+
def mock_session() -> Generator[AsyncMock]:
24+
"""Mock aiohttp ClientSession."""
25+
mock_session = AsyncMock()
26+
mock_response = AsyncMock(spec=ClientResponse, status=200)
27+
mock_response.text.return_value = load_fixture("metrics.txt")
28+
29+
mock_session.get.return_value = mock_response
30+
31+
return mock_session

tests/fixtures/metrics.txt

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# HELP monitor_cert_days_remaining The number of days remaining until the certificate expires
2+
# TYPE monitor_cert_days_remaining gauge
3+
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
4+
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
5+
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
6+
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
7+
8+
# HELP monitor_cert_is_valid Is the certificate still valid? (1 = Yes, 0= No)
9+
# TYPE monitor_cert_is_valid gauge
10+
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
11+
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
12+
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
13+
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
14+
15+
# HELP monitor_uptime_ratio Uptime ratio calculated over sliding window specified by the 'window' label. (0.0 - 1.0)
16+
# TYPE monitor_uptime_ratio gauge
17+
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
18+
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
19+
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
20+
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
21+
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
22+
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
23+
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
24+
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
25+
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
26+
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
27+
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
28+
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
29+
30+
# HELP monitor_response_time_seconds Average response time in seconds calculated over sliding window specified by the 'window' label
31+
# TYPE monitor_response_time_seconds gauge
32+
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
33+
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
34+
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
35+
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
36+
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
37+
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
38+
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
39+
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
40+
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
41+
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
42+
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
43+
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
44+
45+
# HELP monitor_response_time Monitor Response Time (ms)
46+
# TYPE monitor_response_time gauge
47+
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
48+
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
49+
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
50+
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
51+
52+
# HELP monitor_status Monitor Status (1 = UP, 0= DOWN, 2= PENDING, 3= MAINTENANCE)
53+
# TYPE monitor_status gauge
54+
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
55+
monitor_status{monitor_id="2",monitor_name="FritzBox",monitor_type="http",monitor_url="https://home.example.com",monitor_hostname="null",monitor_port="null"} 1
56+
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
57+
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
58+
59+
# HELP app_version The service version by package.json
60+
# TYPE app_version gauge
61+
app_version{version="2.1.0",major="2",minor="1",patch="0"} 1

tests/test_metrics.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""Tests for pythonkuma."""
2+
3+
from unittest.mock import AsyncMock
4+
5+
from syrupy.assertion import SnapshotAssertion
6+
7+
from pythonkuma import UptimeKuma
8+
9+
10+
async def test_metrics(mock_session: AsyncMock, snapshot: SnapshotAssertion) -> None:
11+
"""Test metrics."""
12+
uptime_kuma = UptimeKuma(mock_session, "http://uptime.example.com", "test-apikey")
13+
14+
response = await uptime_kuma.metrics()
15+
16+
assert {k: v.to_dict() for k, v in response.items()} == snapshot

0 commit comments

Comments
 (0)