"""Unit tests for devx.molecule.start_docker.""" from unittest.mock import MagicMock, mock_open, patch from click.testing import CliRunner from devx.molecule.start_docker import is_docker_ready, main, start_docker_daemon class TestIsDockerReady: @patch("devx.molecule.start_docker.subprocess.run") def test_ready(self, mock_run: MagicMock) -> None: mock_run.return_value = MagicMock(returncode=0) assert is_docker_ready() is True mock_run.assert_called_once_with(["docker", "info"], capture_output=True, check=False) @patch("devx.molecule.start_docker.subprocess.run") def test_not_ready(self, mock_run: MagicMock) -> None: mock_run.return_value = MagicMock(returncode=1) assert is_docker_ready() is False class TestStartDockerDaemon: @patch("devx.molecule.start_docker.time.sleep") @patch("devx.molecule.start_docker.is_docker_ready") @patch("devx.molecule.start_docker.subprocess.Popen") @patch("builtins.open", new_callable=mock_open) def test_starts_successfully( self, mock_file: MagicMock, mock_popen: MagicMock, mock_ready: MagicMock, mock_sleep: MagicMock, ) -> None: # First loop iteration: dockerd not ready yet. Second: ready. mock_ready.side_effect = [False, True] assert start_docker_daemon(timeout=5) is True mock_popen.assert_called_once() mock_sleep.assert_called_once_with(1) @patch("devx.molecule.start_docker.time.sleep") @patch("devx.molecule.start_docker.is_docker_ready", return_value=False) @patch("devx.molecule.start_docker.subprocess.Popen") @patch("builtins.open", new_callable=mock_open) def test_fails_after_timeout( self, mock_file: MagicMock, mock_popen: MagicMock, mock_ready: MagicMock, mock_sleep: MagicMock, ) -> None: assert start_docker_daemon(timeout=3) is False mock_popen.assert_called_once() assert mock_sleep.call_count == 3 @patch("devx.molecule.start_docker.time.sleep") @patch("devx.molecule.start_docker.is_docker_ready") @patch("devx.molecule.start_docker.subprocess.Popen") @patch("builtins.open", new_callable=mock_open) def test_ready_on_first_check( self, mock_file: MagicMock, mock_popen: MagicMock, mock_ready: MagicMock, mock_sleep: MagicMock, ) -> None: mock_ready.return_value = True assert start_docker_daemon(timeout=5) is True mock_popen.assert_called_once() mock_sleep.assert_not_called() @patch("devx.molecule.start_docker.time.sleep") @patch("devx.molecule.start_docker.is_docker_ready") @patch("devx.molecule.start_docker.subprocess.Popen") @patch("builtins.open", new_callable=mock_open) def test_custom_timeout( self, mock_file: MagicMock, mock_popen: MagicMock, mock_ready: MagicMock, mock_sleep: MagicMock, ) -> None: # 9 iterations not ready, 10th ready. mock_ready.side_effect = [False] * 9 + [True] assert start_docker_daemon(timeout=10) is True assert mock_sleep.call_count == 9 class TestMain: @patch("devx.molecule.start_docker.start_docker_daemon", return_value=True) def test_success(self, mock_start: MagicMock) -> None: runner = CliRunner() result = runner.invoke(main, []) assert result.exit_code == 0 @patch("devx.molecule.start_docker.start_docker_daemon", return_value=False) def test_failure(self, mock_start: MagicMock) -> None: runner = CliRunner() result = runner.invoke(main, []) assert result.exit_code == 1 @patch("devx.molecule.start_docker.start_docker_daemon", return_value=True) def test_custom_timeout_flag(self, mock_start: MagicMock) -> None: runner = CliRunner() result = runner.invoke(main, ["--timeout", "60"]) assert result.exit_code == 0 mock_start.assert_called_once_with(60)