lazyBoy/tests/gui-targets.test.py

84 lines
4.3 KiB
Python
Raw Normal View History

2026-09-11 02:47:43 +00:00
"""Verify current controld rejects GUI routing conflicts before invoking Cua."""
import http.client
import json
import os
import subprocess
import time
assert os.environ.get('LAZYBOY_RUNNER_TEST_CONTAINER') == '1', 'disposable Computer only'
child = subprocess.Popen(['/usr/local/bin/lazyboy-controld'],
env={'PATH': '/usr/local/bin:/usr/bin:/bin', 'HOME': '/home/lazyboy',
'LAZYBOY_CONTROL_TOKEN': 'gui-target-fixture'}, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
try:
for _ in range(100):
assert child.poll() is None, 'fixture daemon failed to start'
try:
conn = http.client.HTTPConnection('127.0.0.1', 7070, timeout=2)
conn.request('GET', '/health')
response = conn.getresponse()
if response.status == 200:
conn.close()
break
except OSError:
time.sleep(.05)
finally:
conn.close()
else:
raise AssertionError('fixture startup deadline')
cases = 0
for route, initial in [
('act', {'actions': [], 'observe': False, 'settle_ms': 0}),
('browser', {'action': ''}),
('recording/start', {'skillId': 'fixture'}), ('recording/stop', {'skillId': 'fixture'}), ('recording/collect', {'skillId': 'fixture'}),
]:
for variant in ('display', 'profile', 'duplicate-display', 'duplicate-profile', 'slot'):
body = dict(initial)
body['display'] = ':2' if variant == 'display' else ':1'
body['profile_path' if route == 'act' else 'profilePath'] = '/profiles/b' if variant == 'profile' else '/profiles/a'
encoded = json.dumps(body).encode()
conn = http.client.HTTPConnection('127.0.0.1', 7070, timeout=5)
conn.putrequest('POST', '/' + route)
for name, value in [('Authorization', 'Bearer gui-target-fixture'), ('Content-Type', 'application/json'),
('Content-Length', str(len(encoded))), ('x-lazyboy-display', ':1'),
('x-lazyboy-profile', '/profiles/a'), ('x-lazyboy-screen-slot', '1' if variant == 'slot' else '0')]:
conn.putheader(name, value)
if variant.startswith('duplicate-'):
conn.putheader('x-lazyboy-' + variant.removeprefix('duplicate-'), ':1' if variant.endswith('display') else '/profiles/a')
conn.endheaders(encoded)
response = conn.getresponse()
raw = response.read()
conn.close()
assert response.status == 400, (route, variant, response.status, raw)
assert json.loads(raw) == {'ok': False, 'error': '400 Bad Request'}, (route, variant, raw)
cases += 1
for method, route in [('POST', '/observe'), ('GET', '/controller/health')]:
for variant in ('empty', 'invalid', 'remote', 'slot', 'duplicate-display', 'duplicate-profile', 'duplicate-slot'):
conn = http.client.HTTPConnection('127.0.0.1', 7070, timeout=5)
conn.putrequest(method, route)
conn.putheader('Authorization', 'Bearer gui-target-fixture')
conn.putheader('Content-Length', '0')
display = {'empty': '', 'invalid': ':99999', 'remote': 'remote:1'}.get(variant, ':1')
conn.putheader('x-lazyboy-display', display)
conn.putheader('x-lazyboy-profile', '/profiles/a')
conn.putheader('x-lazyboy-screen-slot', '1' if variant == 'slot' else '0')
if variant.startswith('duplicate-'):
name, value = {'duplicate-display': ('x-lazyboy-display', ':1'),
'duplicate-profile': ('x-lazyboy-profile', '/profiles/a'),
'duplicate-slot': ('x-lazyboy-screen-slot', '0')}[variant]
conn.putheader(name, value)
conn.endheaders()
response = conn.getresponse()
raw = response.read()
conn.close()
assert response.status == 400, (route, variant, response.status, raw)
assert json.loads(raw) == {'ok': False, 'error': '400 Bad Request'}, (route, variant, raw)
cases += 1
print(f'controld GUI target binding: {cases} rejected conflicts/duplicates')
finally:
child.terminate()
try:
child.wait(timeout=5)
except subprocess.TimeoutExpired:
child.kill()
child.wait()