lazyBoy/scripts/test-runner-jobs.py

95 lines
6.8 KiB
Python

#!/usr/bin/env python3
"""Disposable, network-isolated integration gate; never mounts existing Computer data."""
import os
from pathlib import Path
import subprocess
import tempfile
import uuid
ROOT = Path(__file__).resolve().parents[1]
if not os.environ.get('DATABASE_URL'):
raise SystemExit('DATABASE_URL must point to a disposable test PostgreSQL server; the API recovery gate creates SQLx test databases')
name = 'lazyboy-plan-runner-test-' + uuid.uuid4().hex[:12]
def run(*args, **kwargs):
return subprocess.run(args, check=True, cwd=ROOT, **kwargs)
run('python3', 'tests/runner-bootstrap.test.py')
run('python3', 'tests/runtime-probe.test.py')
run('python3', 'tests/gui-fences.test.py')
run('python3', 'tests/file-process-proof.test.py')
run('python3', 'tests/file-outbox-scan.test.py')
run('python3', 'tests/file-retirement-proof.test.py')
run('python3', 'tests/file-journal-capacity.test.py')
run('python3', 'tests/file-artifacts.test.py')
created = False
builder_created = False
temporary = tempfile.TemporaryDirectory(prefix='lazyboy-mcp-test-')
try:
build_image = os.environ.get('LAZYBOY_TEST_MCP_IMAGE', 'lazyboy/controld:plan-test')
if 'LAZYBOY_TEST_MCP_IMAGE' not in os.environ:
run('docker', 'build', '-f', 'image/computer/Dockerfile', '--target', 'controld', '-t', build_image, '.')
run('docker', 'create', '--name', name + '-build', build_image)
builder_created = True
binary = str(Path(temporary.name) / 'lazyboy-controld')
run('docker', 'cp', name + '-build:/lazyboy-controld', binary)
runtime_mounts = []
for key, target, mode in [('runner','/var/lib/lazyboy-runner',0o700),('packages','/opt/lazyboy-tools',0o755),('private-packages','/opt/lazyboy-private-tools',0o711),('tool-state','/var/lib/lazyboy-tool-state',0o711)]:
directory=Path(temporary.name)/'runtime'/name/key
directory.mkdir(parents=True,mode=mode)
runtime_mounts.extend(['--mount',f'type=bind,source={directory},target={target}'])
computer_args=['docker','run','-d','--name',name,'--network','none','--cpus','2',
'--memory','512m','--pids-limit','256','--user','0:0','--label',f'lazyboy.homeKey={name}','--entrypoint',
'sleep',os.environ.get('LAZYBOY_COMPUTER_IMAGE','lazyboy/computer:local'),'3600']
run(*computer_args)
created = True
run('docker', 'exec', name, 'mkdir', '-m', '700', '-p', '/var/lib/lazyboy-runner')
runner_source = Path(temporary.name) / 'runner_jobs.py'
runner_source.write_text('RUNTIME_PROBE_SOURCE = ' + repr((ROOT / 'crates/supervisor/src/runtime_probe.py').read_text()) + '\n' + 'FILE_HELPER_SOURCE = ' + repr((ROOT / 'crates/supervisor/src/computer_files.py').read_text()) + '\n' + '\n'.join((ROOT / ('crates/supervisor/src/' + part)).read_text() for part in ['runner_packages.py','runner_files.py','runner_gui_fences.py','runner_jobs.py']))
run('docker', 'cp', str(runner_source), name + ':/var/lib/lazyboy-runner/runner_jobs.py')
run('docker', 'cp', 'tests/runner-jobs.test.py', name + ':/tmp/runner-jobs.test.py')
run('docker', 'cp', 'tests/runner-files.test.py', name + ':/tmp/runner-files.test.py')
run('docker', 'cp', binary, name + ':/usr/local/bin/lazyboy-controld')
run('docker', 'cp', 'tests/mcp-http.test.py', name + ':/tmp/mcp-http.test.py')
run('docker', 'cp', 'tests/mcp-packages.test.py', name + ':/tmp/mcp-packages.test.py')
run('docker', 'cp', 'tests/gui-targets.test.py', name + ':/tmp/gui-targets.test.py')
run('docker', 'exec', '-e', 'LAZYBOY_RUNNER_TEST_CONTAINER=1', name, 'python3', '/tmp/gui-targets.test.py')
run('docker', 'exec', '-e', 'LAZYBOY_RUNNER_TEST_CONTAINER=1', name, 'python3', '/tmp/runner-jobs.test.py')
run('docker', 'exec', '-e', 'LAZYBOY_RUNNER_TEST_CONTAINER=1', name, 'python3', '/tmp/runner-files.test.py')
run('docker', 'exec', '-e', 'LAZYBOY_RUNNER_TEST_CONTAINER=1', name, 'python3', '/tmp/mcp-http.test.py')
run('docker', 'exec', '-e', 'LAZYBOY_RUNNER_TEST_CONTAINER=1', name, 'python3', '/tmp/mcp-packages.test.py')
run('docker', 'cp', 'tests/mcp-restart.test.py', name + ':/tmp/mcp-restart.test.py')
run('docker', 'exec', '-e', 'LAZYBOY_RUNNER_TEST_CONTAINER=1', name, 'python3', '/tmp/mcp-restart.test.py', 'prepare')
run('docker', 'restart', '--timeout', '5', name)
run('docker','exec','-e','LAZYBOY_RUNNER_TEST_CONTAINER=1',name,'python3','/tmp/runner-files.test.py','verify')
run('docker', 'exec', '-e', 'LAZYBOY_RUNNER_TEST_CONTAINER=1', name, 'python3', '/tmp/mcp-restart.test.py', 'verify')
run('docker', 'exec', '-e', 'LAZYBOY_RUNNER_TEST_CONTAINER=1', name, 'python3', '/tmp/mcp-restart.test.py', 'prepare')
run('cargo','test','-p','lazyboy-supervisor','legacy_runtime_migration_preserves_data_and_resumes_publication','--','--ignored','--nocapture',
env={**{key:value for key,value in os.environ.items() if key!='HOST_DATA_DIR'},'LAZYBOY_PLAN_TEST_CONTAINER':name,'LAZYBOY_PLAN_MIGRATION_DATA_DIR':temporary.name,'DATA_DIR':temporary.name})
old_id=run('docker','inspect','--format','{{.Id}}',name,capture_output=True,text=True).stdout.strip()
run('docker','rm','-f',name)
created=False
run(*computer_args[:2],*runtime_mounts,*computer_args[2:])
created=True
new_id=run('docker','inspect','--format','{{.Id}}',name,capture_output=True,text=True).stdout.strip()
assert old_id != new_id, 'recreate must use a different container'
run('docker','cp',binary,name+':/usr/local/bin/lazyboy-controld')
run('docker','cp','tests/mcp-packages.test.py',name+':/tmp/mcp-packages.test.py')
run('docker','cp','tests/mcp-restart.test.py',name+':/tmp/mcp-restart.test.py')
run('docker','cp','tests/runner-files.test.py',name+':/tmp/runner-files.test.py')
run('docker','exec','-e','LAZYBOY_RUNNER_TEST_CONTAINER=1',name,'python3','/tmp/runner-files.test.py','verify')
run('docker','exec','-e','LAZYBOY_RUNNER_TEST_CONTAINER=1',name,'python3','/tmp/mcp-restart.test.py','verify')
print('actual remove/recreate with persistent runtime mounts verified',flush=True)
run('cargo', 'test', '-p', 'lazyboy-supervisor', 'real_computer_files_cas_and_bidirectional_exec',
'--', '--ignored', '--nocapture', env={**os.environ, 'LAZYBOY_PLAN_TEST_CONTAINER': name})
run('cargo','test','-p','lazyboy-supervisor','durable_file_http_transport_replays_without_reapplying','--','--ignored','--nocapture',env={**os.environ,'LAZYBOY_PLAN_TEST_CONTAINER':name})
run('cargo','build','-p','lazyboy-supervisor')
run('cargo','test','-p','lazyboy-api','real_file_dispatch_recovers_lost_response_without_reapplying','--','--ignored','--nocapture',env={**os.environ,'LAZYBOY_PLAN_TEST_CONTAINER':name,'LAZYBOY_PLAN_SUPERVISOR_BINARY':str(ROOT/'target/debug/lazyboy-supervisor')})
finally:
if builder_created:
run('docker', 'rm', '-f', name + '-build')
if created:
run('docker', 'rm', '-f', name)
temporary.cleanup()