Files
endorphin-aster-vm/scripts/smoke.py
T
codex-bot d394f07fc2
Verify databases and deploy Aster VM / verify-and-deploy (push) Successful in 14m16s
Unify heap objects with typed headers and bootstrap Type
2026-10-18 13:38:00 +08:00

80 lines
3.7 KiB
Python

#!/usr/bin/env python3
"""Remote CI end-to-end smoke test, no external database required."""
import json
import os
from pathlib import Path
import signal
import socket
import subprocess
import tempfile
import time
import urllib.request
ROOT = Path(__file__).resolve().parents[1]
def run(*args, env):
return subprocess.run(args, cwd=ROOT, env=env, check=True, text=True, capture_output=True).stdout.strip()
def main():
env = os.environ.copy()
env.pop("DATABASE_URL", None)
run("cargo", "build", "--workspace", "--locked", env=env)
# Cargo may use a configured CARGO_TARGET_DIR; discover the actual binaries.
metadata = json.loads(run("cargo", "metadata", "--no-deps", "--format-version", "1", env=env))
binary_dir = Path(metadata["target_directory"]) / "debug"
cli = str(binary_dir / "aster")
server = str(binary_dir / "aster-server")
with tempfile.TemporaryDirectory(prefix="aster-smoke-") as directory:
env["SQLITE_PATH"] = str(Path(directory) / "nested" / "aster.db")
# This must succeed even when an explicit database URL is invalid.
isolated_env = {**env, "DATABASE_URL": "invalid-database-url"}
bootstrap = run(cli, "bootstrap", env=isolated_env)
assert "root=Object parent=None" in bootstrap, bootstrap
assert "instances_distinct=true heap_objects=5" in bootstrap, bootstrap
assert not Path(env["SQLITE_PATH"]).exists(), "bootstrap must not open storage"
assert "sqlite" in run(cli, "metadata", "put", "smoke", "持久化成功", env=env)
assert run(cli, "metadata", "get", "smoke", env=env) == "持久化成功"
failed = subprocess.run([cli, "metadata", "get", "smoke"], cwd=ROOT, env=isolated_env, capture_output=True)
assert failed.returncode != 0, "invalid explicit DB config must fail"
with socket.socket() as sock:
sock.bind(("127.0.0.1", 0))
port = sock.getsockname()[1]
env["ASTER_LISTEN"] = f"127.0.0.1:{port}"
with tempfile.TemporaryFile(mode="w+") as log:
process = subprocess.Popen([server], cwd=ROOT, env=env, stdout=log, stderr=log)
try:
deadline = time.monotonic() + 15
while True:
try:
with urllib.request.urlopen(f"http://127.0.0.1:{port}/health", timeout=1) as response:
health = json.load(response)
break
except (OSError, ValueError):
if process.poll() is not None or time.monotonic() >= deadline:
log.seek(0)
raise AssertionError(f"server did not become healthy: {log.read()}")
time.sleep(0.1)
assert health == {"status": "ok", "storage": "sqlite"}, health
for _ in range(2):
with urllib.request.urlopen(f"http://127.0.0.1:{port}/bootstrap", timeout=2) as response:
info = json.load(response)
assert info == {"root_class": "Object", "root_has_parent": False, "heap_objects": 2}, info
finally:
process.send_signal(signal.SIGTERM)
try:
process.wait(timeout=10)
except subprocess.TimeoutExpired:
process.kill()
process.wait()
raise AssertionError("server did not shut down gracefully")
assert process.returncode == 0, process.returncode
assert run(cli, "metadata", "get", "host.version", env=env) == "0.1.0"
print("PASS: bootstrap isolation; SQLite persistence; config failure; HTTP health/bootstrap; SIGTERM shutdown")
if __name__ == "__main__":
main()