diff --git a/src/autossh_mgr/cli.py b/src/autossh_mgr/cli.py index a6a5a0d..20b9d49 100644 --- a/src/autossh_mgr/cli.py +++ b/src/autossh_mgr/cli.py @@ -201,7 +201,10 @@ def stop(ctx, name): click.echo(f"{tunnel.name} is not running") continue stop_tunnel(config_dir, tunnel.name) - click.echo(f"Stopped {tunnel.name}") + if status.state == "stale": + click.echo(f"{tunnel.name}: cleaned up stale pid") + else: + click.echo(f"Stopped {tunnel.name}") @cli.command() diff --git a/tests/unit/test_cli_lifecycle.py b/tests/unit/test_cli_lifecycle.py index e8e3e3e..813dc70 100644 --- a/tests/unit/test_cli_lifecycle.py +++ b/tests/unit/test_cli_lifecycle.py @@ -1,11 +1,10 @@ import os import signal -import time import pytest from click.testing import CliRunner from unittest.mock import patch, MagicMock from autossh_mgr.cli import cli -from autossh_mgr.config import save_tunnels +from autossh_mgr.config import TunnelConfig, save_tunnels from autossh_mgr.process import write_pid_file, read_pid_file from datetime import datetime, timezone @@ -101,3 +100,62 @@ def test_check_failure(runner, with_tunnel): result = runner.invoke(cli, ["check", "web-service"]) assert result.exit_code != 0 assert "Connection refused" in result.output + + +def test_restart_running_tunnel(runner, config_dir, with_tunnel, tmp_path, monkeypatch): + fake = tmp_path / "autossh" + fake.write_text("#!/bin/sh\nexec sleep 60\n") + fake.chmod(0o755) + monkeypatch.setenv("PATH", f"{tmp_path}:{os.environ['PATH']}") + + runner.invoke(cli, ["start", "web-service"]) + first_pid = read_pid_file(config_dir, "web-service")[0] + + result = runner.invoke(cli, ["restart", "web-service"]) + assert result.exit_code == 0, result.output + + second_pid_data = read_pid_file(config_dir, "web-service") + assert second_pid_data is not None + assert second_pid_data[0] != first_pid + + os.kill(second_pid_data[0], signal.SIGKILL) + + +def test_start_all(runner, config_dir, tmp_path, monkeypatch): + fake = tmp_path / "autossh" + fake.write_text("#!/bin/sh\nexec sleep 60\n") + fake.chmod(0o755) + monkeypatch.setenv("PATH", f"{tmp_path}:{os.environ['PATH']}") + + t1 = TunnelConfig(name="alpha", host="h.com", user="u", local_port=8001, remote_port=18001) + t2 = TunnelConfig(name="beta", host="h.com", user="u", local_port=8002, remote_port=18002) + save_tunnels(config_dir, [t1, t2]) + + result = runner.invoke(cli, ["start"]) + assert result.exit_code == 0, result.output + assert "alpha" in result.output + assert "beta" in result.output + + for name in ["alpha", "beta"]: + pid_data = read_pid_file(config_dir, name) + if pid_data: + os.kill(pid_data[0], signal.SIGKILL) + + +def test_stop_all(runner, config_dir, tmp_path, monkeypatch): + fake = tmp_path / "autossh" + fake.write_text("#!/bin/sh\nexec sleep 60\n") + fake.chmod(0o755) + monkeypatch.setenv("PATH", f"{tmp_path}:{os.environ['PATH']}") + + t1 = TunnelConfig(name="alpha", host="h.com", user="u", local_port=8001, remote_port=18001) + t2 = TunnelConfig(name="beta", host="h.com", user="u", local_port=8002, remote_port=18002) + save_tunnels(config_dir, [t1, t2]) + + runner.invoke(cli, ["start"]) + result = runner.invoke(cli, ["stop"]) + assert result.exit_code == 0, result.output + assert "alpha" in result.output + assert "beta" in result.output + assert read_pid_file(config_dir, "alpha") is None + assert read_pid_file(config_dir, "beta") is None