LazyBoy2/tests/box_surface_flow.py

78 lines
4.4 KiB
Python
Raw Normal View History

2026-09-14 09:08:35 +00:00
"""Real Docker contract: shared desktop profile, reconnect, restart, transfer paths.
Build grokboy-box:surface-test first; uses an isolated container and temporary data.
"""
import json, subprocess, time, uuid, select
NAME = 'grokboy-surface-test-' + uuid.uuid4().hex[:10]
IMAGE = 'grokboy-box:surface-test'
def docker(*args, **kw):
return subprocess.run(['docker', *args], check=True, capture_output=True, text=True, **kw).stdout.strip()
def ready():
for _ in range(120):
try:
docker('exec', NAME, 'python3', '-c', 'import urllib.request; urllib.request.urlopen("http://127.0.0.1:9222/json/version",timeout=1)')
return
except subprocess.CalledProcessError:
time.sleep(.5)
raise AssertionError('Chromium did not become ready')
def fixture():
docker('exec', NAME, 'sh', '-c', 'mkdir -p /workspace/fixture; printf "<html><body>fixture<input type=file id=upload><a id=download href=download.bin download>download</a></body></html>" > /workspace/fixture/index.html; printf fixture-bytes > /workspace/fixture/download.bin; printf upload-fixture > /workspace/upload.txt')
docker('exec', '-d', NAME, 'python3', '-m', 'http.server', '8765', '--directory', '/workspace/fixture')
def helper():
return subprocess.Popen(['docker','exec','-i','-w','/workspace','-e','GROKBOY_BROWSER_CDP=http://127.0.0.1:9222',NAME,'flock','-n','/home/box/.grokboy-browser.lock','node','/opt/grokboy/playwright/browser_helper.mjs'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
def request(p, op, **args):
p.stdin.write(json.dumps({'id':'fixture', 'op':op, **args})+'\n'); p.stdin.flush()
assert select.select([p.stdout], [], [], 30)[0], f'helper timed out: {op}'
line=p.stdout.readline(); assert line, p.stderr.read()
value=json.loads(line); assert value.get('ok'), value
return value
def close(p):
request(p,'close'); p.stdin.close(); p.wait(timeout=10)
try:
docker('run','-d','--name',NAME,'--shm-size=1g',IMAGE)
ready(); fixture()
p=helper()
request(p,'navigate',url='http://127.0.0.1:8765')
request(p,'eval',expression='document.cookie="login=fixture; max-age=3600; path=/"; localStorage.setItem("account","fixture"); sessionStorage.setItem("tab","same"); true')
request(p,'upload',selector='#upload',path='/workspace/upload.txt')
assert request(p,'eval',expression='document.querySelector("#upload").files[0].name')['result']=='upload.txt'
request(p,'download',selector='#download',path='/workspace/saved.bin')
assert docker('exec',NAME,'cat','/workspace/saved.bin')=='fixture-bytes'
print('PASS upload/download use Docker workspace', flush=True)
info=request(p,'handoff_prepare'); assert info['headed'] and not info['relaunched'], info
close(p)
ready() # releasing the tool must not close the desktop browser
p=helper()
result=request(p,'eval',expression='[document.cookie,localStorage.getItem("account"),sessionStorage.getItem("tab")]')['result']
assert result==['login=fixture','fixture','same'], result
invalid=request(p,'status'); assert invalid['headed']
other=helper()
other.communicate(json.dumps({'id':'busy','op':'ping'})+'\n',timeout=5)
assert other.returncode != 0, 'second helper stole the browser lease'
p.kill(); p.wait(timeout=5)
p.stdin.close(); p.stdout.close(); p.stderr.close()
for _ in range(30):
check=subprocess.run(['docker','exec',NAME,'flock','-n','/home/box/.grokboy-browser.lock','true'],capture_output=True)
if check.returncode == 0: break
time.sleep(.1)
assert check.returncode == 0, 'cancelled helper leaked its browser lease'
p=helper()
assert request(p,'eval',expression='localStorage.getItem("account")')['result']=='fixture'
close(p)
print('PASS competing helpers excluded; cancellation releases the lease', flush=True)
print('PASS handoff and helper reconnect retain same browser, cookie, localStorage, sessionStorage', flush=True)
docker('restart', NAME); ready(); fixture()
p=helper(); request(p,'navigate',url='http://127.0.0.1:8765')
result=request(p,'eval',expression='[document.cookie,localStorage.getItem("account")]')['result']
assert result==['login=fixture','fixture'], result
close(p)
print('PASS Docker restart retains Chromium login profile', flush=True)
finally:
subprocess.run(['docker','rm','-f',NAME],capture_output=True)