103 lines
3.8 KiB
Python
103 lines
3.8 KiB
Python
"""Local E2E hub for Phase D — Windows installer against a real HTTP Hub.
|
|
|
|
Boots the control plane against a scratch SQLite DB, seeds a tenant + admin,
|
|
publishes the real built EXE as release 1.1.1, creates a PENDING enrollment,
|
|
generates an install code, then serves uvicorn on 127.0.0.1:PORT.
|
|
|
|
The operator/Setup CLI then runs against this local Hub. Usage:
|
|
|
|
python scripts/e2e_local_hub.py --port 8901 --exe dist/installer/GTIFiscalAgent-Setup-1.1.1.exe
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import dataclasses
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import uuid
|
|
from datetime import datetime, timedelta, timezone
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(ROOT))
|
|
sys.path.insert(0, str(ROOT / "central"))
|
|
|
|
from central.app import config, enrollment # noqa: E402
|
|
from central.app.models import AgentEnrollment, Tenant # noqa: E402
|
|
from central.scripts.publish_agent_release import publish # noqa: E402
|
|
from central.tests.test_control_plane import create_admin, seed # noqa: E402
|
|
from sqlalchemy import select
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--port", type=int, default=8765)
|
|
parser.add_argument("--exe", default=str(ROOT / "dist/installer/GTIFiscalAgent-Setup-1.1.1.exe"))
|
|
args = parser.parse_args()
|
|
|
|
workdir = Path(tempfile.mkdtemp(prefix="gti_e2e_hub_"))
|
|
print(f"[e2e] workdir={workdir}")
|
|
|
|
replaced = dataclasses.replace(
|
|
config.settings,
|
|
agent_release_storage_path=str(workdir / "releases"),
|
|
base_url=f"http://127.0.0.1:{args.port}",
|
|
web_cookie_secure=False,
|
|
)
|
|
config.settings = replaced
|
|
if "app.config" in sys.modules:
|
|
sys.modules["app.config"].settings = replaced
|
|
|
|
url = f"sqlite:///{workdir / 'e2e_hub.db'}"
|
|
os.environ["DATABASE_URL"] = url
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker
|
|
from sqlalchemy.pool import StaticPool
|
|
|
|
from central.app.models import Base
|
|
|
|
engine = create_engine(url, connect_args={"check_same_thread": False}, poolclass=StaticPool)
|
|
Base.metadata.create_all(engine)
|
|
factory = sessionmaker(bind=engine, expire_on_commit=False)
|
|
|
|
seed(factory)
|
|
|
|
with factory() as session:
|
|
data = Path(args.exe)
|
|
if not data.exists():
|
|
print(f"[e2e] EXE artefact not found: {data}", file=sys.stderr)
|
|
return 2
|
|
release, _created = publish(session, data, version="1.1.1")
|
|
release_id = release.id
|
|
print(f"[e2e] published release {release.version} id={release_id}")
|
|
|
|
tenant = session.scalars(select(Tenant).limit(1)).first()
|
|
enr = AgentEnrollment(enrollment_uuid=str(uuid.uuid4()), tenant_id=tenant.id,
|
|
display_name="Servidor E2E LOCAL", release_id=release_id, status="PENDING")
|
|
session.add(enr)
|
|
session.flush()
|
|
|
|
code = enrollment.generate_install_code()
|
|
enr.install_code_hash = enrollment.install_code_digest(code)
|
|
enr.install_code_expires_at = datetime.now(timezone.utc) + timedelta(seconds=86400)
|
|
enr.install_code_created_at = datetime.now(timezone.utc)
|
|
session.commit()
|
|
print(f"[e2e] enrollment_uuid={enr.enrollment_uuid}", flush=True)
|
|
print(f"[e2e] INSTALL_CODE={code}", flush=True)
|
|
(workdir / "install_code.txt").write_text(code)
|
|
(workdir / "enrollment_uuid.txt").write_text(enr.enrollment_uuid)
|
|
print(f"[e2e] workdir={workdir}", flush=True)
|
|
|
|
import uvicorn
|
|
|
|
from central.app.main import create_app
|
|
|
|
app = create_app(database_probe=lambda: True, session_factory=factory, bootstrap_token=None)
|
|
print(f"[e2e] serving hub on http://127.0.0.1:{args.port}")
|
|
uvicorn.run(app, host="127.0.0.1", port=args.port, log_level="info")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main()) |