Files
hermes-agent/tests/hermes_cli/test_update_pause_service_rollback.py
Teknium 4c77e8954b fix: a failed service pause keeps its record on typed rollback failures
_pause_windows_gateways_for_update decided whether to abandon the durable pause
record by searching the error text for "rollback failures"; rewording that
message would delete the record while gateways are still stopped. The service
pause now raises ServicePauseFailed carrying rollback_failures, and the record
is abandoned only when that list is empty. Any other exception keeps the record.
2026-10-06 06:18:39 -07:00

52 lines
2.5 KiB
Python

"""A failed service pause keeps its record exactly when its rollback left gateways down.
``_pause_windows_gateways_for_update`` decides whether to abandon the durable pause record from the
service-pause failure. That decision is read from a typed ``rollback_failures`` list, never from the
error message's wording: a reworded message must not delete the record while gateways stay stopped.
Real pause producer and durable record; seams are the Windows-only discovery, the SCM stop/restore
calls and the ordinary-gateway stop (nothing is signalled).
"""
from __future__ import annotations
import os
from types import SimpleNamespace
import pytest
from hermes_cli import gateway, update_cmd, update_cmd_windows
from hermes_cli import update_pause_record as pause_record
from hermes_cli.update_cmd_windows import ServicePauseFailed, _pause_windows_gateways_for_update
@pytest.mark.parametrize("restore_fails", [False, True])
def test_the_pause_record_survives_exactly_when_the_service_rollback_failed(monkeypatch, restore_fails):
from hermes_cli import main
pid = os.getpid() # a live identity for the service's gateway; never signalled
service = SimpleNamespace(name="hermes-gw", profile="default", gateway_pid=pid, gateway_create_time=1.0,
service_pid=pid, service_create_time=1.0, descendant_identities=())
monkeypatch.setattr(main, "_is_windows", lambda: True)
monkeypatch.setattr(gateway, "find_gateway_pids", lambda all_profiles=False: [])
monkeypatch.setattr(gateway, "find_profile_gateway_processes", lambda strict=False: [])
monkeypatch.setattr(gateway, "find_windows_gateway_services", lambda profile_processes=(): [service])
monkeypatch.setattr(update_cmd_windows, "_stop_windows_gateways", lambda *a, **k: {})
monkeypatch.setattr(update_cmd_windows, "_record_attested_cold_start_profiles", lambda *a: None)
def stop(*_a, **_k):
raise OSError("access denied")
def restore(_name):
if restore_fails:
raise OSError("service will not start")
monkeypatch.setattr(update_cmd, "_stop_windows_gateway_service", stop)
monkeypatch.setattr(update_cmd, "_restore_windows_gateway_service", restore)
with pytest.raises(ServicePauseFailed) as failed:
_pause_windows_gateways_for_update()
assert bool(failed.value.rollback_failures) is restore_fails
saved = pause_record.read(pause_record.record_path())
assert (saved is not None) is restore_fails, \
"a rolled-back pause kept its record" if saved else "the record of still-stopped gateways was deleted"