108 lines
6.5 KiB
Python
108 lines
6.5 KiB
Python
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_and_packages():
|
|
"""GTI_FISCAL_VERSION is the Hub's own single source (release/VERSION, central/app/version.py,
|
|
central/pyproject.toml, the manifest). It is deliberately NOT required to match the Agent's own
|
|
version (see test_agent_version_is_internally_coherent below) - a Hub-only release (Missão 2/3)
|
|
must be able to bump this without touching, rebuilding or republishing the Agent at all."""
|
|
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.14"
|
|
pep = v.replace("-rc", "rc")
|
|
assert f'version = "{pep}"' in (ROOT / "central" / "pyproject.toml").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_agent_version_is_internally_coherent():
|
|
"""The Agent's own version references must agree with EACH OTHER (never guessed, never silently
|
|
drifting), but are an independent number from the Hub's GTI_FISCAL_VERSION - the Agent is only
|
|
ever re-versioned when its own code/behavior changes and a new installer is actually built."""
|
|
agent_pep = (ROOT / "agent" / "pyproject.toml").read_text(encoding="utf-8")
|
|
m = re.search(r'version = "([^"]+)"', agent_pep)
|
|
assert m, "agent/pyproject.toml must declare a version"
|
|
agent_version = m.group(1)
|
|
assert f'"AGENT_VERSION", "{agent_version}"' in (ROOT / "agent" / "scripts" / "run_agent_service.py").read_text(encoding="utf-8")
|
|
assert f'AGENT_VERSION_DEFAULT = "{agent_version}"' in (ROOT / "agent" / "src" / "gti_fiscal_agent" / "preflight.py").read_text(encoding="utf-8")
|
|
|
|
|
|
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")
|