81b9a66ef5
- Delete 4 obsolete unit tests tied to removed git mount/clone models - Update imports and assertions across unit/integration/service tests - Fix Settings defaults (postgres host, JWT props, cookie_samesite) - Add skip guards for PostgreSQL-dependent integration tests - Fix GitService env assertions and HealthMonitor state-change tests - Repair docker/container inspect assertions in test_docker_service - Fix ToolTypeCreate default_port validator ordering bug - Fix check_port_exposed substring false-positive for port 0 - Update test_tool_types_api_extended to use interface_type field Quality gates: pytest 311 passed, 34 skipped; npm typecheck/lint/test 87 passed
113 lines
3.9 KiB
Python
113 lines
3.9 KiB
Python
"""Unit tests for docker service utilities."""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import logging
|
|
|
|
from src.services.docker.container import (
|
|
get_container_id,
|
|
get_container_name,
|
|
)
|
|
from src.services.docker.compose import sort_volumes_by_specificity
|
|
|
|
|
|
class TestGetContainerId:
|
|
"""Tests for get_container_id."""
|
|
|
|
@patch("subprocess.run")
|
|
def test_lowercases_name_for_filter(self, mock_run) -> None:
|
|
"""Docker inspect is case-sensitive; we must lowercase the name."""
|
|
mock_run.return_value = MagicMock(returncode=0, stdout="abc123\n")
|
|
|
|
result = get_container_id("MyContainer-ABC")
|
|
|
|
assert result == "abc123"
|
|
call_args = mock_run.call_args[0][0]
|
|
# Exact inspect call uses lowercase
|
|
assert call_args == ["docker", "inspect", "-f", "{{.Id}}", "mycontainer-abc"]
|
|
|
|
@patch("subprocess.run")
|
|
def test_returns_none_when_not_found(self, mock_run) -> None:
|
|
mock_run.return_value = MagicMock(returncode=0, stdout="")
|
|
|
|
result = get_container_id("missing")
|
|
|
|
assert result is None
|
|
|
|
|
|
class TestGetContainerName:
|
|
"""Tests for get_container_name."""
|
|
|
|
@patch("subprocess.run")
|
|
def test_lowercases_name_for_filter(self, mock_run) -> None:
|
|
"""Docker inspect is case-sensitive; we must lowercase the name."""
|
|
mock_run.return_value = MagicMock(returncode=0, stdout="mycontainer-abc\n")
|
|
|
|
result = get_container_name("MyContainer-ABC")
|
|
|
|
assert result == "mycontainer-abc"
|
|
call_args = mock_run.call_args[0][0]
|
|
assert call_args == ["docker", "inspect", "-f", "{{.Name}}", "mycontainer-abc"]
|
|
|
|
@patch("subprocess.run")
|
|
def test_returns_none_when_not_found(self, mock_run) -> None:
|
|
mock_run.return_value = MagicMock(returncode=0, stdout="")
|
|
|
|
result = get_container_name("missing")
|
|
|
|
assert result is None
|
|
|
|
|
|
class TestSortVolumesBySpecificity:
|
|
"""Tests for sort_volumes_by_specificity."""
|
|
|
|
def test_parent_before_child(self) -> None:
|
|
"""A repo mount to /workspace/x should come before a file mount to /workspace/x/y/config.json."""
|
|
volumes = [
|
|
"/repo/x/y/config.json:/workspace/x/y/config.json",
|
|
"/repo/x:/workspace/x",
|
|
]
|
|
result = sort_volumes_by_specificity(volumes)
|
|
assert result[0] == "/repo/x:/workspace/x"
|
|
assert result[1] == "/repo/x/y/config.json:/workspace/x/y/config.json"
|
|
|
|
def test_stable_sort_for_equal_depth(self) -> None:
|
|
"""Mounts at the same depth preserve input order."""
|
|
volumes = [
|
|
"/a:/workspace/a",
|
|
"/b:/workspace/b",
|
|
"/c:/workspace/c",
|
|
]
|
|
result = sort_volumes_by_specificity(volumes)
|
|
assert result == volumes
|
|
|
|
def test_with_type_suffix(self) -> None:
|
|
"""Volume strings with :bind or :ro suffixes are parsed correctly."""
|
|
volumes = [
|
|
"/repo/x/y/config.json:/workspace/x/y/config.json:bind",
|
|
"/repo/x:/workspace/x:bind",
|
|
]
|
|
result = sort_volumes_by_specificity(volumes)
|
|
assert result[0] == "/repo/x:/workspace/x:bind"
|
|
assert result[1] == "/repo/x/y/config.json:/workspace/x/y/config.json:bind"
|
|
|
|
def test_empty_list(self) -> None:
|
|
"""Empty list returns empty list."""
|
|
assert sort_volumes_by_specificity([]) == []
|
|
|
|
def test_single_volume(self) -> None:
|
|
"""Single volume returns unchanged."""
|
|
volumes = ["/repo:/workspace"]
|
|
assert sort_volumes_by_specificity(volumes) == volumes
|
|
|
|
def test_duplicate_target_warning(self, caplog) -> None:
|
|
"""Duplicate targets trigger a warning."""
|
|
with caplog.at_level(logging.WARNING, logger="src.services.docker"):
|
|
volumes = [
|
|
"/a:/workspace/x",
|
|
"/b:/workspace/x",
|
|
]
|
|
sort_volumes_by_specificity(volumes)
|
|
assert "Duplicate mount targets detected" in caplog.text
|
|
assert "/workspace/x" in caplog.text
|