test: add coverage for monitor/vm/winrm to reach 90%% gate
Lint / pssa (push) Successful in 35s
Lint / python (push) Successful in 42s

Bring monitor.py 49->99%%, vm.py 79->98%%, winrm.py 76->100%%. Total project coverage 80.10%% -> 90.69%%, satisfying the 90%% gate set in commit 451a61c.
This commit is contained in:
2026-05-14 23:14:08 +02:00
parent 451a61c6a1
commit b2b31f4b6e
4 changed files with 974 additions and 0 deletions
+509
View File
@@ -150,3 +150,512 @@ def test_runner_rate_limit_exits_one(
)
assert result.exit_code == 1
assert "limit" in result.output
# ── helpers: _post_webhook ─────────────────────────────────────────────────
def test_post_webhook_empty_url_returns_false(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.undo()
assert mon._post_webhook("", "msg") is False
def test_post_webhook_success(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.undo()
class _Resp:
status = 204
def __enter__(self) -> _Resp:
return self
def __exit__(self, *_a: Any) -> None:
return None
monkeypatch.setattr(mon.urllib.request, "urlopen", lambda *_a, **_kw: _Resp())
assert mon._post_webhook("https://example.invalid/h", "hi") is True
def test_post_webhook_http_error_returns_false(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.undo()
def _raise(*_a: Any, **_kw: Any) -> Any:
raise mon.urllib.error.URLError("boom")
monkeypatch.setattr(mon.urllib.request, "urlopen", _raise)
assert mon._post_webhook("https://example.invalid/h", "hi") is False
def test_post_webhook_non_2xx_returns_false(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.undo()
class _Resp:
status = 500
def __enter__(self) -> _Resp:
return self
def __exit__(self, *_a: Any) -> None:
return None
monkeypatch.setattr(mon.urllib.request, "urlopen", lambda *_a, **_kw: _Resp())
assert mon._post_webhook("https://example.invalid/h", "hi") is False
# ── helpers: _write_windows_event ──────────────────────────────────────────
def test_write_windows_event_non_windows(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "posix")
# Note: autouse fixture monkeypatches the symbol; call the real impl.
monkeypatch.undo()
monkeypatch.setattr(mon.os, "name", "posix")
assert mon._write_windows_event("src", 1, "Warning", "msg") is False
def test_write_windows_event_eventcreate_missing(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.undo()
monkeypatch.setattr(mon.os, "name", "nt")
monkeypatch.setattr(mon.shutil, "which", lambda _n: None)
assert mon._write_windows_event("src", 1, "Warning", "msg") is False
def test_write_windows_event_subprocess_ok(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.undo()
monkeypatch.setattr(mon.os, "name", "nt")
monkeypatch.setattr(mon.shutil, "which", lambda _n: r"C:\Windows\System32\eventcreate.exe")
class _R:
returncode = 0
stderr = ""
monkeypatch.setattr(mon.subprocess, "run", lambda *_a, **_kw: _R())
assert mon._write_windows_event("src", 1, "Information", "msg") is True
def test_write_windows_event_subprocess_nonzero(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.undo()
monkeypatch.setattr(mon.os, "name", "nt")
monkeypatch.setattr(mon.shutil, "which", lambda _n: r"C:\Windows\System32\eventcreate.exe")
class _R:
returncode = 5
stderr = "nope"
monkeypatch.setattr(mon.subprocess, "run", lambda *_a, **_kw: _R())
assert mon._write_windows_event("src", 1, "Error", "msg") is False
def test_write_windows_event_subprocess_oserror(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.undo()
monkeypatch.setattr(mon.os, "name", "nt")
monkeypatch.setattr(mon.shutil, "which", lambda _n: r"C:\Windows\System32\eventcreate.exe")
def _boom(*_a: Any, **_kw: Any) -> Any:
raise OSError("boom")
monkeypatch.setattr(mon.subprocess, "run", _boom)
assert mon._write_windows_event("src", 1, "Warning", "msg") is False
# ── helpers: _resolve_drive_path ───────────────────────────────────────────
def test_resolve_drive_path_windows(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "nt")
assert mon._resolve_drive_path("F") == "F:\\"
def test_resolve_drive_path_non_windows(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "posix")
assert mon._resolve_drive_path("/mnt/data") == "/mnt/data"
# ── helpers: _service_status ───────────────────────────────────────────────
class _RunResult:
def __init__(self, returncode: int = 0, stdout: str = "", stderr: str = "") -> None:
self.returncode = returncode
self.stdout = stdout
self.stderr = stderr
def test_service_status_windows_running(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "nt")
monkeypatch.setattr(mon.shutil, "which", lambda _n: r"C:\Windows\System32\sc.exe")
monkeypatch.setattr(mon.subprocess, "run", lambda *_a, **_kw: _RunResult(0, "STATE : 4 RUNNING"))
assert mon._service_status("svc") == "Running"
def test_service_status_windows_stopped(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "nt")
monkeypatch.setattr(mon.shutil, "which", lambda _n: r"C:\Windows\System32\sc.exe")
monkeypatch.setattr(mon.subprocess, "run", lambda *_a, **_kw: _RunResult(0, "STATE : 1 STOPPED"))
assert mon._service_status("svc") == "Stopped"
def test_service_status_windows_unknown_state(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "nt")
monkeypatch.setattr(mon.shutil, "which", lambda _n: r"C:\Windows\System32\sc.exe")
monkeypatch.setattr(mon.subprocess, "run", lambda *_a, **_kw: _RunResult(0, "STATE : 7 PAUSED"))
assert mon._service_status("svc") == "Unknown"
def test_service_status_windows_sc_failed(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "nt")
monkeypatch.setattr(mon.shutil, "which", lambda _n: r"C:\Windows\System32\sc.exe")
monkeypatch.setattr(mon.subprocess, "run", lambda *_a, **_kw: _RunResult(1, ""))
assert mon._service_status("svc") is None
def test_service_status_windows_subprocess_oserror(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "nt")
monkeypatch.setattr(mon.shutil, "which", lambda _n: r"C:\Windows\System32\sc.exe")
def _boom(*_a: Any, **_kw: Any) -> Any:
raise OSError("boom")
monkeypatch.setattr(mon.subprocess, "run", _boom)
assert mon._service_status("svc") is None
def test_service_status_linux_active(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "posix")
monkeypatch.setattr(mon.shutil, "which", lambda _n: "/usr/bin/systemctl")
monkeypatch.setattr(mon.subprocess, "run", lambda *_a, **_kw: _RunResult(0, "active\n"))
assert mon._service_status("svc") == "Running"
def test_service_status_linux_inactive(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "posix")
monkeypatch.setattr(mon.shutil, "which", lambda _n: "/usr/bin/systemctl")
monkeypatch.setattr(mon.subprocess, "run", lambda *_a, **_kw: _RunResult(3, "inactive\n"))
assert mon._service_status("svc") == "Stopped"
def test_service_status_linux_unknown(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "posix")
monkeypatch.setattr(mon.shutil, "which", lambda _n: "/usr/bin/systemctl")
monkeypatch.setattr(mon.subprocess, "run", lambda *_a, **_kw: _RunResult(4, "activating\n"))
assert mon._service_status("svc") is None
def test_service_status_linux_no_systemctl(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "posix")
monkeypatch.setattr(mon.shutil, "which", lambda _n: None)
assert mon._service_status("svc") is None
def test_service_status_linux_subprocess_oserror(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "posix")
monkeypatch.setattr(mon.shutil, "which", lambda _n: "/usr/bin/systemctl")
def _boom(*_a: Any, **_kw: Any) -> Any:
raise OSError("boom")
monkeypatch.setattr(mon.subprocess, "run", _boom)
assert mon._service_status("svc") is None
# ── helpers: _restart_service ──────────────────────────────────────────────
def test_restart_service_windows_success(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "nt")
monkeypatch.setattr(mon.shutil, "which", lambda _n: r"C:\Windows\System32\sc.exe")
monkeypatch.setattr(mon.subprocess, "run", lambda *_a, **_kw: _RunResult(0, ""))
monkeypatch.setattr(mon.time, "sleep", lambda _s: None)
monkeypatch.setattr(mon, "_service_status", lambda _s: "Running")
assert mon._restart_service("svc") is True
def test_restart_service_windows_oserror(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "nt")
monkeypatch.setattr(mon.shutil, "which", lambda _n: r"C:\Windows\System32\sc.exe")
def _boom(*_a: Any, **_kw: Any) -> Any:
raise OSError("boom")
monkeypatch.setattr(mon.subprocess, "run", _boom)
assert mon._restart_service("svc") is False
def test_restart_service_linux_success(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "posix")
monkeypatch.setattr(mon.shutil, "which", lambda _n: "/usr/bin/systemctl")
monkeypatch.setattr(mon.subprocess, "run", lambda *_a, **_kw: _RunResult(0, ""))
monkeypatch.setattr(mon.time, "sleep", lambda _s: None)
monkeypatch.setattr(mon, "_service_status", lambda _s: "Running")
assert mon._restart_service("svc") is True
def test_restart_service_linux_no_systemctl(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "posix")
monkeypatch.setattr(mon.shutil, "which", lambda _n: None)
assert mon._restart_service("svc") is False
def test_restart_service_linux_oserror(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.os, "name", "posix")
monkeypatch.setattr(mon.shutil, "which", lambda _n: "/usr/bin/systemctl")
def _boom(*_a: Any, **_kw: Any) -> Any:
raise subprocess.TimeoutExpired(cmd="systemctl", timeout=30.0)
import subprocess
monkeypatch.setattr(mon.subprocess, "run", _boom)
assert mon._restart_service("svc") is False
# ── helpers: _load_restart_log / _save_restart_log ─────────────────────────
def test_load_restart_log_missing(tmp_path: Path) -> None:
assert mon._load_restart_log(tmp_path / "no.json") == []
def test_load_restart_log_empty(tmp_path: Path) -> None:
p = tmp_path / "empty.json"
p.write_text(" ")
assert mon._load_restart_log(p) == []
def test_load_restart_log_invalid_json(tmp_path: Path) -> None:
p = tmp_path / "bad.json"
p.write_text("{not json")
assert mon._load_restart_log(p) == []
def test_load_restart_log_non_list(tmp_path: Path) -> None:
p = tmp_path / "obj.json"
p.write_text(json.dumps({"a": 1}))
assert mon._load_restart_log(p) == []
def test_load_restart_log_valid(tmp_path: Path) -> None:
p = tmp_path / "ok.json"
p.write_text(json.dumps(["2026-05-14T10:00:00+00:00"]))
assert mon._load_restart_log(p) == ["2026-05-14T10:00:00+00:00"]
def test_load_restart_log_oserror(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
p = tmp_path / "bad.json"
p.write_text("[]")
def _boom(*_a: Any, **_kw: Any) -> Any:
raise OSError("boom")
monkeypatch.setattr(Path, "read_text", _boom)
assert mon._load_restart_log(p) == []
def test_save_restart_log_writes_file(tmp_path: Path) -> None:
p = tmp_path / "sub" / "log.json"
mon._save_restart_log(p, ["a", "b"])
assert json.loads(p.read_text()) == ["a", "b"]
def test_save_restart_log_oserror_swallowed(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
def _boom(*_a: Any, **_kw: Any) -> Any:
raise OSError("readonly")
monkeypatch.setattr(Path, "mkdir", _boom)
# Should not raise.
mon._save_restart_log(tmp_path / "x" / "log.json", ["a"])
# ── helpers: _gitea_runners_online ─────────────────────────────────────────
def _make_urlopen(payload: Any) -> Any:
class _Resp:
def __enter__(self) -> _Resp:
return self
def __exit__(self, *_a: Any) -> None:
return None
def read(self) -> bytes:
return json.dumps(payload).encode("utf-8")
return lambda *_a, **_kw: _Resp()
def test_gitea_runners_online_counts(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
mon.urllib.request,
"urlopen",
_make_urlopen([{"online": True}, {"online": False}, {"online": True}]),
)
assert mon._gitea_runners_online("https://g/", "tok") == 2
def test_gitea_runners_online_non_list(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(mon.urllib.request, "urlopen", _make_urlopen({"x": 1}))
assert mon._gitea_runners_online("https://g/", "tok") is None
def test_gitea_runners_online_url_error(monkeypatch: pytest.MonkeyPatch) -> None:
def _raise(*_a: Any, **_kw: Any) -> Any:
raise mon.urllib.error.URLError("nope")
monkeypatch.setattr(mon.urllib.request, "urlopen", _raise)
assert mon._gitea_runners_online("https://g/", "tok") is None
# ── monitor_runner: gitea-online branches & restart-fail path ──────────────
def _stub_keyring(monkeypatch: pytest.MonkeyPatch, password: str = "tok") -> None:
class _Cred:
username = "u"
def __init__(self, pw: str) -> None:
self.password = pw
class _Store:
def get(self, _target: str) -> Any:
return _Cred(password)
import ci_orchestrator.credentials as creds
monkeypatch.setattr(creds, "KeyringCredentialStore", _Store)
def test_runner_running_with_gitea_online(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
monkeypatch.setattr(mon, "_service_status", lambda _s: "Running")
monkeypatch.setattr(mon, "_gitea_runners_online", lambda *_a, **_kw: 2)
_stub_keyring(monkeypatch)
result = CliRunner().invoke(
cli,
[
"monitor", "runner",
"--state-dir", str(tmp_path),
"--gitea-url", "https://g/",
"--gitea-credential-target", "tgt",
],
)
assert result.exit_code == 0
assert "2 runner(s) online" in result.output
def test_runner_running_with_gitea_zero_online(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
monkeypatch.setattr(mon, "_service_status", lambda _s: "Running")
monkeypatch.setattr(mon, "_gitea_runners_online", lambda *_a, **_kw: 0)
_stub_keyring(monkeypatch)
result = CliRunner().invoke(
cli,
[
"monitor", "runner",
"--state-dir", str(tmp_path),
"--gitea-url", "https://g/",
"--gitea-credential-target", "tgt",
"--webhook-url", "https://hook/",
],
)
assert result.exit_code == 0
assert "0 online runners" in result.output
def test_runner_running_with_gitea_api_failure(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
monkeypatch.setattr(mon, "_service_status", lambda _s: "Running")
monkeypatch.setattr(mon, "_gitea_runners_online", lambda *_a, **_kw: None)
_stub_keyring(monkeypatch)
result = CliRunner().invoke(
cli,
[
"monitor", "runner",
"--state-dir", str(tmp_path),
"--gitea-url", "https://g/",
"--gitea-credential-target", "tgt",
],
)
assert result.exit_code == 0
assert "skipped/failed" in result.output
def test_runner_running_with_gitea_keyring_keyerror(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
monkeypatch.setattr(mon, "_service_status", lambda _s: "Running")
class _Store:
def get(self, _target: str) -> Any:
raise KeyError("missing")
import ci_orchestrator.credentials as creds
monkeypatch.setattr(creds, "KeyringCredentialStore", _Store)
result = CliRunner().invoke(
cli,
[
"monitor", "runner",
"--state-dir", str(tmp_path),
"--gitea-url", "https://g/",
"--gitea-credential-target", "tgt",
],
)
assert result.exit_code == 0
assert "could not load Gitea PAT" in result.output
def test_runner_restart_fails_exits_one(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
statuses = iter(["Stopped", "Stopped"])
monkeypatch.setattr(mon, "_service_status", lambda _s: next(statuses))
monkeypatch.setattr(mon, "_restart_service", lambda _s: False)
result = CliRunner().invoke(
cli,
[
"monitor", "runner",
"--state-dir", str(tmp_path),
"--webhook-url", "https://hook/",
],
)
assert result.exit_code == 1
assert "restarted" in result.output
def test_runner_rate_limit_with_webhook(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
recent = [datetime.now(tz=UTC).isoformat()] * 3
(tmp_path / "runner-restart-log.json").write_text(json.dumps(recent))
monkeypatch.setattr(mon, "_service_status", lambda _s: "Stopped")
result = CliRunner().invoke(
cli,
[
"monitor", "runner",
"--state-dir", str(tmp_path),
"--max-restarts", "3",
"--webhook-url", "https://hook/",
],
)
assert result.exit_code == 1
assert "limit" in result.output
def test_runner_pruned_old_entries(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""Old timestamps and invalid entries must be pruned."""
from datetime import timedelta
old = (datetime.now(tz=UTC) - timedelta(hours=2)).isoformat()
naive = datetime.now(tz=UTC).replace(tzinfo=None).isoformat()
bad = "not-a-timestamp"
(tmp_path / "runner-restart-log.json").write_text(json.dumps([old, naive, bad]))
statuses = iter(["Stopped", "Running"])
monkeypatch.setattr(mon, "_service_status", lambda _s: next(statuses))
monkeypatch.setattr(mon, "_restart_service", lambda _s: True)
result = CliRunner().invoke(
cli, ["monitor", "runner", "--state-dir", str(tmp_path)]
)
assert result.exit_code == 0