From c1d6d72297fce50e81af38b682f2f87a4790d6bb Mon Sep 17 00:00:00 2001 From: maro114510 Date: Fri, 29 May 2026 14:49:22 +0900 Subject: [PATCH 1/2] fix(cli): skip update check for unknown subcommands to fail fast --- src/apm_cli/cli.py | 4 +- .../integration/test_version_notification.py | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/apm_cli/cli.py b/src/apm_cli/cli.py index 1179090e0..1e1537b20 100644 --- a/src/apm_cli/cli.py +++ b/src/apm_cli/cli.py @@ -64,8 +64,8 @@ def cli(ctx): warnings.filterwarnings("ignore", category=AgentsTargetDeprecationWarning) - # Check for updates non-blockingly (only if not already showing version) - if not ctx.resilient_parsing: + # Check for updates only for known commands; skip on invalid input to fail fast. + if not ctx.resilient_parsing and ctx.invoked_subcommand in cli.commands: _check_and_notify_updates() diff --git a/tests/integration/test_version_notification.py b/tests/integration/test_version_notification.py index d962f2324..60a58eed5 100644 --- a/tests/integration/test_version_notification.py +++ b/tests/integration/test_version_notification.py @@ -62,6 +62,43 @@ def test_notification_does_not_block_command(self, mock_check): self.assertIn("initialized successfully", result.output) +class TestUpdateCheckSkippedOnInvalidCommand(unittest.TestCase): + """Test that update check is skipped for unknown/invalid commands.""" + + def setUp(self): + self.runner = CliRunner() + + @patch("apm_cli.cli._check_and_notify_updates") + def test_no_update_check_on_unknown_command(self, mock_check): + """Update check must not run when an unknown command is invoked.""" + from apm_cli.cli import cli + + result = self.runner.invoke(cli, ["invalid"]) + + mock_check.assert_not_called() + self.assertNotEqual(result.exit_code, 0) + self.assertIn("invalid", result.output) + + @patch("apm_cli.cli._check_and_notify_updates") + def test_update_check_runs_on_valid_command(self, mock_check): + """Update check must still run when a known command is invoked.""" + from apm_cli.cli import cli + + with self.runner.isolated_filesystem(): + self.runner.invoke(cli, ["init", "test-project", "--yes"]) + + mock_check.assert_called_once() + + @patch("apm_cli.cli._check_and_notify_updates") + def test_no_update_check_without_subcommand(self, mock_check): + """Update check must not run when no subcommand is given (help display).""" + from apm_cli.cli import cli + + self.runner.invoke(cli, []) + + mock_check.assert_not_called() + + class TestUpdateCommand(unittest.TestCase): """Test the update command.""" From d5c1c59f4c0314ed7ce3aaf38177253be23163da Mon Sep 17 00:00:00 2001 From: maro114510 Date: Sat, 20 Jun 2026 03:34:46 +0900 Subject: [PATCH 2/2] fix(cli): use ctx.command.get_command() instead of cli.commands lookup Addresses Copilot's feedback: routes the known-command check through Click's public resolution API rather than inspecting the internal commands dict directly, making the guard robust to future dynamic command loading. --- src/apm_cli/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apm_cli/cli.py b/src/apm_cli/cli.py index 892448c3a..05ab45f50 100644 --- a/src/apm_cli/cli.py +++ b/src/apm_cli/cli.py @@ -143,7 +143,7 @@ def cli(ctx, verbose: bool) -> None: warnings.filterwarnings("ignore", category=AgentsTargetDeprecationWarning) # Check for updates only for known commands; skip on invalid input to fail fast. - if not ctx.resilient_parsing and ctx.invoked_subcommand in cli.commands: + if not ctx.resilient_parsing and ctx.command.get_command(ctx, ctx.invoked_subcommand) is not None: _check_and_notify_updates()