mirror of
https://github.com/datahub-project/datahub.git
synced 2025-08-07 00:37:56 +00:00
265 lines
9.0 KiB
Python
265 lines
9.0 KiB
Python
from unittest.mock import MagicMock, patch
|
|
|
|
import click
|
|
import pytest
|
|
from docker import DockerClient
|
|
|
|
from datahub.cli.docker_cli import (
|
|
_check_upgrade_and_show_instructions,
|
|
check,
|
|
download_compose_files,
|
|
get_github_file_url,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_click():
|
|
with patch("datahub.cli.docker_cli.click") as mock:
|
|
yield mock
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_docker_client():
|
|
with patch("datahub.cli.docker_check.get_docker_client") as mock:
|
|
client = MagicMock(spec=DockerClient)
|
|
mock.return_value.__enter__.return_value = client
|
|
yield client
|
|
|
|
|
|
@pytest.fixture
|
|
def click_context():
|
|
"""Create a Click context for testing commands"""
|
|
ctx = click.Context(check)
|
|
ctx.obj = {} # Initialize empty context object
|
|
return ctx
|
|
|
|
|
|
def test_check_healthy(mock_click, mock_docker_client, click_context):
|
|
# Setup mock Docker client responses
|
|
# Mock containers
|
|
container1 = MagicMock()
|
|
container1.name = "datahub-mysql-1"
|
|
container1.status = "running"
|
|
container1.attrs = {"State": {"Health": {"Status": "healthy"}}}
|
|
container1.labels = {
|
|
"com.docker.compose.service": "mysql",
|
|
"com.docker.compose.project.config_files": "docker-compose.yml",
|
|
}
|
|
|
|
container2 = MagicMock()
|
|
container2.name = "datahub-frontend-1"
|
|
container2.status = "running"
|
|
container2.attrs = {"State": {"Health": {"Status": "healthy"}}}
|
|
container2.labels = {
|
|
"com.docker.compose.service": "frontend",
|
|
"com.docker.compose.project.config_files": "docker-compose.yml",
|
|
}
|
|
|
|
mock_docker_client.containers.list.return_value = [container1, container2]
|
|
mock_docker_client.containers.get.return_value = container1
|
|
mock_docker_client.containers.get.side_effect = lambda name: next(
|
|
(c for c in [container1, container2] if c.name == name), None
|
|
)
|
|
|
|
# Mock networks
|
|
network = MagicMock()
|
|
network.name = "datahub_network"
|
|
mock_docker_client.networks.list.return_value = [network]
|
|
|
|
# Mock volumes
|
|
volume = MagicMock()
|
|
volume.name = "datahub_mysqldata"
|
|
mock_docker_client.volumes.list.return_value = [volume]
|
|
|
|
# Mock compose file
|
|
with (
|
|
patch(
|
|
"datahub.cli.docker_check._get_services_from_compose",
|
|
return_value={"mysql", "frontend"},
|
|
),
|
|
patch(
|
|
"datahub.cli.docker_check._get_volumes_from_compose",
|
|
return_value={"datahub_mysqldata"},
|
|
),
|
|
):
|
|
# Execute
|
|
click_context.invoke(check)
|
|
|
|
# Verify
|
|
mock_click.secho.assert_called_once_with("✔ No issues detected", fg="green")
|
|
|
|
|
|
def test_check_unhealthy(mock_click, mock_docker_client, click_context):
|
|
# Setup mock Docker client responses
|
|
# Mock containers with unhealthy status
|
|
container1 = MagicMock()
|
|
container1.name = "datahub-mysql-1"
|
|
container1.status = "running"
|
|
container1.attrs = {"State": {"Health": {"Status": "unhealthy"}}}
|
|
container1.labels = {
|
|
"com.docker.compose.service": "mysql",
|
|
"com.docker.compose.project.config_files": "docker-compose.yml",
|
|
}
|
|
|
|
mock_docker_client.containers.list.return_value = [container1]
|
|
mock_docker_client.containers.get.return_value = container1
|
|
|
|
# Mock networks
|
|
network = MagicMock()
|
|
network.name = "datahub_network"
|
|
mock_docker_client.networks.list.return_value = [network]
|
|
|
|
# Mock volumes
|
|
volume = MagicMock()
|
|
volume.name = "datahub_mysqldata"
|
|
mock_docker_client.volumes.list.return_value = [volume]
|
|
|
|
# Mock compose file
|
|
with (
|
|
patch(
|
|
"datahub.cli.docker_check._get_services_from_compose",
|
|
return_value={"mysql"},
|
|
),
|
|
patch(
|
|
"datahub.cli.docker_check._get_volumes_from_compose",
|
|
return_value={"datahub_mysqldata"},
|
|
),
|
|
):
|
|
# Execute and verify
|
|
with pytest.raises(Exception) as exc_info:
|
|
click_context.invoke(check)
|
|
|
|
assert "issues were detected" in str(exc_info.value)
|
|
|
|
|
|
def test_check_missing_containers(mock_click, mock_docker_client, click_context):
|
|
# Setup mock Docker client responses
|
|
# Return empty container list to simulate missing containers
|
|
mock_docker_client.containers.list.return_value = []
|
|
|
|
# Mock networks
|
|
network = MagicMock()
|
|
network.name = "datahub_network"
|
|
mock_docker_client.networks.list.return_value = [network]
|
|
|
|
# Mock volumes
|
|
volume = MagicMock()
|
|
volume.name = "datahub_mysqldata"
|
|
mock_docker_client.volumes.list.return_value = [volume]
|
|
|
|
# Mock compose file
|
|
with (
|
|
patch(
|
|
"datahub.cli.docker_check._get_services_from_compose",
|
|
return_value={"mysql", "frontend"},
|
|
),
|
|
patch(
|
|
"datahub.cli.docker_check._get_volumes_from_compose",
|
|
return_value={"datahub_mysqldata"},
|
|
),
|
|
):
|
|
# Execute and verify
|
|
with pytest.raises(Exception) as exc_info:
|
|
click_context.invoke(check)
|
|
|
|
assert "issues were detected" in str(exc_info.value)
|
|
|
|
|
|
def test_get_github_file_url_default():
|
|
"""Test get_github_file_url with default environment (no DOCKER_COMPOSE_BASE set)"""
|
|
with patch.dict("os.environ", {}, clear=True):
|
|
result = get_github_file_url("v1.0.0")
|
|
expected = "https://raw.githubusercontent.com/datahub-project/datahub/v1.0.0/docker/quickstart/docker-compose.quickstart-profile.yml"
|
|
assert result == expected
|
|
|
|
|
|
def test_get_github_file_url_custom_base():
|
|
"""Test get_github_file_url with custom DOCKER_COMPOSE_BASE environment variable"""
|
|
custom_base = "https://github.com/my-fork/datahub/my-branch"
|
|
with patch.dict("os.environ", {"DOCKER_COMPOSE_BASE": custom_base}):
|
|
result = get_github_file_url("v2.0.0")
|
|
expected = (
|
|
f"{custom_base}/docker/quickstart/docker-compose.quickstart-profile.yml"
|
|
)
|
|
assert result == expected
|
|
|
|
|
|
def test_download_compose_files_404_error():
|
|
"""Test download_compose_files when quickstart_download_response.status_code is 404"""
|
|
# Mock the requests.Session and its get method
|
|
mock_response = MagicMock()
|
|
mock_response.status_code = 404
|
|
|
|
mock_session = MagicMock()
|
|
mock_session.get.return_value = mock_response
|
|
|
|
with (
|
|
patch("datahub.cli.docker_cli.requests.Session", return_value=mock_session),
|
|
patch("datahub.cli.docker_cli.tempfile.NamedTemporaryFile") as mock_tempfile,
|
|
):
|
|
# Setup tempfile mock
|
|
mock_tempfile_instance = MagicMock()
|
|
mock_tempfile_instance.__enter__.return_value = mock_tempfile_instance
|
|
mock_tempfile_instance.__exit__.return_value = None
|
|
mock_tempfile_instance.name = "/tmp/test.yml"
|
|
mock_tempfile.return_value = mock_tempfile_instance
|
|
|
|
# Test that ClickException is raised with the correct message
|
|
with pytest.raises(click.ClickException) as exc_info:
|
|
download_compose_files(None, [], "v1.0.0")
|
|
|
|
expected_message = "Could not find quickstart compose file for version v1.0.0. Please try a different version or check the version exists at https://github.com/datahub-project/datahub/releases"
|
|
assert str(exc_info.value) == expected_message
|
|
|
|
|
|
def test_check_upgrade_and_show_instructions_upgrade_not_supported():
|
|
"""Test _check_upgrade_and_show_instructions when check_upgrade_supported returns False"""
|
|
# Mock the dependencies
|
|
mock_status = MagicMock()
|
|
mock_status.is_ok.return_value = (
|
|
True # Status is OK, so migration instructions will be shown
|
|
)
|
|
|
|
with (
|
|
patch(
|
|
"datahub.cli.docker_cli.check_docker_quickstart", return_value=mock_status
|
|
),
|
|
patch("datahub.cli.docker_cli.check_upgrade_supported", return_value=False),
|
|
patch(
|
|
"datahub.cli.docker_cli.show_migration_instructions"
|
|
) as mock_show_migration,
|
|
):
|
|
# Call the function
|
|
result = _check_upgrade_and_show_instructions([])
|
|
|
|
# Verify that show_migration_instructions was called (since status.is_ok() returns True)
|
|
mock_show_migration.assert_called_once()
|
|
|
|
# Verify that the function returns False
|
|
assert result is False
|
|
|
|
|
|
def test_check_upgrade_and_show_instructions_upgrade_not_supported_repair():
|
|
"""Test _check_upgrade_and_show_instructions when check_upgrade_supported returns False and status is not OK"""
|
|
# Mock the dependencies
|
|
mock_status = MagicMock()
|
|
mock_status.is_ok.return_value = (
|
|
False # Status is not OK, so repair instructions will be shown
|
|
)
|
|
|
|
with (
|
|
patch(
|
|
"datahub.cli.docker_cli.check_docker_quickstart", return_value=mock_status
|
|
),
|
|
patch("datahub.cli.docker_cli.check_upgrade_supported", return_value=False),
|
|
patch("datahub.cli.docker_cli.show_repair_instructions") as mock_show_repair,
|
|
):
|
|
# Call the function
|
|
result = _check_upgrade_and_show_instructions([])
|
|
|
|
# Verify that show_repair_instructions was called (since status.is_ok() returns False)
|
|
mock_show_repair.assert_called_once()
|
|
|
|
# Verify that the function returns False
|
|
assert result is False
|