3e779be6f3
CI / lint (push) Failing after 13m4s
CI / test (3.11, ubuntu-latest) (push) Failing after 2m4s
CI / test (3.13, ubuntu-latest) (push) Successful in 13m30s
CI / test (3.14, ubuntu-latest) (push) Successful in 17m21s
CI / test (3.12, ubuntu-latest) (push) Successful in 17m55s
CI / discover-apps-ps (push) Successful in 1m56s
CI / test (3.9, ubuntu-latest) (push) Successful in 13m17s
CI / test (3.10, ubuntu-latest) (push) Successful in 26m21s
CI / audit (push) Successful in 13m38s
Deploy site / deploy (push) Has been cancelled
CI / test (3.14, ubuntu-24.04-arm) (push) Has been cancelled
110 lines
3.5 KiB
Python
110 lines
3.5 KiB
Python
# SPDX-License-Identifier: MIT
|
|
"""Tests for `core.pod.recovery.try_recover_rdp` — agent-driven RDP recovery.
|
|
|
|
Covers the three terminal RecoveryAction outcomes plus the "RDP comes back
|
|
mid-window" success path. The agent transport + RDP probe + time.sleep are
|
|
mocked so the test runs in milliseconds even though the production flow
|
|
polls for up to 30 s.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from winpodx.core.config import Config
|
|
from winpodx.core.pod.recovery import (
|
|
RecoveryAction,
|
|
try_recover_rdp,
|
|
)
|
|
|
|
|
|
def _agent_health(available: bool, detail: str = "") -> MagicMock:
|
|
h = MagicMock()
|
|
h.available = available
|
|
h.detail = detail
|
|
return h
|
|
|
|
|
|
def _exec_result(rc: int = 0, stdout: str = "", stderr: str = "") -> MagicMock:
|
|
r = MagicMock()
|
|
r.rc = rc
|
|
r.stdout = stdout
|
|
r.stderr = stderr
|
|
return r
|
|
|
|
|
|
def test_recovery_succeeds_when_rdp_comes_back():
|
|
cfg = Config()
|
|
|
|
fake_transport = MagicMock()
|
|
fake_transport.health.return_value = _agent_health(True)
|
|
fake_transport.exec.return_value = _exec_result(rc=0)
|
|
|
|
with (
|
|
patch("winpodx.core.transport.agent.AgentTransport", return_value=fake_transport),
|
|
patch("winpodx.core.pod.recovery.check_rdp_port", return_value=True),
|
|
patch("winpodx.core.pod.recovery.time.sleep"),
|
|
):
|
|
result = try_recover_rdp(cfg)
|
|
|
|
assert result.success is True
|
|
assert result.action == RecoveryAction.RESTARTED_TERMSERVICE
|
|
fake_transport.exec.assert_called_once()
|
|
args, kwargs = fake_transport.exec.call_args
|
|
assert "TermService" in args[0]
|
|
|
|
|
|
def test_recovery_fails_with_agent_unreachable_when_health_down():
|
|
cfg = Config()
|
|
|
|
fake_transport = MagicMock()
|
|
fake_transport.health.return_value = _agent_health(False, detail="connection refused")
|
|
|
|
with patch("winpodx.core.transport.agent.AgentTransport", return_value=fake_transport):
|
|
result = try_recover_rdp(cfg)
|
|
|
|
assert result.success is False
|
|
assert result.action == RecoveryAction.AGENT_UNREACHABLE
|
|
assert "connection refused" in result.detail
|
|
fake_transport.exec.assert_not_called()
|
|
|
|
|
|
def test_recovery_fails_with_rdp_still_down_when_probe_never_clears():
|
|
cfg = Config()
|
|
|
|
fake_transport = MagicMock()
|
|
fake_transport.health.return_value = _agent_health(True)
|
|
fake_transport.exec.return_value = _exec_result(rc=0)
|
|
|
|
with (
|
|
patch("winpodx.core.transport.agent.AgentTransport", return_value=fake_transport),
|
|
patch("winpodx.core.pod.recovery.check_rdp_port", return_value=False),
|
|
patch("winpodx.core.pod.recovery.time.sleep"),
|
|
patch(
|
|
"winpodx.core.pod.recovery.time.monotonic",
|
|
side_effect=[0.0, 5.0, 15.0, 31.0],
|
|
),
|
|
):
|
|
result = try_recover_rdp(cfg)
|
|
|
|
assert result.success is False
|
|
assert result.action == RecoveryAction.RDP_STILL_DOWN
|
|
assert "still unreachable" in result.detail
|
|
|
|
|
|
def test_recovery_propagates_agent_exec_failure_as_agent_unreachable():
|
|
from winpodx.core.transport.base import TransportTimeoutError
|
|
|
|
cfg = Config()
|
|
|
|
fake_transport = MagicMock()
|
|
fake_transport.health.return_value = _agent_health(True)
|
|
fake_transport.exec.side_effect = TransportTimeoutError("exec hit 20s timeout")
|
|
|
|
with patch("winpodx.core.transport.agent.AgentTransport", return_value=fake_transport):
|
|
result = try_recover_rdp(cfg)
|
|
|
|
assert result.success is False
|
|
assert result.action == RecoveryAction.AGENT_UNREACHABLE
|
|
assert "20s timeout" in result.detail
|