-
Notifications
You must be signed in to change notification settings - Fork 113
[Test Improver] test: expand coverage for update command (64% -> ~95%) #657
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -93,5 +93,201 @@ def test_update_uses_shell_installer_on_unix( | |||||||
| mock_chmod.assert_called_once() | ||||||||
|
|
||||||||
|
|
||||||||
| class TestUpdatePlatformHelpers(unittest.TestCase): | ||||||||
| """Tests for platform-detection helper functions.""" | ||||||||
|
|
||||||||
| def test_is_windows_platform_true_on_win32(self): | ||||||||
| with patch.object(update_module.sys, "platform", "win32"): | ||||||||
| self.assertTrue(update_module._is_windows_platform()) | ||||||||
|
|
||||||||
| def test_is_windows_platform_false_on_linux(self): | ||||||||
| with patch.object(update_module.sys, "platform", "linux"): | ||||||||
| self.assertFalse(update_module._is_windows_platform()) | ||||||||
|
|
||||||||
| def test_is_windows_platform_false_on_darwin(self): | ||||||||
| with patch.object(update_module.sys, "platform", "darwin"): | ||||||||
| self.assertFalse(update_module._is_windows_platform()) | ||||||||
|
|
||||||||
| def test_installer_url_windows(self): | ||||||||
| with patch.object(update_module.sys, "platform", "win32"): | ||||||||
| url = update_module._get_update_installer_url() | ||||||||
| self.assertEqual(url, "https://aka.ms/apm-windows") | ||||||||
|
|
||||||||
| def test_installer_url_unix(self): | ||||||||
| with patch.object(update_module.sys, "platform", "linux"): | ||||||||
| url = update_module._get_update_installer_url() | ||||||||
| self.assertEqual(url, "https://aka.ms/apm-unix") | ||||||||
|
|
||||||||
| def test_installer_suffix_windows(self): | ||||||||
| with patch.object(update_module.sys, "platform", "win32"): | ||||||||
| suffix = update_module._get_update_installer_suffix() | ||||||||
| self.assertEqual(suffix, ".ps1") | ||||||||
|
|
||||||||
| def test_installer_suffix_unix(self): | ||||||||
| with patch.object(update_module.sys, "platform", "linux"): | ||||||||
| suffix = update_module._get_update_installer_suffix() | ||||||||
| self.assertEqual(suffix, ".sh") | ||||||||
|
|
||||||||
| def test_manual_update_command_unix(self): | ||||||||
| with patch.object(update_module.sys, "platform", "linux"): | ||||||||
| command = update_module._get_manual_update_command() | ||||||||
| self.assertIn("aka.ms/apm-unix", command) | ||||||||
| self.assertIn("curl", command) | ||||||||
|
|
||||||||
| def test_installer_run_command_unix_bin_sh_exists(self): | ||||||||
| with patch.object(update_module.sys, "platform", "linux"), \ | ||||||||
| patch.object(update_module.os.path, "exists", return_value=True): | ||||||||
| cmd = update_module._get_installer_run_command("/tmp/install.sh") | ||||||||
| self.assertEqual(cmd, ["/bin/sh", "/tmp/install.sh"]) | ||||||||
|
|
||||||||
| def test_installer_run_command_unix_fallback_to_sh(self): | ||||||||
| with patch.object(update_module.sys, "platform", "linux"), \ | ||||||||
| patch.object(update_module.os.path, "exists", return_value=False): | ||||||||
| cmd = update_module._get_installer_run_command("/tmp/install.sh") | ||||||||
| self.assertEqual(cmd, ["sh", "/tmp/install.sh"]) | ||||||||
|
|
||||||||
| def test_installer_run_command_windows_powershell_not_found(self): | ||||||||
| with patch.object(update_module.sys, "platform", "win32"), \ | ||||||||
| patch.object(update_module.shutil, "which", return_value=None): | ||||||||
| with self.assertRaises(FileNotFoundError): | ||||||||
| update_module._get_installer_run_command("/tmp/install.ps1") | ||||||||
|
|
||||||||
| def test_installer_run_command_windows_pwsh_fallback(self): | ||||||||
| def _which(name): | ||||||||
| return "pwsh.exe" if name == "pwsh" else None | ||||||||
|
|
||||||||
| with patch.object(update_module.sys, "platform", "win32"), \ | ||||||||
| patch.object(update_module.shutil, "which", side_effect=_which): | ||||||||
| cmd = update_module._get_installer_run_command("/tmp/install.ps1") | ||||||||
| self.assertEqual(cmd[0], "pwsh.exe") | ||||||||
| self.assertIn("-File", cmd) | ||||||||
|
|
||||||||
|
|
||||||||
| class TestUpdateCommandLogic(unittest.TestCase): | ||||||||
| """Tests for the update click command business logic.""" | ||||||||
|
|
||||||||
| def setUp(self): | ||||||||
| self.runner = CliRunner() | ||||||||
|
|
||||||||
| @patch("apm_cli.commands.update.get_version", return_value="unknown") | ||||||||
| def test_update_dev_version_warns_and_returns(self, mock_version): | ||||||||
| result = self.runner.invoke(cli, ["update"]) | ||||||||
| self.assertEqual(result.exit_code, 0) | ||||||||
| self.assertIn("development mode", result.output) | ||||||||
|
|
||||||||
| @patch("apm_cli.commands.update.get_version", return_value="unknown") | ||||||||
| def test_update_dev_version_check_flag_no_reinstall_hint(self, mock_version): | ||||||||
| """When --check is passed with dev version, reinstall hint should be suppressed.""" | ||||||||
| result = self.runner.invoke(cli, ["update", "--check"]) | ||||||||
| self.assertEqual(result.exit_code, 0) | ||||||||
| self.assertIn("development mode", result.output) | ||||||||
| self.assertNotIn("reinstall", result.output) | ||||||||
|
|
||||||||
| @patch("apm_cli.utils.version_checker.get_latest_version_from_github", return_value=None) | ||||||||
| @patch("apm_cli.commands.update.get_version", return_value="1.0.0") | ||||||||
| def test_update_cannot_fetch_latest_exits_1(self, mock_version, mock_latest): | ||||||||
| result = self.runner.invoke(cli, ["update"]) | ||||||||
| self.assertEqual(result.exit_code, 1) | ||||||||
| self.assertIn("Unable to fetch latest version", result.output) | ||||||||
|
|
||||||||
| @patch("apm_cli.utils.version_checker.get_latest_version_from_github", return_value="1.0.0") | ||||||||
| @patch("apm_cli.commands.update.get_version", return_value="1.0.0") | ||||||||
| def test_update_already_on_latest(self, mock_version, mock_latest): | ||||||||
| result = self.runner.invoke(cli, ["update"]) | ||||||||
| self.assertEqual(result.exit_code, 0) | ||||||||
| self.assertIn("latest version", result.output) | ||||||||
|
|
||||||||
| @patch("apm_cli.utils.version_checker.get_latest_version_from_github", return_value="1.1.0") | ||||||||
| @patch("apm_cli.commands.update.get_version", return_value="1.0.0") | ||||||||
| def test_update_check_flag_shows_available_no_install(self, mock_version, mock_latest): | ||||||||
| result = self.runner.invoke(cli, ["update", "--check"]) | ||||||||
| self.assertEqual(result.exit_code, 0) | ||||||||
| self.assertIn("1.0.0", result.output) | ||||||||
| self.assertIn("1.1.0", result.output) | ||||||||
|
|
||||||||
| @patch("requests.get") | ||||||||
| @patch("subprocess.run") | ||||||||
| @patch("apm_cli.commands.update.get_version", return_value="1.0.0") | ||||||||
| @patch("apm_cli.commands.update.os.chmod") | ||||||||
| @patch("apm_cli.utils.version_checker.get_latest_version_from_github", return_value="1.1.0") | ||||||||
| def test_update_installer_failure_exits_1( | ||||||||
| self, mock_latest, mock_chmod, mock_version, mock_run, mock_get | ||||||||
| ): | ||||||||
| mock_response = Mock() | ||||||||
| mock_response.text = "echo install" | ||||||||
| mock_response.raise_for_status.return_value = None | ||||||||
| mock_get.return_value = mock_response | ||||||||
| mock_run.return_value = Mock(returncode=1) | ||||||||
|
|
||||||||
| with patch.object(update_module.sys, "platform", "linux"), \ | ||||||||
| patch("apm_cli.commands.update.os.path.exists", return_value=True): | ||||||||
| result = self.runner.invoke(cli, ["update"]) | ||||||||
|
|
||||||||
| self.assertEqual(result.exit_code, 1) | ||||||||
| self.assertIn("Installation failed", result.output) | ||||||||
|
|
||||||||
| @patch("apm_cli.commands.update.get_version", return_value="1.0.0") | ||||||||
| @patch("apm_cli.utils.version_checker.get_latest_version_from_github", return_value="1.1.0") | ||||||||
| def test_update_requests_not_available_exits_1(self, mock_latest, mock_version): | ||||||||
| """When requests library is missing, exit with clear message.""" | ||||||||
| import builtins | ||||||||
| real_import = builtins.__import__ | ||||||||
|
|
||||||||
| def mock_import(name, *args, **kwargs): | ||||||||
| if name == "requests": | ||||||||
| raise ImportError("No module named 'requests'") | ||||||||
| return real_import(name, *args, **kwargs) | ||||||||
|
|
||||||||
| with patch("builtins.__import__", side_effect=mock_import), \ | ||||||||
| patch.object(update_module.sys, "platform", "linux"): | ||||||||
| result = self.runner.invoke(cli, ["update"]) | ||||||||
|
|
||||||||
| self.assertEqual(result.exit_code, 1) | ||||||||
| self.assertIn("requests", result.output) | ||||||||
|
|
||||||||
| @patch("requests.get") | ||||||||
| @patch("apm_cli.commands.update.get_version", return_value="1.0.0") | ||||||||
| @patch("apm_cli.commands.update.os.chmod") | ||||||||
| @patch("apm_cli.utils.version_checker.get_latest_version_from_github", return_value="1.1.0") | ||||||||
| def test_update_network_error_exits_1(self, mock_latest, mock_chmod, mock_version, mock_get): | ||||||||
| mock_get.side_effect = Exception("Network error") | ||||||||
|
|
||||||||
| with patch.object(update_module.sys, "platform", "linux"): | ||||||||
| result = self.runner.invoke(cli, ["update"]) | ||||||||
|
|
||||||||
| self.assertEqual(result.exit_code, 1) | ||||||||
| self.assertIn("Update failed", result.output) | ||||||||
|
|
||||||||
| @patch("requests.get") | ||||||||
| @patch("subprocess.run") | ||||||||
| @patch("apm_cli.commands.update.get_version", return_value="1.0.0") | ||||||||
| @patch("apm_cli.commands.update.os.chmod") | ||||||||
| @patch("apm_cli.utils.version_checker.get_latest_version_from_github", return_value="1.1.0") | ||||||||
| def test_update_temp_file_cleanup_on_success( | ||||||||
| self, mock_latest, mock_chmod, mock_version, mock_run, mock_get | ||||||||
| ): | ||||||||
| """Verify temporary script is deleted after successful install.""" | ||||||||
| deleted_paths = [] | ||||||||
| original_unlink = update_module.os.unlink | ||||||||
|
|
||||||||
| def tracking_unlink(path): | ||||||||
| deleted_paths.append(path) | ||||||||
|
||||||||
| deleted_paths.append(path) | |
| deleted_paths.append(path) | |
| original_unlink(path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apm updatewrites the installer script intoget_apm_temp_dir()(env var / user config dependent). These tests invoke the command without pinning that temp dir, so they can fail on developer machines whereAPM_TEMP_DIRor~/.apm/config.jsonpoints to a non-writable/non-existent location. Consider settingAPM_TEMP_DIRto a temporary directory (or otherwise patchingget_apm_temp_dir) insetUpso the tests are hermetic across environments.