diff --git a/src/autossh_mgr/cli.py b/src/autossh_mgr/cli.py index b2e58fa..539d149 100644 --- a/src/autossh_mgr/cli.py +++ b/src/autossh_mgr/cli.py @@ -1,4 +1,5 @@ import click +from dataclasses import replace from autossh_mgr.config import ( TunnelConfig, get_config_dir, ensure_dirs, load_tunnels, save_tunnels, get_tunnel, @@ -98,14 +99,15 @@ def add( def remove(ctx, name): """Remove a tunnel configuration.""" config_dir = ctx.obj["config_dir"] - get_tunnel(config_dir, name) # raises if not found + tunnels = load_tunnels(config_dir) + if not any(t.name == name for t in tunnels): + raise click.ClickException(f"No tunnel named '{name}'") status = get_status(config_dir, name) if status.state == "running": raise click.ClickException(f"Stop '{name}' before removing it") if not click.confirm(f"Remove tunnel '{name}'?"): return - tunnels = [t for t in load_tunnels(config_dir) if t.name != name] - save_tunnels(config_dir, tunnels) + save_tunnels(config_dir, [t for t in tunnels if t.name != name]) click.echo(f"Removed tunnel '{name}'") @@ -149,13 +151,8 @@ def config_cmd(ctx, name, key, value): f"Invalid value for '{key}': expected {_FIELD_TYPES[key].__name__}" ) tunnels = load_tunnels(config_dir) - found = False - for t in tunnels: - if t.name == name: - setattr(t, key, typed_value) - found = True - break - if not found: + if not any(t.name == name for t in tunnels): raise click.ClickException(f"No tunnel named '{name}'") + tunnels = [replace(t, **{key: typed_value}) if t.name == name else t for t in tunnels] save_tunnels(config_dir, tunnels) click.echo(f"Updated {name}.{key} = {typed_value}") diff --git a/tests/unit/test_cli_config.py b/tests/unit/test_cli_config.py index 556828c..6de60cf 100644 --- a/tests/unit/test_cli_config.py +++ b/tests/unit/test_cli_config.py @@ -1,7 +1,10 @@ +import os import pytest +from datetime import datetime, timezone from click.testing import CliRunner from autossh_mgr.cli import cli from autossh_mgr.config import load_tunnels, save_tunnels +from autossh_mgr.process import write_pid_file @pytest.fixture(autouse=True) @@ -126,3 +129,12 @@ def test_config_invalid_type(runner, config_dir, sample_tunnel): save_tunnels(config_dir, [sample_tunnel]) result = runner.invoke(cli, ["config", "web-service", "port", "notanumber"]) assert result.exit_code != 0 + + +def test_remove_running_tunnel_fails(runner, config_dir, sample_tunnel): + save_tunnels(config_dir, [sample_tunnel]) + write_pid_file(config_dir, "web-service", os.getpid(), datetime.now(timezone.utc)) + result = runner.invoke(cli, ["remove", "web-service"], input="y\n") + assert result.exit_code != 0 + assert "Stop" in result.output + assert len(load_tunnels(config_dir)) == 1