cuivienen's picture
Deploy Synderesis API Docker Space
10ff0f9 verified
Raw
History Blame Contribute Delete
7.27 kB
#!/usr/bin/env python3
"""Launch smoke checks for Customer Agent static surfaces and optional live API."""
from __future__ import annotations
import argparse
import json
import sys
import urllib.error
import urllib.request
from pathlib import Path
def check_local_files(root: Path) -> list[str]:
required = [
root / "embed" / "agent.js",
root / "embed" / "agent.css",
root / "admin" / "index.html",
root / "test" / "index.html",
root / "docs" / "customer-setup-guide.md",
root / "docs" / "SHIPPING.md",
root / "docs" / "LAUNCH.md",
root / "VERSION",
root / "integrations" / "wordpress" / "synderesis-customer-agent" / "synderesis-customer-agent.php",
root / "integrations" / "wordpress" / "synderesis-customer-agent" / "uninstall.php",
root / "evals" / "golden_cases.json",
]
missing = [str(path.relative_to(root)) for path in required if not path.is_file()]
return missing
def http_get(url: str, timeout: float = 10.0) -> tuple[int, str]:
req = urllib.request.Request(url, headers={"User-Agent": "SynderesisSmoke/1.5"})
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
body = resp.read(200_000).decode("utf-8", errors="replace")
return int(resp.status), body
except urllib.error.HTTPError as exc:
body = exc.read(20_000).decode("utf-8", errors="replace")
return int(exc.code), body
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--product-root",
default=str(Path(__file__).resolve().parents[1]),
help="Path to customer-agent/",
)
parser.add_argument(
"--base-url",
default="",
help="Optional live base URL, e.g. http://127.0.0.1:8080 or https://www.synderesis.eu",
)
parser.add_argument(
"--site-key",
default="",
help="Optional pk_live_ key for public config smoke",
)
args = parser.parse_args(argv)
root = Path(args.product_root).resolve()
errors: list[str] = []
print(f"Product root: {root}")
missing = check_local_files(root)
if missing:
for item in missing:
errors.append(f"missing file: {item}")
print(f"FAIL missing {item}")
else:
print("PASS local package files present")
version = (root / "VERSION").read_text(encoding="utf-8").strip()
print(f"VERSION {version}")
# Offline golden evals
eval_script = root / "scripts" / "run_evals.py"
if eval_script.is_file():
import subprocess
proc = subprocess.run(
[sys.executable, str(eval_script)],
cwd=str(root.parents[0]),
capture_output=True,
text=True,
)
if proc.returncode == 0:
print("PASS golden evals")
else:
errors.append("golden evals failed")
print(proc.stdout)
print(proc.stderr)
print("FAIL golden evals")
else:
errors.append("run_evals.py missing")
base = args.base_url.rstrip("/")
static_server = None
static_thread = None
if not base:
# Self-contained static smoke: serve customer-agent/ so HTTP paths can be checked
# without a full Synderesis API process.
import http.server
import socketserver
import threading
class _Handler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *a, **k):
super().__init__(*a, directory=str(root), **k)
def log_message(self, format: str, *args: object) -> None: # noqa: A003
return
# Bind ephemeral port on localhost
static_server = socketserver.TCPServer(("127.0.0.1", 0), _Handler)
port = static_server.server_address[1]
static_thread = threading.Thread(target=static_server.serve_forever, daemon=True)
static_thread.start()
base = f"http://127.0.0.1:{port}"
print(f"Started local static smoke server at {base}")
static_paths = [
"/embed/agent.js",
"/embed/agent.css",
"/admin/index.html",
"/test/index.html",
"/docs/customer-setup-guide.md",
"/docs/SHIPPING.md",
"/docs/LAUNCH.md",
"/VERSION",
]
for path in static_paths:
url = base + path
try:
status, body = http_get(url)
except Exception as exc: # noqa: BLE001
errors.append(f"static {path}: {exc}")
print(f"FAIL static {path} ({exc})")
continue
if status != 200:
errors.append(f"static {path}: HTTP {status}")
print(f"FAIL static {path} HTTP {status}")
elif path.endswith("agent.js") and "site-key" not in body and "siteKey" not in body:
errors.append(f"static {path}: unexpected body")
print(f"FAIL static {path} unexpected body")
else:
print(f"PASS static {path} HTTP {status}")
static_server.shutdown()
print("Stopped local static smoke server")
else:
paths = [
"/health",
"/embed/agent.js",
"/embed/agent.css",
"/customer-agent/admin/",
"/customer-agent/test/",
"/customer-agent/docs/customer-setup-guide.md",
"/customer-agent/docs/SHIPPING.md",
"/customer-agent/docs/LAUNCH.md",
]
for path in paths:
url = base + path
try:
status, body = http_get(url)
except Exception as exc: # noqa: BLE001
errors.append(f"{path}: {exc}")
print(f"FAIL {path} ({exc})")
continue
if status != 200:
errors.append(f"{path}: HTTP {status}")
print(f"FAIL {path} HTTP {status}")
elif path.endswith("agent.js") and "SYNDERESIS" not in body.upper() and "site-key" not in body:
errors.append(f"{path}: unexpected body")
print(f"FAIL {path} unexpected body")
else:
print(f"PASS {path} HTTP {status}")
if args.site_key:
cfg_url = f"{base}/v1/public/agents/{args.site_key}/config"
try:
status, body = http_get(cfg_url)
data = json.loads(body) if body else {}
except Exception as exc: # noqa: BLE001
errors.append(f"public config: {exc}")
print(f"FAIL public config ({exc})")
else:
if status == 200 and data.get("name"):
print(f"PASS public config agent={data.get('name')!r}")
else:
errors.append(f"public config HTTP {status}")
print(f"FAIL public config HTTP {status}")
if errors:
print(f"\nSMOKE FAILED ({len(errors)} issue(s))")
for item in errors:
print(f" - {item}")
return 1
print("\nSMOKE PASSED")
return 0
if __name__ == "__main__":
raise SystemExit(main())