"""Unit tests for devx.molecule.start_docker.""" from unittest.mock import MagicMock, patch from click.testing import CliRunner from devx.molecule.start_docker import DOCKER_SOCK, 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 call_kwargs = mock_run.call_args assert call_kwargs.args[0] == ["docker", "info"] assert call_kwargs.kwargs["env"]["DOCKER_HOST"] == f"unix://{DOCKER_SOCK}" @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("devx.molecule.start_docker.tempfile.NamedTemporaryFile") def test_starts_successfully( self, mock_ntf: MagicMock, mock_popen: MagicMock, mock_ready: MagicMock, mock_sleep: MagicMock, ) -> None: mock_ntf.return_value = MagicMock() mock_ready.side_effect = [False, True] assert start_docker_daemon(timeout=5) is True mock_popen.assert_called_once() popen_args = mock_popen.call_args.args[0] assert "dockerd" in popen_args assert "--storage-driver" in popen_args assert "vfs" in popen_args assert "-H" in popen_args assert f"unix://{DOCKER_SOCK}" in popen_args 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("devx.molecule.start_docker.tempfile.NamedTemporaryFile") def test_fails_after_timeout( self, mock_ntf: MagicMock, mock_popen: MagicMock, mock_ready: MagicMock, mock_sleep: MagicMock, ) -> None: mock_ntf.return_value = MagicMock() 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("devx.molecule.start_docker.tempfile.NamedTemporaryFile") def test_ready_on_first_check( self, mock_ntf: MagicMock, mock_popen: MagicMock, mock_ready: MagicMock, mock_sleep: MagicMock, ) -> None: mock_ntf.return_value = MagicMock() 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("devx.molecule.start_docker.tempfile.NamedTemporaryFile") def test_custom_timeout( self, mock_ntf: MagicMock, mock_popen: MagicMock, mock_ready: MagicMock, mock_sleep: MagicMock, ) -> None: mock_ntf.return_value = MagicMock() mock_ready.side_effect = [False] * 9 + [True] assert start_docker_daemon(timeout=10) is True assert mock_sleep.call_count == 9 @patch("devx.molecule.start_docker.os.environ") @patch("devx.molecule.start_docker.time.sleep") @patch("devx.molecule.start_docker.is_docker_ready") @patch("devx.molecule.start_docker.subprocess.Popen") @patch("devx.molecule.start_docker.tempfile.NamedTemporaryFile") def test_sets_docker_host( self, mock_ntf: MagicMock, mock_popen: MagicMock, mock_ready: MagicMock, mock_sleep: MagicMock, mock_environ: MagicMock, ) -> None: """DOCKER_HOST must be set so molecule connects to local daemon.""" mock_ntf.return_value = MagicMock() mock_ready.return_value = True start_docker_daemon(timeout=5) mock_environ.__setitem__.assert_called_with("DOCKER_HOST", f"unix://{DOCKER_SOCK}") 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)