update_cmd_fleet.py had grown past the 2,000-line cap (2,230). The verify phase (_verify_fleet_after_update, the settle-polled fleet snapshot, owed-inventory recording, legacy-unit warning) is its own topic: it runs after the restart phase and decides follow-up vs clear. It now lives in a sibling; restart-phase helpers it uses are read through update_cmd_fleet so their test patches still apply. update_cmd re-imports _verify_fleet_after_update from the new module. Tests retarget their patches to where production reads. No behaviour change.
63 lines
2.9 KiB
Python
63 lines
2.9 KiB
Python
"""SQLite completion and fleet verification remain independent update outcomes."""
|
|
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from hermes_cli import update_cmd, update_cmd_fleet, update_cmd_fleet_verify as fleet_verify, update_cmd_maint, update_receipt
|
|
from hermes_constants import get_hermes_home
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"update_complete,state",
|
|
[(True, "current"), (False, "current"), (True, "stale"), (True, "down"), (True, None)],
|
|
)
|
|
def test_fleet_completion_preserves_runtime_verdict_and_restart_obligation(
|
|
update_complete, state, monkeypatch,
|
|
):
|
|
refreshed, migrated = [], []
|
|
snapshot = [{"profile": "default", "pid": 1234, "state": state}] if state else []
|
|
restart = update_cmd_fleet._GatewayRestartOutcome(
|
|
incomplete=False, phase_errors=[], pre_restart_gateway_pids=[1234],
|
|
restarted_services=["hermes-gateway"], failed_or_stale_units=[],
|
|
relaunched_profiles=[], externally_supervised_profiles=[], killed_pids=set(),
|
|
)
|
|
monkeypatch.setattr(fleet_verify, "_print_legacy_units_warning", lambda: None)
|
|
monkeypatch.setattr(update_cmd, "_surviving_pre_update_serve_runtimes", lambda plan: [])
|
|
monkeypatch.setattr(
|
|
update_cmd_maint, "_refresh_dashboard_after_update",
|
|
lambda **kwargs: refreshed.append(kwargs),
|
|
)
|
|
monkeypatch.setattr(update_receipt, "_code_identity", lambda **kwargs: {})
|
|
monkeypatch.setattr(
|
|
"hermes_cli.gateway_migrate.maybe_auto_migrate_after_update", lambda: migrated.append(True),
|
|
)
|
|
|
|
def collect(outcome, rows_expected):
|
|
assert outcome is restart
|
|
assert rows_expected is True
|
|
return snapshot
|
|
|
|
monkeypatch.setattr(fleet_verify, "_collect_fleet_snapshot", collect)
|
|
update_cmd_fleet._write_fleet_restart_pending_marker()
|
|
assert update_cmd_fleet._fleet_restart_obligation_armed()
|
|
healthy = update_complete and state == "current"
|
|
with update_receipt.update_receipt_scope():
|
|
update_receipt.begin_update_receipt()
|
|
# Contract C3: neither verdict fails the committed update any more (was SystemExit(1)).
|
|
fleet_verify._verify_fleet_after_update(
|
|
restart, _pre_update_plan=None, _windows_gateway_resume=None,
|
|
update_complete=update_complete,
|
|
)
|
|
|
|
receipt = json.loads((get_hermes_home() / "logs/update_receipts/latest.json").read_text())
|
|
# Was "partial" when not healthy: the run is a success; an owed fleet restart is a follow-up.
|
|
assert receipt["outcome"] == "success"
|
|
assert [f["step"] for f in receipt.get("followups", [])] == ([] if state == "current" else ["gateway_restart"])
|
|
assert receipt["fleet"] == snapshot
|
|
# The two verdicts stay independent: only the fleet keeps the restart obligation armed.
|
|
assert restart.incomplete is (state != "current")
|
|
assert update_cmd_fleet._fleet_restart_obligation_armed() is (state != "current")
|
|
assert migrated == ([True] if healthy else [])
|
|
assert refreshed == [{"already_restarted_units": {"hermes-gateway"}}]
|