feat(infra): auto-commit con 56 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
"""Tests para refresh_local_hub.
|
||||
|
||||
No recarga Caddy ni reinicia Glance de verdad: mockea `subprocess.run`. Escribe las
|
||||
configs generadas en un `tmp_path` parcheando las rutas de salida. Mockea
|
||||
`discover_local_services` para devolver 2 servicios fijos (no depende de puertos reales).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
import yaml
|
||||
import pytest
|
||||
|
||||
from pipelines import refresh_local_hub as rlh_module
|
||||
from pipelines.refresh_local_hub import refresh_local_hub
|
||||
|
||||
|
||||
FAKE_SERVICES = [
|
||||
{
|
||||
"name": "metabase",
|
||||
"subdomain": "metabase",
|
||||
"port": 3030,
|
||||
"health_path": "/api/health",
|
||||
"title": "Metabase",
|
||||
"icon": "si:metabase",
|
||||
"category": "Datos",
|
||||
"up": True,
|
||||
},
|
||||
{
|
||||
"name": "portainer",
|
||||
"subdomain": "portainer",
|
||||
"port": 9000,
|
||||
"health_path": "/",
|
||||
"title": "Portainer",
|
||||
"icon": "si:portainer",
|
||||
"category": "Infra",
|
||||
"up": False,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _write_manifest(tmp_path) -> str:
|
||||
"""Crea un manifiesto YAML mínimo y devuelve su ruta."""
|
||||
manifest = {
|
||||
"dashboard_subdomain": "home",
|
||||
"glance_port": 8585,
|
||||
"services": [],
|
||||
}
|
||||
path = os.path.join(str(tmp_path), "local_services.yaml")
|
||||
with open(path, "w", encoding="utf-8") as fh:
|
||||
yaml.safe_dump(manifest, fh)
|
||||
return path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def patched_env(tmp_path, monkeypatch):
|
||||
"""Parchea discover_local_services, las rutas de salida y subprocess.run."""
|
||||
# discover_local_services devuelve 2 servicios fijos.
|
||||
monkeypatch.setattr(rlh_module, "discover_local_services", lambda *a, **k: list(FAKE_SERVICES))
|
||||
|
||||
# Rutas de salida hacia tmp_path.
|
||||
caddy_path = os.path.join(str(tmp_path), "local_hub.caddy")
|
||||
monkeypatch.setattr(rlh_module, "CADDY_FRAGMENT_PATH", caddy_path)
|
||||
monkeypatch.setattr(rlh_module, "_registry_root", lambda: str(tmp_path))
|
||||
|
||||
# subprocess.run mockeado: registra las llamadas y devuelve un objeto con returncode 0.
|
||||
calls: list[list[str]] = []
|
||||
|
||||
class _FakeProc:
|
||||
def __init__(self, rc: int = 0) -> None:
|
||||
self.returncode = rc
|
||||
self.stdout = ""
|
||||
self.stderr = ""
|
||||
|
||||
def _fake_run(cmd, *args, **kwargs):
|
||||
calls.append(list(cmd))
|
||||
return _FakeProc(0)
|
||||
|
||||
monkeypatch.setattr(rlh_module.subprocess, "run", _fake_run)
|
||||
|
||||
manifest_path = _write_manifest(tmp_path)
|
||||
return {
|
||||
"manifest_path": manifest_path,
|
||||
"caddy_path": caddy_path,
|
||||
"glance_path": os.path.join(str(tmp_path), "apps", "local_hub", "glance", "glance.yml"),
|
||||
"calls": calls,
|
||||
"tmp_path": str(tmp_path),
|
||||
}
|
||||
|
||||
|
||||
def test_compone_las_tres_funciones_y_escribe_configs(patched_env):
|
||||
"""compone las tres funciones del registry y escribe ambas configs"""
|
||||
result = refresh_local_hub(manifest_path=patched_env["manifest_path"], reload=False)
|
||||
|
||||
# Caddyfile escrito con un bloque por servicio + dashboard.
|
||||
assert os.path.exists(patched_env["caddy_path"])
|
||||
caddy_text = open(patched_env["caddy_path"], encoding="utf-8").read()
|
||||
assert "metabase.localhost" in caddy_text
|
||||
assert "portainer.localhost" in caddy_text
|
||||
assert "home.localhost" in caddy_text # bloque del dashboard
|
||||
assert "reverse_proxy 127.0.0.1:3030" in caddy_text
|
||||
|
||||
# Glance escrito con el bloque servidor fijo + la salida de render_glance_config.
|
||||
assert os.path.exists(result["glance_path"])
|
||||
glance_text = open(result["glance_path"], encoding="utf-8").read()
|
||||
assert "host: 127.0.0.1" in glance_text
|
||||
assert "port: 8585" in glance_text
|
||||
assert "primary-color: 210 90 70" in glance_text
|
||||
assert "type: monitor" in glance_text
|
||||
|
||||
|
||||
def test_glance_full_es_yaml_parseable(patched_env):
|
||||
"""la config completa de Glance generada es YAML parseable"""
|
||||
result = refresh_local_hub(manifest_path=patched_env["manifest_path"], reload=False)
|
||||
|
||||
glance_text = open(result["glance_path"], encoding="utf-8").read()
|
||||
parsed = yaml.safe_load(glance_text)
|
||||
assert isinstance(parsed, dict)
|
||||
assert parsed["server"]["host"] == "127.0.0.1"
|
||||
assert parsed["server"]["port"] == 8585
|
||||
assert "theme" in parsed
|
||||
assert "pages" in parsed # la salida de render_glance_config
|
||||
|
||||
|
||||
def test_reload_false_no_llama_subprocess(patched_env):
|
||||
"""con reload=False no se llama a subprocess.run"""
|
||||
result = refresh_local_hub(manifest_path=patched_env["manifest_path"], reload=False)
|
||||
|
||||
assert patched_env["calls"] == []
|
||||
assert result["reloaded"] is False
|
||||
assert result["caddy_reload_rc"] is None
|
||||
assert result["glance_restart_rc"] is None
|
||||
|
||||
|
||||
def test_reload_true_recarga_caddy_y_reinicia_glance(patched_env):
|
||||
"""con reload=True se invoca caddy reload y systemctl --user restart glance"""
|
||||
result = refresh_local_hub(manifest_path=patched_env["manifest_path"], reload=True)
|
||||
|
||||
cmds = patched_env["calls"]
|
||||
assert len(cmds) == 2
|
||||
assert cmds[0][0] == "caddy" and "reload" in cmds[0]
|
||||
assert cmds[1] == ["systemctl", "--user", "restart", "glance"]
|
||||
assert result["reloaded"] is True
|
||||
assert result["caddy_reload_rc"] == 0
|
||||
assert result["glance_restart_rc"] == 0
|
||||
|
||||
|
||||
def test_dict_retorno_tiene_claves_esperadas(patched_env):
|
||||
"""el dict de retorno tiene todas las claves del contrato"""
|
||||
result = refresh_local_hub(manifest_path=patched_env["manifest_path"], reload=False)
|
||||
|
||||
expected_keys = {
|
||||
"total", "up", "down", "caddy_path", "glance_path",
|
||||
"reloaded", "caddy_reload_rc", "glance_restart_rc", "services",
|
||||
}
|
||||
assert expected_keys <= set(result.keys())
|
||||
assert result["total"] == 2
|
||||
assert result["up"] == 1
|
||||
assert result["down"] == 1
|
||||
assert len(result["services"]) == 2
|
||||
assert result["services"][0]["subdomain"] == "metabase"
|
||||
assert {"name", "subdomain", "port", "up"} <= set(result["services"][0].keys())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
Reference in New Issue
Block a user