89 lines
4.5 KiB
Python
89 lines
4.5 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()
|
|
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 / "cli")
|
|
server = str(binary_dir / "server")
|
|
with tempfile.TemporaryDirectory(prefix="aster-smoke-") as directory:
|
|
env["ASTER_STATE_DIR"] = str(Path(directory) / "host-info")
|
|
env["SQLITE_PATH"] = str(Path(directory) / "nested" / "aster.db")
|
|
# Neither executable may use database settings for basic VM information.
|
|
env["DATABASE_URL"] = "invalid-database-url"
|
|
bootstrap = run(cli, "bootstrap", env=env)
|
|
assert "runtime=EndorObject type_system=disabled" in bootstrap, bootstrap
|
|
assert "objects_distinct=true heap_objects=2" in bootstrap, bootstrap
|
|
assert "released heap_objects=0" in bootstrap, bootstrap
|
|
assert "stored (file)" in run(cli, "metadata", "put", "project.note", "日本語・中文・😀", env=env)
|
|
assert run(cli, "metadata", "get", "project.note", env=env) == "日本語・中文・😀"
|
|
legacy = json.dumps([{"key": "project.note", "value": "old"},
|
|
{"key": "legacy.note", "value": "preserved"}])
|
|
subprocess.run([cli, "metadata", "import"], input=legacy, cwd=ROOT, env=env,
|
|
check=True, text=True, capture_output=True)
|
|
assert run(cli, "metadata", "get", "project.note", env=env) == "日本語・中文・😀"
|
|
assert run(cli, "metadata", "get", "legacy.note", env=env) == "preserved"
|
|
assert not Path(env["SQLITE_PATH"]).exists(), "CLI must not open a database"
|
|
|
|
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"}, 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 == {"object_model": "EndorObject", "type_system": False, "heap_objects": 0}, 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 not Path(env["SQLITE_PATH"]).exists(), "Server must not open a database"
|
|
assert run(cli, "metadata", "get", "host.version", env=env) == "0.1.0"
|
|
record = json.loads((Path(env["ASTER_STATE_DIR"]) / "host.version.json").read_text(encoding="utf-8"))
|
|
assert record == {"format_version": 1, "key": "host.version", "value": "0.1.0"}
|
|
assert run(cli, "metadata", "get", "project.note", env=env) == "日本語・中文・😀"
|
|
print("PASS: no implicit database access; file metadata persistence; HTTP health/bootstrap; SIGTERM shutdown")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|