generated from coulomb/repo-seed
feat(restart): route reverse tunnels through stale-forward cleanup
bridge restart now means blank-slate recovery: reverse tunnels run should_cleanup_tunnel and clear orphan remote listeners before reconnecting; healthy forwards are left running. Local-direction tunnels keep stop/start only. CLI and MCP report per-tunnel actions (healthy, cleaned_and_restarted, restarted, error) and exit non-zero on cleanup failure. Closes BRIDGE-WP-0005.
This commit is contained in:
@@ -266,25 +266,96 @@ class TestCheckCommand:
|
||||
assert result.exit_code == 1
|
||||
|
||||
|
||||
REVERSE_CONFIG = VALID_CONFIG
|
||||
|
||||
LOCAL_TUNNEL_CONFIG = textwrap.dedent("""\
|
||||
tunnels:
|
||||
k3s-api:
|
||||
host: host.local
|
||||
remote_port: 6443
|
||||
local_port: 6443
|
||||
ssh_user: ubuntu
|
||||
ssh_key: ~/.ssh/id_ops
|
||||
actor: adm-bernd
|
||||
direction: local
|
||||
actors:
|
||||
adm-bernd:
|
||||
class: adm
|
||||
description: Bernd
|
||||
""")
|
||||
|
||||
|
||||
class TestRestartCommand:
|
||||
def test_restart_unknown_tunnel_exit_1(self, env):
|
||||
result = runner.invoke(app, ["restart", "nonexistent"], env=env)
|
||||
assert result.exit_code == 1
|
||||
|
||||
def test_restart_help_mentions_remote_cleanup(self):
|
||||
result = runner.invoke(app, ["restart", "--help"])
|
||||
assert result.exit_code == 0
|
||||
assert "stale-forward" in result.output.lower() or "remote" in result.output.lower()
|
||||
|
||||
@pytest.mark.capability("bridge_restart")
|
||||
@pytest.mark.access_mode("cli")
|
||||
def test_restart_calls_stop_then_start(self, env):
|
||||
with patch("bridge.cli.TunnelManager") as mock_mgr_cls:
|
||||
def test_restart_reverse_tunnel_delegates_to_cleanup(self, env):
|
||||
from bridge.cleanup import CleanupAction
|
||||
|
||||
with patch("bridge.cli.restart_tunnel") as mock_restart:
|
||||
mock_restart.return_value = CleanupAction(
|
||||
"test-tunnel", "healthy", "remote forward healthy"
|
||||
)
|
||||
result = runner.invoke(app, ["restart", "test-tunnel"], env=env)
|
||||
|
||||
assert result.exit_code == 0
|
||||
mock_restart.assert_called_once()
|
||||
assert "test-tunnel: healthy" in result.output
|
||||
|
||||
def test_restart_reverse_tunnel_reports_cleaned_and_restarted(self, env):
|
||||
from bridge.cleanup import CleanupAction
|
||||
|
||||
with patch("bridge.cli.restart_tunnel") as mock_restart:
|
||||
mock_restart.return_value = CleanupAction(
|
||||
"test-tunnel",
|
||||
"cleaned_and_restarted",
|
||||
"stale forward; restarted tunnel; cleared",
|
||||
)
|
||||
result = runner.invoke(app, ["restart", "test-tunnel"], env=env)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "cleaned_and_restarted" in result.output
|
||||
|
||||
def test_restart_reverse_tunnel_error_exit_1(self, env):
|
||||
from bridge.cleanup import CleanupAction
|
||||
|
||||
with patch("bridge.cli.restart_tunnel") as mock_restart:
|
||||
mock_restart.return_value = CleanupAction(
|
||||
"test-tunnel", "error", "cleanup failed: still_listening"
|
||||
)
|
||||
result = runner.invoke(app, ["restart", "test-tunnel"], env=env)
|
||||
|
||||
assert result.exit_code == 1
|
||||
assert "error" in result.output
|
||||
|
||||
def test_restart_local_tunnel_uses_stop_start(self, tmp_path, state_dir):
|
||||
config_file = tmp_path / "tunnels.yaml"
|
||||
config_file.write_text(LOCAL_TUNNEL_CONFIG)
|
||||
env = {
|
||||
"BRIDGE_CONFIG": str(config_file),
|
||||
"BRIDGE_STATE_DIR": str(state_dir),
|
||||
}
|
||||
|
||||
with patch("bridge.cleanup.TunnelManager") as mock_mgr_cls:
|
||||
mock_mgr = MagicMock()
|
||||
mock_mgr_cls.return_value = mock_mgr
|
||||
call_order = []
|
||||
mock_mgr.stop.side_effect = lambda: call_order.append("stop")
|
||||
mock_mgr.start.side_effect = lambda: call_order.append("start")
|
||||
|
||||
result = runner.invoke(app, ["restart", "test-tunnel"], env=env)
|
||||
result = runner.invoke(app, ["restart", "k3s-api"], env=env)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert call_order == ["stop", "start"]
|
||||
assert "k3s-api: restarted" in result.output
|
||||
|
||||
|
||||
class TestCertStatusCommand:
|
||||
|
||||
Reference in New Issue
Block a user