Added chatterbox connections

This commit is contained in:
Jeff Luke committed 2026-10-18 10:54:38 -07:00
1 parent 41ef12f954
commit e58ceeb192
58 files changed
+8931 -8

No files matched your search

+104
View File
@@ -0,0 +1,104 @@
"""Real Godot / Rust / NATS smoke test; no physical hardware. Stop other labs first."""
import os
from pathlib import Path
import shutil
import socket
import subprocess
import tempfile
import time
import urllib.error
import lab
ROOT = Path(__file__).resolve().parent
PROJECT = ROOT.parents[1]
def main():
for port in (*lab.PORTS, 18768):
with socket.socket() as sock:
sock.bind(("127.0.0.1", port))
nats = shutil.which("nats-server")
godot = os.environ.get("GLITCH_LAB_GODOT") or str(PROJECT / "Godot_v4.7.2-stable_win64_console.exe")
binary = ROOT / "target/debug" / ("glitch-odin-lab.exe" if os.name == "nt" else "glitch-odin-lab")
if not nats or not Path(godot).is_file() or not binary.is_file():
raise RuntimeError("Need nats-server, built Rust lab, and Godot (or GLITCH_LAB_GODOT)")
children = []
with tempfile.TemporaryFile(mode="w+b") as log:
def spawn(args):
p = subprocess.Popen(args, cwd=ROOT, stdout=log, stderr=log,
creationflags=subprocess.CREATE_NO_WINDOW if os.name == "nt" else 0)
children.append(p)
return p
def launch_game(inject):
return spawn([godot, "--headless", "--path", str(PROJECT),
"res://tests/odin_lab/cabinet.tscn", "--", "--glitch-lab",
*(["--lab-inject-rfid"] if inject else [])])
def state():
deadline = time.monotonic() + 20
while time.monotonic() < deadline:
try:
return lab.api("/state")["state"]
except (OSError, ValueError):
time.sleep(.1)
raise RuntimeError("No cabinet state after startup")
try:
spawn([nats, "-c", str(ROOT / "nats.conf")])
deadline = time.monotonic() + 10
while True:
try:
with socket.create_connection(("127.0.0.1", 14222), timeout=.2):
break
except OSError:
if time.monotonic() > deadline: raise
time.sleep(.1)
game = launch_game(True)
spawn([str(binary), "godot"])
spawn([str(binary), "controller"])
initial = state()
assert initial["simulated"] is False and initial["phase"] == "powered_off"
session = initial["session"]
def send(action):
return lab.api("/command", lab.command(action, session))
assert not send("start")["accepted"]
assert send("power_on")["state"]["phase"] == "booting"
assert not send("scan")["accepted"]
deadline = time.monotonic() + 8
while state()["phase"] != "ready":
assert time.monotonic() < deadline, "Boot did not finish"
time.sleep(.1)
assert not send("start")["accepted"]
scan = lab.command("scan", session)
assert lab.api("/command", scan)["state"]["credits"] == 1
replay = lab.api("/command", scan)
assert replay["duplicate"] and replay["state"]["scans"] == 1
assert send("start")["state"]["phase"] == "playing"
assert not send("win")["accepted"], "Remote synthetic win must be disabled"
assert send("reset")["state"]["phase"] == "powered_off"
game.terminate()
game.wait(timeout=5)
try:
lab.api("/state")
raise AssertionError("Offline cabinet falsely returned state")
except urllib.error.HTTPError as error:
assert error.code == 503
launch_game(False)
restarted = state()
assert restarted["session"] != session and not restarted["rfid_injection"]
assert not send("power_on")["accepted"], "Old session accepted"
session = restarted["session"]
assert not send("scan")["accepted"]
print("PASS: HTTP -> Rust -> NATS -> sidecar -> real Godot, boot/credit/start/reset, offline and restart")
finally:
for child in reversed(children):
if child.poll() is None:
child.terminate()
for child in children:
try:
child.wait(timeout=5)
except subprocess.TimeoutExpired:
child.kill()
child.wait()
log.seek(0)
print(log.read().decode("utf-8", errors="replace")[-4000:])
if __name__ == "__main__":
main()