From cb4ae756ab3b38c1a18c7e4a5c3dc6b94ca262a5 Mon Sep 17 00:00:00 2001 From: Oskar Kapala Date: Mon, 1 Jun 2026 20:38:24 +0200 Subject: [PATCH] test(brain-watchdog): add pytest suite covering import and check() logic 7 cases: package importable, fresh ok, stale, unreachable, HTTP error, missing last_update field, unparseable timestamp. pytest.ini sets pythonpath=src so tests run without PYTHONPATH set in the environment. Co-Authored-By: Claude Sonnet 4.6 --- services/brain-watchdog/pytest.ini | 3 + services/brain-watchdog/tests/__init__.py | 0 services/brain-watchdog/tests/test_main.py | 66 ++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 services/brain-watchdog/pytest.ini create mode 100644 services/brain-watchdog/tests/__init__.py create mode 100644 services/brain-watchdog/tests/test_main.py diff --git a/services/brain-watchdog/pytest.ini b/services/brain-watchdog/pytest.ini new file mode 100644 index 0000000..442bb94 --- /dev/null +++ b/services/brain-watchdog/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +pythonpath = src +testpaths = tests diff --git a/services/brain-watchdog/tests/__init__.py b/services/brain-watchdog/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services/brain-watchdog/tests/test_main.py b/services/brain-watchdog/tests/test_main.py new file mode 100644 index 0000000..5ff588b --- /dev/null +++ b/services/brain-watchdog/tests/test_main.py @@ -0,0 +1,66 @@ +""" +Tests for brain_watchdog.main. + +Module-level env vars are required at import time; set them before the first +import of the module so tests can run without a real control-plane. +""" +import importlib.util +import os +import time +from unittest.mock import patch + +os.environ.setdefault("CONTROL_PLANE_URL", "http://test-cp:8080") +os.environ.setdefault("TG_TOKEN", "test_token") +os.environ.setdefault("TG_CHAT_ID", "12345") + +import brain_watchdog.main as bwm + + +def test_package_importable(): + spec = importlib.util.find_spec("brain_watchdog") + assert spec is not None + + +def test_check_ok_fresh(): + now = time.time() + with patch.object(bwm, "http_get", return_value=(200, {"last_update": now - 10})): + ok, reason = bwm.check() + assert ok + assert "ok" in reason + + +def test_check_fail_stale(): + now = time.time() + stale_ts = now - (bwm.STALE_THRESHOLD + 120) + with patch.object(bwm, "http_get", return_value=(200, {"last_update": stale_ts})): + ok, reason = bwm.check() + assert not ok + assert "stale" in reason + + +def test_check_fail_unreachable(): + with patch.object(bwm, "http_get", return_value=(None, None)): + ok, reason = bwm.check() + assert not ok + assert "unreachable" in reason + + +def test_check_fail_http_error(): + with patch.object(bwm, "http_get", return_value=(503, None)): + ok, reason = bwm.check() + assert not ok + assert "503" in reason + + +def test_check_fail_missing_last_update(): + with patch.object(bwm, "http_get", return_value=(200, {"other": "data"})): + ok, reason = bwm.check() + assert not ok + assert "last_update" in reason + + +def test_check_fail_unparseable_timestamp(): + with patch.object(bwm, "http_get", return_value=(200, {"last_update": "not-a-number"})): + ok, reason = bwm.check() + assert not ok + assert "parseable" in reason