Files
room_glitch/tests/room_network/check_objective_integration.py
Jeff Luke c1b2195834 Expand Glitch operations hub, cabinet authoring, deployment, and RFID support
Operator hub
- Introduce Escape Gallery branding and Glitch: the Dark Machine room views
- Add cabinet illustrations, Pac-Man marquee/gameplay artwork, and waiting states
- Add device feedback panels with RFID readings, input/output states, and stale status
- Add logged operator hardware interventions with request deduplication

Room and cabinet authoring
- Add a visual custom-PCB connection editor with per-cabinet device inventory
- Support device IDs, bindings, wiring notes, input polarity, and validation
- Add physical-room label/dimension toggles and editable doorway annotations
- Preserve doorway changes through undo, save, and revert
- Expand cabinet profiles, room layout, and Glitch scenario resources

Scenarios and gameplay
- Add per-assignment game configuration and runtime feature validation
- Add timed test wins and Frogger individual-home objectives
- Add the three-cabinet timed/score/Frogger test scenario
- Add Cabinet 1 Pac-Man RFID credits, credit sound, and CREDIT 0/1 display
- Require physical Start after an accepted scan and prevent duplicate credits

Pi hardware and deployment
- Add GPIO feedback sampling and a separate multiplexed RFID helper
- Validate reader pin ownership and input/output conflicts
- Add SSH cabinet startup and Pi desktop-session service support
- Add master packaging, site setup, SSH enrollment, and staged cabinet releases
- Verify deployment manifests and support explicit activation/status/log commands

Documentation and verification
- Consolidate the root README around the current architecture and setup workflow
- Add the complete master/Pi/RFID export and deployment walkthrough
- Document network isolation, credentials, card identification, and rollback
- Add PCB, room-editor, feedback, credit, deployment, and controller tests
- Isolate protocol tests from designer-edited cabinet configurations

Known limitations
- RFID helper compilation and physical Pi commissioning remain unverified
- Fleet provisioning does not yet configure RFID-only installations completely
- Broader Pac-Man tests retain a ghost-capture assertion failure
- Staged exports must be rebuilt and checked before deploying runtime 2.3
2026-10-17 14:53:54 -07:00

51 lines
3.0 KiB
Python

"""Three real game clients; no GPIO, no live deployment database."""
import json
import os
from pathlib import Path
import subprocess
import sys
import tempfile
import threading
import time
import urllib.request
from http.server import HTTPServer
from test_room import ROOT, server
with tempfile.TemporaryDirectory() as temp:
fleet=server.Fleet(Path(temp)/'chain.db',json.loads((ROOT/'tools/room_controller/catalog.json').read_text()))
service=HTTPServer(('127.0.0.1',0),server.handler_for(fleet,'test-operator-token',{'cabinet_01':'test-token-one-123','cabinet_02':'test-token-two-123','cabinet_03':'test-token-three-123'}))
thread=threading.Thread(target=service.serve_forever,daemon=True)
thread.start()
url='http://127.0.0.1:'+str(service.server_port)
processes=[]
def post(path,data={}):
request=urllib.request.Request(url+path,data=json.dumps(data).encode(),headers={'Authorization':'Bearer test-operator-token'})
with urllib.request.urlopen(request,timeout=3) as response: return json.load(response)
def wait(predicate):
deadline=time.monotonic()+30
while time.monotonic()<deadline:
state=post('/operator/state')
if predicate(state['cabinets']): return state
time.sleep(.1)
raise AssertionError('Timed out waiting for chain state')
try:
for cabinet,token in [('cabinet_01','test-token-one-123'),('cabinet_02','test-token-two-123'),('cabinet_03','test-token-three-123')]:
processes.append(subprocess.Popen([sys.argv[1],'--headless','--path',str(ROOT),'--log-file',str(Path(temp)/(cabinet+'.log')),'--script','tests/room_network/objective_agent.gd','--','--cabinet='+cabinet,'--test-no-reward'],env=dict(os.environ,ARCADE_ROOM_URL=url,ARCADE_CABINET_TOKEN=token)))
wait(lambda c:all(c[k]['online'] for k in ['cabinet_01','cabinet_02','cabinet_03']))
post('/operator/command',{'action':'prepare_scenario','scenario':'three_cabinet_objective_test'})
wait(lambda c:c['cabinet_01']['phase']=='ready' and c['cabinet_02']['phase']=='waiting' and c['cabinet_03']['phase']=='waiting')
post('/operator/command',{'action':'start','cabinet':'cabinet_01'})
state=wait(lambda c:all(c[k]['phase']=='won' for k in ['cabinet_01','cabinet_02','cabinet_03']))
commands=state['cabinets']['cabinet_02']['commands']
assert [c['type'] for c in commands]==['reset','prepare','start']
assert all(c['status']=='accepted' for c in commands)
assert [c['type'] for c in state['cabinets']['cabinet_03']['commands']]==['reset','prepare','start']
for process in processes: assert process.wait(timeout=8)==0
print('PASS: actual five-second auto-win -> 499/500 score boundary -> one Frogger home -> all three wins')
finally:
for process in processes:
if process.poll() is None:
process.terminate(); process.wait(timeout=5)
service.shutdown(); service.server_close(); thread.join()
for room in fleet.rooms.values(): room.db.close()