feat(check): add Layer 1 local backend probe before SSH auth

check_connectivity now runs a two-layer short-circuit probe:
  Layer 1: TCP connect to (local_host, local_port) to confirm the
           backend service (e.g. Gitea, Next.js) is actually listening.
  Layer 2: original SSH auth probe (unchanged).

This fixes the common false-positive where check returned OK while the
tunneled backend was down. Unlike the previously removed _is_port_in_use
bind check (commit 4c9cd00), this uses connect() with correct semantics:
the forward target SHOULD be listening, not free.
This commit is contained in:
2026-08-05 15:27:04 +08:00
parent 4c9cd00c90
commit 6124abbdb2
3 changed files with 75 additions and 14 deletions

View File

@@ -87,7 +87,9 @@ def test_status_single(runner, config_dir, with_tunnel):
def test_check_success(runner, with_tunnel):
with patch("autossh_mgr.check.subprocess.run") as mock_run:
mock_sock = MagicMock()
with patch("autossh_mgr.check.socket.socket", return_value=mock_sock), \
patch("autossh_mgr.check.subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0, stderr="")
result = runner.invoke(cli, ["check", "web-service"])
assert result.exit_code == 0
@@ -95,7 +97,9 @@ def test_check_success(runner, with_tunnel):
def test_check_failure(runner, with_tunnel):
with patch("autossh_mgr.check.subprocess.run") as mock_run:
mock_sock = MagicMock()
with patch("autossh_mgr.check.socket.socket", return_value=mock_sock), \
patch("autossh_mgr.check.subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=255, stderr="Connection refused")
result = runner.invoke(cli, ["check", "web-service"])
assert result.exit_code != 0