Files
devx/tests/unit/test_tools_check_docker_init.py
T
emil 9f1bdc4cf1
Post-merge / detect-and-configure (push) Successful in 12s
Post-merge / release-and-maintain (push) Successful in 1m19s
DEVX-145: feat: extract reusable components from infra and grm into devx
2026-07-22 20:56:27 +00:00

292 lines
9.2 KiB
Python

"""Unit tests for devx.tools.check_docker_init."""
from __future__ import annotations
import textwrap
from pathlib import Path
from click.testing import CliRunner
from devx.tools.check_docker_init import _check_template, _find_compose_templates, _parse_services, main
class TestFindComposeTemplates:
def test_finds_docker_compose_templates(self, tmp_path: Path):
(tmp_path / "docker-compose.observability.yml.j2").write_text("services:")
(tmp_path / "docker-compose.service.yml.j2").write_text("services:")
result = _find_compose_templates(tmp_path)
assert len(result) == 2
def test_finds_exporters_compose(self, tmp_path: Path):
(tmp_path / "exporters-compose.yml.j2").write_text("services:")
result = _find_compose_templates(tmp_path)
assert len(result) == 1
assert "exporters-compose" in str(result[0])
def test_finds_compose_yaml_templates(self, tmp_path: Path):
(tmp_path / "compose.yaml.j2").write_text("services:")
result = _find_compose_templates(tmp_path)
assert len(result) == 1
def test_single_file(self, tmp_path: Path):
f = tmp_path / "docker-compose.test.yml.j2"
f.write_text("services:")
result = _find_compose_templates(f)
assert result == [f]
def test_nonexistent_path(self, tmp_path: Path):
assert _find_compose_templates(tmp_path / "nonexistent") == []
def test_deduplicates(self, tmp_path: Path):
(tmp_path / "docker-compose.yml.j2").write_text("services:")
result = _find_compose_templates(tmp_path)
assert len(result) == 1
def test_recursive(self, tmp_path: Path):
(tmp_path / "sub").mkdir()
(tmp_path / "sub" / "docker-compose.yml.j2").write_text("services:")
result = _find_compose_templates(tmp_path)
assert len(result) == 1
class TestParseServices:
def test_basic_services(self):
content = textwrap.dedent("""
services:
web:
image: nginx
healthcheck:
test: ["CMD", "curl", "localhost"]
db:
image: postgres
networks:
default:
""").strip()
services = _parse_services(content)
assert "web" in services
assert "db" in services
assert any("image: nginx" in line for line in services["web"])
def test_jinja2_service_names(self):
content = textwrap.dedent("""
services:
{{ app_name }}:
image: {{ app_image }}
healthcheck:
test: ["CMD", "curl"]
{{ app_name }}-db:
image: postgres
networks:
traefik:
""").strip()
services = _parse_services(content)
assert "{{ app_name }}" in services
assert "{{ app_name }}-db" in services
def test_no_services_section(self):
content = "version: '3'\nvolumes:\n data:"
assert _parse_services(content) == {}
def test_service_at_end_of_file(self):
content = textwrap.dedent("""
services:
web:
image: nginx
""").strip()
services = _parse_services(content)
assert "web" in services
def test_volumes_ends_services(self):
content = textwrap.dedent("""
services:
web:
image: nginx
volumes:
data:
""").strip()
services = _parse_services(content)
assert "web" in services
assert "data" not in services
class TestCheckTemplate:
def test_service_with_healthcheck_and_init_ok(self, tmp_path: Path):
content = textwrap.dedent("""
services:
web:
image: nginx
init: true
healthcheck:
test: ["CMD", "curl", "localhost"]
networks:
default:
""").strip()
f = tmp_path / "docker-compose.yml.j2"
f.write_text(content)
assert _check_template(f, tmp_path) == []
def test_service_with_healthcheck_no_init_flagged(self, tmp_path: Path):
content = textwrap.dedent("""
services:
web:
image: nginx
healthcheck:
test: ["CMD", "curl", "localhost"]
networks:
default:
""").strip()
f = tmp_path / "docker-compose.yml.j2"
f.write_text(content)
errors = _check_template(f, tmp_path)
assert len(errors) == 1
assert "web" in errors[0]
assert "init: true" in errors[0]
def test_service_without_healthcheck_ok(self, tmp_path: Path):
content = textwrap.dedent("""
services:
web:
image: nginx
networks:
default:
""").strip()
f = tmp_path / "docker-compose.yml.j2"
f.write_text(content)
assert _check_template(f, tmp_path) == []
def test_multiple_services_some_missing(self, tmp_path: Path):
content = textwrap.dedent("""
services:
good:
image: nginx
init: true
healthcheck:
test: ["CMD", "curl"]
bad:
image: redis
healthcheck:
test: ["CMD", "redis-cli", "ping"]
networks:
default:
""").strip()
f = tmp_path / "docker-compose.yml.j2"
f.write_text(content)
errors = _check_template(f, tmp_path)
assert len(errors) == 1
assert "bad" in errors[0]
assert "good" not in errors[0]
def test_no_services_section(self, tmp_path: Path):
content = "version: '3'\nvolumes:\n data:"
f = tmp_path / "docker-compose.yml.j2"
f.write_text(content)
assert _check_template(f, tmp_path) == []
def test_jinja2_conditional_service(self, tmp_path: Path):
content = textwrap.dedent("""
services:
{% if backup_enabled %}
backup:
image: backup
healthcheck:
test: ["CMD-SHELL", "pgrep backup"]
{% endif %}
networks:
default:
""").strip()
f = tmp_path / "docker-compose.yml.j2"
f.write_text(content)
errors = _check_template(f, tmp_path)
assert len(errors) == 1
assert "backup" in errors[0]
def test_relative_path_in_error(self, tmp_path: Path):
content = textwrap.dedent("""
services:
web:
image: nginx
healthcheck:
test: ["CMD"]
networks:
default:
""").strip()
f = tmp_path / "docker-compose.yml.j2"
f.write_text(content)
errors = _check_template(f, tmp_path)
assert len(errors) == 1
assert "docker-compose.yml.j2" in errors[0]
assert str(tmp_path) not in errors[0]
class TestMain:
def test_passes_when_all_ok(self, tmp_path: Path):
content = textwrap.dedent("""
services:
web:
image: nginx
init: true
healthcheck:
test: ["CMD"]
networks:
default:
""").strip()
f = tmp_path / "docker-compose.yml.j2"
f.write_text(content)
runner = CliRunner()
result = runner.invoke(main, ["--path", str(f)])
assert result.exit_code == 0
assert "OK" in result.output
def test_fails_when_missing_init(self, tmp_path: Path):
content = textwrap.dedent("""
services:
web:
image: nginx
healthcheck:
test: ["CMD"]
networks:
default:
""").strip()
f = tmp_path / "docker-compose.yml.j2"
f.write_text(content)
runner = CliRunner()
result = runner.invoke(main, ["--path", str(f)])
assert result.exit_code == 1
assert "FAIL" in result.output
assert "web" in result.output
def test_default_dir(self, tmp_path: Path):
(tmp_path / "docker-compose.good.yml.j2").write_text(
textwrap.dedent("""
services:
web:
image: nginx
init: true
healthcheck:
test: ["CMD"]
networks:
default:
""").strip()
)
(tmp_path / "docker-compose.bad.yml.j2").write_text(
textwrap.dedent("""
services:
db:
image: postgres
healthcheck:
test: ["CMD"]
networks:
default:
""").strip()
)
runner = CliRunner()
result = runner.invoke(main, ["--templates-dir", str(tmp_path)])
assert result.exit_code == 1
assert "db" in result.output
def test_no_templates_found(self, tmp_path: Path):
runner = CliRunner()
result = runner.invoke(main, ["--templates-dir", str(tmp_path)])
assert result.exit_code == 0
assert "OK" in result.output