import importlib.util import json import re import sys import zipfile from pathlib import Path import pytest ROOT = Path(__file__).parents[1] sys.path.insert(0, str(ROOT)) def test_versions_are_coherent_across_hub_agent_and_packages(): from central.app.version import GTI_FISCAL_VERSION v = (ROOT / "release" / "VERSION").read_text(encoding="utf-8").strip() assert v == GTI_FISCAL_VERSION == "1.3.4" pep = v.replace("-rc", "rc") for pp in ("central/pyproject.toml", "agent/pyproject.toml"): assert f'version = "{pep}"' in (ROOT / pp).read_text(encoding="utf-8") assert '"AGENT_VERSION", "1.3.4"' in (ROOT / "agent" / "scripts" / "run_agent_service.py").read_text(encoding="utf-8") manifest = ROOT / "release" / "RELEASE_MANIFEST.json" if manifest.exists(): m = json.loads(manifest.read_text(encoding="utf-8")) assert m["GTI_FISCAL_VERSION"] == v and m["migrations"]["alembic_head_in_repo"] == m["migrations"]["applied_revision"] def test_manifest_records_the_real_migration_head(): # The manifest said "0011" while the repository was already at 0018: the # generator used the 4-digit file prefix and nothing compared it to Alembic. from alembic.config import Config from alembic.script import ScriptDirectory config = Config(str(ROOT / "central" / "alembic.ini")) config.set_main_option("script_location", str(ROOT / "central" / "alembic")) script = ScriptDirectory.from_config(config) migrations = json.loads((ROOT / "release" / "RELEASE_MANIFEST.json").read_text(encoding="utf-8"))["migrations"] assert migrations["alembic_head_in_repo"] == script.get_current_head() assert migrations["count"] == len(list(script.walk_revisions())) def test_production_compose_never_exposes_postgres_or_dev_token(): import yaml raw = (ROOT / "deploy" / "hub" / "docker-compose.prod.yml").read_text(encoding="utf-8") doc = yaml.safe_load(raw) pg, hub = doc["services"]["postgres"], doc["services"]["fiscal-hub"] assert "ports" not in pg and pg["networks"] == ["internal"] and doc["networks"]["internal"]["internal"] is True assert any(v.startswith("postgres_data:") for v in pg["volumes"]) and pg["restart"] == "unless-stopped" and "healthcheck" in pg assert "AGENT_DEV_TOKEN" not in hub["environment"] and hub["environment"]["APP_ENV"] == "production" assert hub["restart"] == "unless-stopped" and "healthcheck" in hub and hub["depends_on"]["postgres"]["condition"] == "service_healthy" assert "POSTGRES_PASSWORD:?" in raw and hub["ports"][0].startswith("${HUB_BIND:-127.0.0.1}") assert set(doc["services"]) == {"postgres", "fiscal-hub"} images = " ".join(str(v.get("image", "")) for v in doc["services"].values()).lower() for banned in ("redis", "rabbitmq", "kubernetes"): assert banned not in images def test_templates_and_env_examples_contain_no_secrets(): files = list((ROOT / "deploy").rglob("*.example")) + [ROOT / "deploy" / "agent-windows" / "agent.env.template", ROOT / "deploy" / "hub" / ".env.example", ROOT / ".env.example"] for f in files: text = f.read_text(encoding="utf-8") for m in re.finditer(r"^(AGENT_TOKEN|FB_PASSWORD|POSTGRES_PASSWORD|FB_USER)=(.*)$", text, re.M): assert m.group(2).strip() == "", f"{f.name}: {m.group(1)} must be empty in a template" pilot = (ROOT / "deploy" / "agent-windows" / "pilot-construcao.env.example").read_text(encoding="utf-8") assert "COMPANY_CODE=000001" in pilot and "COMPANY_CRT_SOURCE=AUTO" in pilot and "FISCAL_MODE=DRY_RUN" in pilot and "FIREBIRD_WRITES=0" in pilot for name in ("mmmodas", "rincao", "uniao"): prof = (ROOT / "deploy" / "agent-windows" / "profiles" / f"{name}.env.example").read_text(encoding="utf-8") assert "COMPANY_CRT_SOURCE=EMISSOR" in prof and "FISCAL_MODE=DRY_RUN" in prof and "FIREBIRD_WRITES=0" in prof def test_agent_package_is_self_contained_and_secret_free(tmp_path): spec = importlib.util.spec_from_file_location("build_agent_package", ROOT / "scripts" / "build_agent_package.py") mod = importlib.util.module_from_spec(spec); sys.modules[spec.name] = mod; spec.loader.exec_module(mod) mod.ROOT = ROOT mod.main() z = zipfile.ZipFile(ROOT / "dist" / f"{mod.NAME}.zip") names = z.namelist() assert len(names) == len(set(names)) for required in ("gti-agent.ps1", "gti-agent.cmd", "requirements-agent.txt", "pilot-construcao.env.example", "agent/scripts/preflight.py", "agent/scripts/run_agent_service.py", "agent/service/gti_agent_windows_service.py", "scripts/firebird_write_executor.py", "central/app/fiscal_decision.py", "MANIFEST.json", "VERSION"): assert f"{mod.NAME}/{required}" in names, required assert not [n for n in names if n.endswith((".env", ".sqlite", ".log", ".FDB"))] assert f"{mod.NAME}/enrollment.json" not in names # never baked into the universal package — added per-download in the bundle ps = z.read(f"{mod.NAME}/gti-agent.ps1").decode("utf-8") for cmd in ("install", "install-auto", "configure", "preflight", "start", "stop", "restart", "status", "uninstall"): assert f"'{cmd}'" in ps def test_secret_scan_rejects_a_token_in_a_template(): spec = importlib.util.spec_from_file_location("bap2", ROOT / "scripts" / "build_agent_package.py") mod = importlib.util.module_from_spec(spec); spec.loader.exec_module(mod) with pytest.raises(SystemExit): mod.scan("x.env.example", b"AGENT_TOKEN=abcdefghijklmnop123\n") mod.scan("x.env.example", b"AGENT_TOKEN=\nAGENT_ID=\n")