FrameBridge 0.3.1 community source release
This commit is contained in:
commit
b23819fff1
1354 files changed
+343716
No files matched your search
@@ -0,0 +1,204 @@
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import pty
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
from unittest import mock
|
||||
from framebridge_expanded import connection_background as background
|
||||
from framebridge_expanded import tunnel
|
||||
|
||||
ID = "tunnel_background_fixture"
|
||||
MCP = "http://127.0.0.1:20865/mcp"
|
||||
FAKE = "SYNTHETIC_BACKGROUND_ONLY_0123456789"
|
||||
CLIENT = r'''
|
||||
import datetime,http.server,json,os,sys,time
|
||||
from pathlib import Path
|
||||
started=datetime.datetime.now(datetime.timezone.utc).isoformat()
|
||||
identifier=sys.argv[sys.argv.index('--control-plane.tunnel-id')+1]
|
||||
mcp=sys.argv[sys.argv.index('--mcp.server-url')+1].removeprefix('url=')
|
||||
class Handler(http.server.BaseHTTPRequestHandler):
|
||||
def log_message(self,*args): pass
|
||||
def do_GET(self):
|
||||
if self.path=='/readyz': body=b'ready'
|
||||
elif self.path=='/healthz': body=b'live'
|
||||
elif self.path=='/api/status': body=json.dumps({'control_plane_tunnel_id':identifier,'mcp_server_url':mcp,'started_at':started,'tunnel_metadata':{'ID':identifier}}).encode()
|
||||
elif self.path=='/metrics': body=('commands_poll_last_successful_timestamp_seconds '+str(time.time())+'\n').encode()
|
||||
else: self.send_response(404);self.end_headers();return
|
||||
self.send_response(200);self.end_headers();self.wfile.write(body)
|
||||
server=http.server.HTTPServer(('127.0.0.1',0),Handler)
|
||||
Path(sys.argv[sys.argv.index('--health.url-file')+1]).write_text('http://127.0.0.1:'+str(server.server_port))
|
||||
print('TECHNICAL_FIXTURE '+os.environ['FRAMEBRIDGE_EXPANDED_RUNTIME_KEY'],flush=True)
|
||||
server.serve_forever()
|
||||
'''
|
||||
|
||||
|
||||
def command(code=CLIENT):
|
||||
return [sys.executable, "-u", "-c", code, "--control-plane.tunnel-id", ID,
|
||||
"--mcp.server-url", "url=" + MCP]
|
||||
|
||||
|
||||
class BackgroundTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temp = tempfile.TemporaryDirectory(prefix="fb-background-test-")
|
||||
self.state = Path(self.temp.name)
|
||||
|
||||
def tearDown(self):
|
||||
background.disconnect(self.state)
|
||||
self.temp.cleanup()
|
||||
|
||||
def start(self, wait=10):
|
||||
output = io.StringIO()
|
||||
rc = background.start(command(), FAKE, self.state, output=output, wait_timeout=wait)
|
||||
self.assertEqual(rc, 0, output.getvalue())
|
||||
return output.getvalue()
|
||||
|
||||
def test_real_detached_worker_ready_private_logs_and_no_credential_on_disk(self):
|
||||
output = self.start()
|
||||
current = background.status(self.state)
|
||||
self.assertTrue(current["alive"])
|
||||
self.assertTrue(current["verified"])
|
||||
self.assertIn("terminal can be closed", output)
|
||||
self.assertNotIn(FAKE, output)
|
||||
self.assertNotIn("TECHNICAL_FIXTURE", output)
|
||||
worker = Path('/proc') / str(current['pid'])
|
||||
self.assertNotIn(FAKE.encode(), (worker / 'cmdline').read_bytes())
|
||||
self.assertNotIn(FAKE.encode(), (worker / 'environ').read_bytes())
|
||||
log = self.state / 'logs/chatgpt/connection.log'
|
||||
deadline = time.monotonic()+3
|
||||
while 'TECHNICAL_FIXTURE' not in log.read_text() and time.monotonic()<deadline:
|
||||
time.sleep(.05)
|
||||
self.assertIn('TECHNICAL_FIXTURE [REDACTED]', log.read_text())
|
||||
for path in self.state.rglob('*'):
|
||||
if path.is_file():
|
||||
self.assertNotIn(FAKE.encode(), path.read_bytes(), str(path))
|
||||
self.assertEqual((self.state / 'connection').stat().st_mode & 0o777, 0o700)
|
||||
for name in ('owner.lock','status.json'):
|
||||
self.assertEqual((self.state / 'connection' / name).stat().st_mode & 0o777, 0o600)
|
||||
stopped = background.disconnect(self.state)
|
||||
self.assertTrue(stopped['ok'])
|
||||
self.assertFalse(background.status(self.state)['alive'])
|
||||
self.assertTrue(background.disconnect(self.state)['ok'])
|
||||
|
||||
def test_second_connect_reuses_owned_profile_without_duplicate_worker(self):
|
||||
self.start()
|
||||
pid = background.status(self.state)['pid']
|
||||
output = io.StringIO()
|
||||
self.assertEqual(background.start(command(), FAKE, self.state, output=output, wait_timeout=2),0)
|
||||
self.assertEqual(background.status(self.state)['pid'],pid)
|
||||
self.assertIn('already owns this profile',output.getvalue())
|
||||
|
||||
def test_startup_failure_never_reports_ready_and_releases_lock(self):
|
||||
output = io.StringIO()
|
||||
rc = background.start(command('raise SystemExit(7)'), FAKE, self.state, output=output, wait_timeout=5)
|
||||
self.assertEqual(rc,1)
|
||||
self.assertNotIn('[OK]',output.getvalue())
|
||||
self.assertFalse(background.status(self.state)['alive'])
|
||||
self.start()
|
||||
|
||||
def test_pending_is_explicit_and_keeps_background_worker(self):
|
||||
output=io.StringIO()
|
||||
rc=background.start(command('import time;time.sleep(60)'),FAKE,self.state,output=output,wait_timeout=.7)
|
||||
self.assertEqual(rc,1)
|
||||
self.assertIn('continues retrying in the background',output.getvalue())
|
||||
self.assertTrue(background.status(self.state)['alive'])
|
||||
self.assertFalse(background.status(self.state)['verified'])
|
||||
|
||||
def test_closed_controlling_terminal_does_not_stop_verified_tunnel(self):
|
||||
master,slave=pty.openpty()
|
||||
root=Path(background.__file__).resolve().parents[1]
|
||||
code=("import os,fcntl,termios,sys,signal;os.setsid();fcntl.ioctl(0,termios.TIOCSCTTY,0);"
|
||||
+"sys.path.insert(0,"+repr(str(root))+");from framebridge_expanded import connection_background as b;"
|
||||
+"rc=b.start("+repr(command())+","+repr(FAKE)+","+repr(str(self.state))+",wait_timeout=10);"
|
||||
+"signal.pause()")
|
||||
parent=subprocess.Popen([sys.executable,'-c',code],stdin=slave,stdout=slave,stderr=slave,close_fds=True)
|
||||
os.close(slave)
|
||||
try:
|
||||
deadline=time.monotonic()+12
|
||||
while time.monotonic()<deadline and not background.status(self.state)['verified']:
|
||||
self.assertIsNone(parent.poll())
|
||||
time.sleep(.1)
|
||||
self.assertTrue(background.status(self.state)['verified'])
|
||||
os.close(master);master=None
|
||||
parent.wait(timeout=3)
|
||||
time.sleep(.3)
|
||||
self.assertTrue(background.status(self.state)['verified'])
|
||||
finally:
|
||||
if master is not None: os.close(master)
|
||||
if parent.poll() is None: parent.terminate();parent.wait(timeout=3)
|
||||
|
||||
def test_stale_heartbeat_is_not_verified_and_forged_message_is_not_printed(self):
|
||||
self.start()
|
||||
pid=background.status(self.state)['pid']
|
||||
os.kill(pid,signal.SIGSTOP)
|
||||
try:
|
||||
path=self.state/'connection/status.json'
|
||||
record=json.loads(path.read_text());record['verified_at']=time.time()-30
|
||||
record['message']='\x1b[31m'+FAKE
|
||||
background._write(path,record)
|
||||
current=background.status(self.state)
|
||||
self.assertFalse(current['verified'])
|
||||
self.assertEqual(current['state'],'retrying')
|
||||
self.assertNotIn(FAKE,current['message'])
|
||||
self.assertNotIn('\x1b',current['message'])
|
||||
finally:
|
||||
os.kill(pid,signal.SIGCONT)
|
||||
|
||||
def test_forged_identity_does_not_signal_unrelated_process(self):
|
||||
child=subprocess.Popen([sys.executable,'-c','import time;time.sleep(30)'],start_new_session=True)
|
||||
try:
|
||||
directory=background._directory(self.state)
|
||||
lock=background._checked_fd(directory/'owner.lock',os.O_RDWR,create=True)
|
||||
try:
|
||||
record=background._identity(child.pid,self.state,lock)
|
||||
record.update(state='ready',verified_at=time.time())
|
||||
background._write(directory/'status.json',record)
|
||||
with mock.patch.object(signal,'pidfd_send_signal') as send:
|
||||
self.assertTrue(background.disconnect(self.state)['ok'])
|
||||
send.assert_not_called()
|
||||
self.assertIsNone(child.poll())
|
||||
self.assertFalse(background.status(self.state)['verified'])
|
||||
finally: os.close(lock)
|
||||
finally:
|
||||
child.terminate();child.wait(timeout=3)
|
||||
|
||||
def test_foreground_mode_cannot_overlap_owned_background_connection(self):
|
||||
self.start()
|
||||
with self.assertRaisesRegex(RuntimeError, 'already owns'):
|
||||
background.run_foreground(command(),FAKE,self.state,output=io.StringIO())
|
||||
self.assertTrue(background.status(self.state)['verified'])
|
||||
|
||||
def test_ctrl_c_before_published_state_reaps_owned_supervisor(self):
|
||||
real_spawn=subprocess.Popen
|
||||
seen=[]
|
||||
def spawn(*args,**kwargs):
|
||||
child=real_spawn(*args,**kwargs)
|
||||
seen.append(child)
|
||||
return child
|
||||
real_sleep=time.sleep
|
||||
interrupted=[]
|
||||
def interrupt_once(seconds):
|
||||
if not interrupted:
|
||||
interrupted.append(True)
|
||||
raise KeyboardInterrupt
|
||||
return real_sleep(seconds)
|
||||
with mock.patch.object(background.subprocess,'Popen',side_effect=spawn), mock.patch.object(background.time,'sleep',side_effect=interrupt_once):
|
||||
rc=background.start(command(),FAKE,self.state,output=io.StringIO(),wait_timeout=5)
|
||||
self.assertEqual(rc,130)
|
||||
self.assertTrue(seen)
|
||||
self.assertIsNotNone(seen[0].poll())
|
||||
self.assertFalse(background.status(self.state)['alive'])
|
||||
|
||||
def test_interactive_background_does_not_prompt_when_already_running(self):
|
||||
self.start()
|
||||
with mock.patch.object(tunnel,'_hidden') as prompt:
|
||||
self.assertEqual(tunnel.connect_chatgpt('/unused',self.state,MCP,output=io.StringIO()),0)
|
||||
prompt.assert_not_called()
|
||||
|
||||
|
||||
if __name__=='__main__': unittest.main()
|
||||
Reference in new issue
Block a user