188 lines
8.1 KiB
Python
188 lines
8.1 KiB
Python
"""Pruebas de bootstrap y carga de .env."""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from app.config.bootstrap import ensure_runtime_layout
|
|
from app.config.env_loader import (
|
|
apply_env_overrides,
|
|
get_launch_options,
|
|
is_panel_configured,
|
|
render_env_template,
|
|
)
|
|
from app.constants import CONFIG_DIR, DEFAULT_CONFIG, DIR_ENTRADA, ENV_PATH
|
|
|
|
|
|
def test_render_env_template_contains_app_dir(tmp_path: Path):
|
|
text = render_env_template(tmp_path)
|
|
assert str(tmp_path) in text
|
|
assert "CLOUDRESTORE_PANEL_API_URL" in text
|
|
|
|
|
|
def test_ensure_runtime_layout_creates_structure(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
|
|
monkeypatch.setattr("app.config.bootstrap.APP_DIR", tmp_path)
|
|
monkeypatch.setattr("app.config.bootstrap.CONFIG_DIR", tmp_path / "config")
|
|
monkeypatch.setattr("app.config.bootstrap.DATA_DIR", tmp_path / "config" / "data")
|
|
monkeypatch.setattr("app.config.bootstrap.LOGS_DIR", tmp_path / "config" / "logs")
|
|
monkeypatch.setattr("app.config.bootstrap.ODBC_DIR", tmp_path / "config" / "odbc")
|
|
monkeypatch.setattr("app.config.bootstrap.SEVEN_ZIP_DIR", tmp_path / "config" / "7zip")
|
|
monkeypatch.setattr("app.config.bootstrap.ENV_PATH", tmp_path / "config" / ".env")
|
|
monkeypatch.setattr("app.config.bootstrap.DIR_ENTRADA", tmp_path / "Entrada")
|
|
monkeypatch.setattr("app.config.bootstrap.DIR_PROCESADOS", tmp_path / "Procesados")
|
|
monkeypatch.setattr("app.config.bootstrap.DIR_FALLADOS", tmp_path / "Fallados")
|
|
monkeypatch.setattr("app.config.bootstrap.DIR_TEMP", tmp_path / "Temp")
|
|
monkeypatch.setattr("app.constants.DB_PATH", tmp_path / "config" / "data" / "app.db")
|
|
|
|
ensure_runtime_layout()
|
|
|
|
assert (tmp_path / "config" / ".env").is_file()
|
|
assert (tmp_path / "Entrada").is_dir()
|
|
assert (tmp_path / "config" / "data" / "app.db").is_file()
|
|
|
|
|
|
def test_is_panel_configured(monkeypatch: pytest.MonkeyPatch):
|
|
monkeypatch.delenv("CLOUDRESTORE_PANEL_API_URL", raising=False)
|
|
monkeypatch.delenv("CLOUDRESTORE_PANEL_API_TOKEN", raising=False)
|
|
monkeypatch.delenv("CLOUDRESTORE_PANEL_INSTANCE_KEY", raising=False)
|
|
assert not is_panel_configured()
|
|
|
|
monkeypatch.setenv("CLOUDRESTORE_PANEL_API_URL", "https://panel:3000")
|
|
monkeypatch.setenv("CLOUDRESTORE_PANEL_API_TOKEN", "secret")
|
|
monkeypatch.setenv("CLOUDRESTORE_PANEL_INSTANCE_KEY", "srv1")
|
|
assert is_panel_configured()
|
|
|
|
|
|
def test_get_launch_options_auto_start_does_not_minimize(monkeypatch: pytest.MonkeyPatch):
|
|
monkeypatch.setenv("CLOUDRESTORE_AUTO_START", "true")
|
|
monkeypatch.delenv("CLOUDRESTORE_START_MINIMIZED", raising=False)
|
|
opts = get_launch_options()
|
|
assert opts.start_engine is True
|
|
assert opts.minimized is False
|
|
|
|
|
|
def test_get_launch_options_start_minimized(monkeypatch: pytest.MonkeyPatch):
|
|
monkeypatch.setenv("CLOUDRESTORE_START_MINIMIZED", "true")
|
|
opts = get_launch_options()
|
|
assert opts.minimized is True
|
|
|
|
|
|
def test_render_env_template_documents_start_minimized(tmp_path: Path):
|
|
text = render_env_template(tmp_path)
|
|
assert "CLOUDRESTORE_START_MINIMIZED=false" in text
|
|
|
|
|
|
def test_apply_env_overrides_paths(monkeypatch: pytest.MonkeyPatch):
|
|
monkeypatch.setenv("CLOUDRESTORE_INPUT_FOLDER", "/tmp/in")
|
|
cfg = apply_env_overrides(DEFAULT_CONFIG.copy())
|
|
assert cfg["paths"]["input_folder"] == "/tmp/in"
|
|
|
|
|
|
def _redirect_bootstrap(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
|
|
"""Reapunta los globales de bootstrap a un árbol temporal. Devuelve config/."""
|
|
for name, rel in (
|
|
("APP_DIR", ""),
|
|
("CONFIG_DIR", "config"),
|
|
("DATA_DIR", "config/data"),
|
|
("LOGS_DIR", "config/logs"),
|
|
("ODBC_DIR", "config/odbc"),
|
|
("SEVEN_ZIP_DIR", "config/7zip"),
|
|
("ENV_PATH", "config/.env"),
|
|
("DIR_ENTRADA", "Entrada"),
|
|
("DIR_PROCESADOS", "Procesados"),
|
|
("DIR_FALLADOS", "Fallados"),
|
|
("DIR_TEMP", "Temp"),
|
|
):
|
|
target = tmp_path / rel if rel else tmp_path
|
|
monkeypatch.setattr(f"app.config.bootstrap.{name}", target)
|
|
monkeypatch.setattr("app.constants.DB_PATH", tmp_path / "config" / "data" / "app.db")
|
|
return tmp_path / "config"
|
|
|
|
|
|
def test_bootstrap_escribe_sello_de_version(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
|
|
"""
|
|
El instalador remoto del PANEL lee config/.version por SFTP para verificar el
|
|
despliegue: en Windows el .exe se compila con console=False y no hay stdout confiable.
|
|
"""
|
|
from app import __version__
|
|
|
|
config_dir = _redirect_bootstrap(tmp_path, monkeypatch)
|
|
ensure_runtime_layout()
|
|
assert (config_dir / ".version").read_text(encoding="utf-8").strip() == __version__
|
|
|
|
|
|
def test_bootstrap_redespliega_deps_cuando_cambia_el_manifiesto(
|
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
):
|
|
"""
|
|
config/7zip y config/odbc se re-copian cuando el build trae otras versiones embebidas.
|
|
Antes solo se copiaban si la carpeta estaba vacía, así que una actualización con driver
|
|
ODBC nuevo conservaba el viejo indefinidamente.
|
|
"""
|
|
config_dir = _redirect_bootstrap(tmp_path, monkeypatch)
|
|
|
|
src_7zip = tmp_path / "bundle" / "7zip"
|
|
src_odbc = tmp_path / "bundle" / "odbc"
|
|
src_7zip.mkdir(parents=True)
|
|
src_odbc.mkdir(parents=True)
|
|
(src_7zip / "7zz").write_text("7zz v26", encoding="utf-8")
|
|
(src_odbc / "odbcinst.ini").write_text("odbc 18.5", encoding="utf-8")
|
|
monkeypatch.setattr("app.config.bootstrap.BUNDLED_SOURCE_7ZIP", src_7zip)
|
|
monkeypatch.setattr("app.config.bootstrap.BUNDLED_SOURCE_ODBC", src_odbc)
|
|
|
|
# bundled-versions.json se busca bajo BUNDLE_DIR/packaging (va embebido en el onefile).
|
|
bundle_root = tmp_path / "bundle_root"
|
|
(bundle_root / "packaging").mkdir(parents=True)
|
|
manifest = bundle_root / "packaging" / "bundled-versions.json"
|
|
manifest.write_text('{"seven_zip": "26.01"}', encoding="utf-8")
|
|
monkeypatch.setattr("app.config.bootstrap.BUNDLE_DIR", bundle_root)
|
|
|
|
ensure_runtime_layout()
|
|
assert (config_dir / "7zip" / "7zz").read_text(encoding="utf-8") == "7zz v26"
|
|
stamp_before = (config_dir / ".bundled_deps").read_text(encoding="utf-8").strip()
|
|
assert stamp_before.startswith("sha256:")
|
|
|
|
# Mismo manifiesto: no debe re-copiar (no pisa ajustes locales en cada arranque).
|
|
(config_dir / "7zip" / "7zz").write_text("editado a mano", encoding="utf-8")
|
|
ensure_runtime_layout()
|
|
assert (config_dir / "7zip" / "7zz").read_text(encoding="utf-8") == "editado a mano"
|
|
|
|
# Manifiesto distinto (build con deps nuevas): debe re-copiar.
|
|
(src_7zip / "7zz").write_text("7zz v27", encoding="utf-8")
|
|
(src_odbc / "odbcinst.ini").write_text("odbc 19.0", encoding="utf-8")
|
|
manifest.write_text('{"seven_zip": "27.00"}', encoding="utf-8")
|
|
ensure_runtime_layout()
|
|
assert (config_dir / "7zip" / "7zz").read_text(encoding="utf-8") == "7zz v27"
|
|
assert (config_dir / "odbc" / "odbcinst.ini").read_text(encoding="utf-8") == "odbc 19.0"
|
|
assert (config_dir / ".bundled_deps").read_text(encoding="utf-8").strip() != stamp_before
|
|
|
|
# El re-despliegue SOBRESCRIBE, así que lo que el operador hubiera editado a mano debe
|
|
# quedar respaldado: no es reconstruible.
|
|
assert (config_dir / "7zip.bak" / "7zz").read_text(encoding="utf-8") == "editado a mano"
|
|
assert (config_dir / "odbc.bak" / "odbcinst.ini").read_text(encoding="utf-8") == "odbc 18.5"
|
|
|
|
|
|
def test_bootstrap_no_respalda_cuando_no_hay_refresco(
|
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
):
|
|
"""Sin cambio de manifiesto no se re-copia, así que tampoco debe crearse el .bak."""
|
|
config_dir = _redirect_bootstrap(tmp_path, monkeypatch)
|
|
|
|
src_7zip = tmp_path / "bundle" / "7zip"
|
|
src_7zip.mkdir(parents=True)
|
|
(src_7zip / "7zz").write_text("7zz v26", encoding="utf-8")
|
|
monkeypatch.setattr("app.config.bootstrap.BUNDLED_SOURCE_7ZIP", src_7zip)
|
|
|
|
bundle_root = tmp_path / "bundle_root"
|
|
(bundle_root / "packaging").mkdir(parents=True)
|
|
(bundle_root / "packaging" / "bundled-versions.json").write_text(
|
|
'{"seven_zip": "26.01"}', encoding="utf-8"
|
|
)
|
|
monkeypatch.setattr("app.config.bootstrap.BUNDLE_DIR", bundle_root)
|
|
|
|
ensure_runtime_layout()
|
|
ensure_runtime_layout() # segunda vez: mismo manifiesto, no debe refrescar
|
|
|
|
assert not (config_dir / "7zip.bak").exists()
|