Files
ServiceMap-Public/backend/tests/test_docker_discovery.py
T

136 lines
4.7 KiB
Python

from datetime import UTC, datetime
from typing import ClassVar
import pytest
from app import docker_discovery
from app.models import Service, ServiceKind
class _FakeImage:
tags: ClassVar[list[str]] = ["example/service:1.0"]
class _FakeContainer:
id = "abcdef1234567890"
name = "example-service"
status = "running"
image = _FakeImage()
attrs: ClassVar[dict[str, object]] = {
"State": {"Status": "running", "Health": {"Status": "healthy"}},
"Config": {"Image": "example/service:1.0"},
"NetworkSettings": {
"Networks": {"default": {}, "monitoring": {}},
"Ports": {
"8000/tcp": [{"HostIp": "0.0.0.0", "HostPort": 1253}],
"5432/tcp": None,
},
},
}
class _FakeClient:
def __init__(self, **_: object) -> None:
self.containers = self
self.closed = False
def ping(self) -> bool:
return True
def list(self, all: bool) -> list[_FakeContainer]:
assert all is True
return [_FakeContainer()]
def close(self) -> None:
self.closed = True
def test_discovery_normalizes_ports_networks_and_health(monkeypatch: pytest.MonkeyPatch) -> None:
client = _FakeClient()
monkeypatch.setattr(docker_discovery.docker, "DockerClient", lambda **kwargs: client)
result = docker_discovery.discover_containers("tcp://docker.local:2375")
assert result.docker_host == "tcp://docker.local:2375"
assert result.candidates[0].name == "example-service"
assert result.candidates[0].health == "healthy"
assert result.candidates[0].networks == ["default", "monitoring"]
assert {(port.container_port, port.host_port) for port in result.candidates[0].ports} == {
("8000/tcp", 1253),
("5432/tcp", None),
}
assert client.closed is True
def test_discovery_filters_sensitive_label_keys_but_keeps_discovery_metadata(
monkeypatch: pytest.MonkeyPatch,
) -> None:
token = "do-not-persist-this-token"
labels = {
"servicemap.api-token": token,
"servicemap.password": "do-not-persist-this-password",
"servicemap.name": "Example API",
"servicemap.probe": "http",
"servicemap.port": "8000",
"com.docker.compose.project": "app-stack",
"com.docker.compose.service": "web",
"com.docker.compose.depends_on": "db",
"com.docker.compose.postgres-password": "do-not-persist-this-compose-password",
"org.opencontainers.image.title": "Safe image title",
"other.secret": "not forwarded",
}
attrs = {
**_FakeContainer.attrs,
"Config": {**_FakeContainer.attrs["Config"], "Labels": labels},
}
monkeypatch.setattr(_FakeContainer, "attrs", attrs)
client = _FakeClient()
monkeypatch.setattr(docker_discovery.docker, "DockerClient", lambda **kwargs: client)
result = docker_discovery.discover_containers("tcp://docker.local:2375")
candidate = result.candidates[0]
serialized = str(candidate.model_dump(mode="json"))
assert token not in serialized
assert "do-not-persist-this-password" not in serialized
assert "do-not-persist-this-compose-password" not in serialized
assert "other.secret" not in candidate.labels
assert candidate.labels == {
"servicemap.name": "Example API",
"servicemap.probe": "http",
"servicemap.port": "8000",
"com.docker.compose.project": "app-stack",
"com.docker.compose.service": "web",
"com.docker.compose.depends_on": "db",
"org.opencontainers.image.title": "Safe image title",
}
assert candidate.stable_source_ref == "docker:compose:app-stack:web"
assert candidate.suggestions[0].kind == ServiceKind.HTTP
def test_discovery_rejects_untrusted_scheme() -> None:
with pytest.raises(ValueError, match="must use"):
docker_discovery.discover_containers("file:///var/run/docker.sock")
def test_discovery_marks_imported_and_carries_review_metadata(monkeypatch: pytest.MonkeyPatch) -> None:
client = _FakeClient()
monkeypatch.setattr(docker_discovery.docker, "DockerClient", lambda **kwargs: client)
existing = Service(
id="svc-1",
name="example-service",
kind=ServiceKind.HTTP,
url="http://example-service:8000",
source_type="docker",
source_ref="docker:abcdef123456@example/service:1.0",
created_at=datetime.now(UTC),
updated_at=datetime.now(UTC),
)
result = docker_discovery.discover_containers("tcp://docker.local:2375", [existing])
candidate = result.candidates[0]
assert candidate.import_state == "already imported"
assert candidate.suggestions[0].source_type == "docker"
assert candidate.suggestions[0].source_ref == "docker:abcdef123456@example/service:1.0"