import concurrent.futures from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer import json from pathlib import Path import subprocess import sys import threading import time import pytest from bekci_watch import Frames, Ledger, MetadataFilter, WatchOwner, opaque from native_speech_mux import NativeSpeechMux, stdio_invocation from test_native_speech_mux import Harness def wait(predicate): until=time.time()+5 while time.time()0) time.sleep(.1);assert mux._queue.empty() and not mux._bindings finally:owner.close();server.close() server=Server([frame('ready',{'cursor':'start'}),frame('notice',n,n['cursor'])]);owner=WatchOwner(mux,Ledger(tmp_path/'db2'),origin=server.origin,test_loopback=True) try: for t in ('A','B'): p=meta();p['subscription']=t*40;owner.accept(p,capture(owner,t),'member',server.origin) wait(lambda:mux._queue.qsize()==2) assert {mux._queue.get()[1][0]['thread_id'] for _ in range(2)}=={'A','B'} finally:owner.close();server.close() def test_launcher_stdio_grammar(tmp_path): assert stdio_invocation(['-c','x=true','app-server','--analytics-default-enabled','-c','y="z"']) assert not stdio_invocation(['exec','app-server']) assert not stdio_invocation(['app-server','--listen','ws://127.0.0.1']) assert not stdio_invocation(['app-server','daemon','start']) @pytest.mark.parametrize('active_owner', [True, False]) def test_http_receipt_through_real_bridge_handler_to_real_pipe(tmp_path, active_owner): """Real imported H, real HTTP primary/events, real subprocess append/approval.""" import os import http.client import urllib.request bridge=tmp_path/'bridge';bridge.mkdir() (bridge/'bridge.json').write_text(json.dumps({'aiturk_hat':{'enabled':False},'sesle_model':False,'nokta_karsila':False,'yarim_cevap':False,'ozet_kopru':{'enabled':False},'tasma_bekcisi':False}),encoding='utf-8') agent=tmp_path/'agent';agent.mkdir() old=dict(os.environ);os.environ['AITURK_CODEX_BRIDGE_HOME']=str(bridge);os.environ['HERMES_HOME']=str(agent) try: import kopru finally: os.environ.clear();os.environ.update(old) h=Harness(tmp_path);h.start() p=meta();n=notice();requests=[] class Primary(BaseHTTPRequestHandler): def log_message(self,*args): pass def do_POST(self): body=json.loads(self.rfile.read(int(self.headers['Content-Length']))) requests.append((self.path,self.headers.get('Authorization'),body)) if self.path=='/v1/bekci/events': wire=frame('ready',{'cursor':'start'})+frame('notice',n,n['cursor']) kind='text/event-stream' elif body.get('stream',True): wire=frame('response.output_text.delta',{'type':'response.output_text.delta','delta':'answer'})+frame('bekci.watch',p)+b'data: [DONE]\n\n';kind='text/event-stream' else: wire=json.dumps({'output':[],'_bekci_watch':p}).encode();kind='application/json' self.send_response(200);self.send_header('Content-Type',kind);self.send_header('Content-Length',str(len(wire)));self.end_headers();self.wfile.write(wire) upstream=ThreadingHTTPServer(('127.0.0.1',0),Primary);upstream.daemon_threads=True threading.Thread(target=upstream.serve_forever,daemon=True).start();origin='http://127.0.0.1:'+str(upstream.server_port) owner=WatchOwner(h.mux,Ledger(tmp_path/'http-ledger'),origin=origin,test_loopback=True) kopru.WATCH_OWNER=owner if active_owner else None;kopru.MODEL_MAP['ide/test-watch']=('test-route','Sesli-Hizli-Zeki',False) kopru.ROUTES['test-route']={'url':origin+'/v1/responses','key':'public-member','flatten_tools':False} server=kopru.TS(('127.0.0.1',0),kopru.H);threading.Thread(target=server.serve_forever,daemon=True).start() try: for stream in (True,False): conn=http.client.HTTPConnection('127.0.0.1',server.server_port,timeout=5) conn.request('POST','/v1/responses',json.dumps({'model':'ide/test-watch','stream':stream,'prompt_cache_key':'thread-A','input':[]}),{'Content-Type':'application/json','session-id':'thread-A','thread-id':'thread-A'}) resp=conn.getresponse();data=resp.read();conn.close() assert resp.status==200 and b'subscription' not in data and b'_bekci_watch' not in data if active_owner: approval=json.loads(h.recv());speech=json.loads(h.recv()) assert approval['method']=='item/commandExecution/requestApproval' assert speech['method']=='seen-speech' and speech['params']=={'threadId':'thread-A','text':'correction'} assert any(path=='/v1/bekci/events' and auth=='Bearer public-member' for path,auth,_ in requests) else: assert not any(path=='/v1/bekci/events' for path,_,_ in requests) finally: owner.close();server.shutdown();server.server_close();upstream.shutdown();upstream.server_close();kopru.WATCH_OWNER=None;h.close() def test_http_403_expiry_and_crash_claim_never_resend(tmp_path): server=Server([],status=403);mux=NativeSpeechMux(['unused'],observer=True);started(mux,'A') owner=WatchOwner(mux,Ledger(tmp_path/'db'),origin=server.origin,test_loopback=True) try: owner.accept(meta(),capture(owner,'A'),'wrong-key',server.origin) wait(lambda:len(server.requests)==1);wait(lambda:not owner.watches) assert not mux._bindings and mux._queue.empty() finally:owner.close();server.close() n=notice();server=Server([frame('ready',{'cursor':'start'}),frame('notice',n,n['cursor'])]) ledger=Ledger(tmp_path/'crash');p=meta();scope=opaque('public-watch','member',p['subscription']);assert ledger.claim(scope,'one') owner=WatchOwner(mux,Ledger(tmp_path/'crash'),origin=server.origin,test_loopback=True) try: p['expires_at']=(time.time()+.4)*1000 owner.accept(p,capture(owner,'A'),'member',server.origin) wait(lambda:owner.ledger.cursor(scope)==n['cursor']);wait(lambda:not owner.watches) assert mux._queue.empty() finally:owner.close();server.close() def test_receipt_cannot_move_to_other_thread_or_generation(tmp_path): server=Server([frame('ready',{'cursor':'start'})]);mux=NativeSpeechMux(['unused'],observer=True) started(mux,'A');started(mux,'B');owner=WatchOwner(mux,Ledger(tmp_path/'db'),origin=server.origin,test_loopback=True);p=meta() try: assert owner.accept(p,capture(owner,'A'),'member',server.origin)=='subscribing' assert owner.accept(p,capture(owner,'B'),'member',server.origin)=='receipt_replay_mismatch' mux._observe_client({'method':'thread/unsubscribe','params':{'threadId':'A'}}) wait(lambda:not owner.watches);started(mux,'A') assert owner.accept(p,capture(owner,'A'),'member',server.origin)=='receipt_replay_mismatch' finally:owner.close();server.close() @pytest.mark.parametrize('seconds', [16*60, 60*60, 24*60*60, 24*60*60+30]) def test_protocol_lifetime_receipt_admitted(tmp_path, seconds): server=Server([frame('ready', {'cursor':'start'})]) mux=NativeSpeechMux(['unused'],observer=True);started(mux,'A') owner=WatchOwner(mux,Ledger(tmp_path/'db'),origin=server.origin,test_loopback=True) try: p=meta();p['expires_at']=(time.time()+seconds)*1000 assert owner.accept(p,capture(owner,'A'),'member',server.origin)=='subscribing' wait(lambda:len(server.requests)==1) wait(lambda:bool(mux._bindings)) assert owner.watches finally: owner.close();server.close() @pytest.mark.parametrize('expiry', [0, -1, float('inf'), float('-inf'), float('nan'), True, False, 'infinite', None, 10**100]) def test_invalid_or_unbounded_expiry_rejected(tmp_path, expiry): mux=NativeSpeechMux(['unused'],observer=True);started(mux,'A') owner=WatchOwner(mux,Ledger(tmp_path/'db')) p=meta();p['expires_at']=expiry assert owner.accept(p,capture(owner,'A'),'member','https://ai.turkservis.online/v1/responses')=='invalid_receipt' assert not owner.watches def test_receipt_beyond_protocol_and_skew_rejected(tmp_path): mux=NativeSpeechMux(['unused'],observer=True);started(mux,'A') owner=WatchOwner(mux,Ledger(tmp_path/'db')) p=meta();p['expires_at']=(time.time()+24*60*60+120)*1000 assert owner.accept(p,capture(owner,'A'),'member','https://ai.turkservis.online/v1/responses')=='invalid_receipt' @pytest.mark.parametrize('target', ['Other-Model', 'sesli-hizli-zeki', 'Sesli-Hizli-Zeki-extra']) @pytest.mark.parametrize('json_response', [False, True]) @pytest.mark.parametrize('jev', [False, True]) def test_actual_handler_nonvoice_preserves_response_pipeline(tmp_path, monkeypatch, target, json_response, jev): import http.client import kopru from unittest.mock import Mock # Exceed the sideband frame cap: other models must never enter its buffer. wire=(json.dumps({'output':[], 'payload':'x'*270000}).encode() if json_response else frame('response.output_text.delta', {'delta':'x'*270000})) class Primary(BaseHTTPRequestHandler): def log_message(self,*args): pass def do_POST(self): self.rfile.read(int(self.headers['Content-Length'])) self.send_response(200) self.send_header('Content-Type','application/json' if json_response else 'text/event-stream') self.send_header('Content-Length',str(len(wire)));self.end_headers();self.wfile.write(wire) upstream=ThreadingHTTPServer(('127.0.0.1',0),Primary) threading.Thread(target=upstream.serve_forever,daemon=True).start() owner=Mock();monkeypatch.setattr(kopru,'WATCH_OWNER',owner) monkeypatch.setitem(kopru.MODEL_MAP,'ide/scope-proof',('aiturk-ide',target,None)) monkeypatch.setitem(kopru.ROUTES,'aiturk-ide',{'url':f'http://127.0.0.1:{upstream.server_port}/v1/responses','key':'scope-member','flatten_tools':jev}) monkeypatch.setattr(kopru.jev_denetim,'anahtar',lambda:'test-key' if jev else '') wrapped=Mock(side_effect=AssertionError('nonvoice entered watch wrapper')) monkeypatch.setattr(kopru,'WatchResponse',wrapped) shortcut=Mock(side_effect=AssertionError('nonvoice entered JSON shortcut')) monkeypatch.setattr(kopru.H,'_send_json',shortcut) calls=[] original_stream=kopru.H._stream def stream(self,resp,*args,**kwargs): calls.append('stream') return original_stream(self,resp,*args,**kwargs) def denetim(self,resp,tag,rewriter,izle,istek,sira,ac,*,hedef): # Verify original upstream object/body reaches Jev, not a watch wrapper. calls.append('jev');assert hedef==target and sira data=resp.read();resp.close();assert data==wire self.send_response(200);self.send_header('Content-Length',str(len(data))) self.end_headers();self.wfile.write(data) monkeypatch.setattr(kopru.H,'_stream',stream) monkeypatch.setattr(kopru.H,'_denetimli_akis',denetim) server=kopru.TS(('127.0.0.1',0),kopru.H) threading.Thread(target=server.serve_forever,daemon=True).start() try: conn=http.client.HTTPConnection('127.0.0.1',server.server_port,timeout=5) conn.request('POST','/v1/responses',json.dumps({'model':'ide/scope-proof','stream':not json_response,'input':[]}),{'Content-Type':'application/json'}) response=conn.getresponse();data=response.read();conn.close() assert response.status==200 and data==wire assert calls==(['jev'] if jev else ['stream']) wrapped.assert_not_called();shortcut.assert_not_called();owner.accept.assert_not_called() finally: server.shutdown();server.server_close();upstream.shutdown();upstream.server_close() class QuietWatchServer: """Real sockets, with no heartbeat; observes client shutdown at the peer.""" def __init__(self, protocol='HTTP/1.0', delay=0, headers_delay=False, reconnects=False, delayed_headers=False): self.requests=[];self.closed=threading.Event();self.peer_closed=threading.Event() self.ready=threading.Event();owner=self class H(BaseHTTPRequestHandler): protocol_version='HTTP/1.1' if protocol=='HTTP/1.1-close' else protocol def log_message(self,*args): pass def do_POST(self): body=json.loads(self.rfile.read(int(self.headers['Content-Length']))) owner.requests.append(body);number=len(owner.requests) try: if headers_delay: owner.ready.set() self.connection.settimeout(5) if self.connection.recv(1)==b'': owner.peer_closed.set() return if delayed_headers and owner.closed.wait(delay): return self.send_response(200);self.send_header('Content-Type','text/event-stream') if protocol=='HTTP/1.1-close': self.send_header('Connection','close') self.end_headers() self.wfile.write(frame('ready',{'cursor':'start'}));self.wfile.flush();owner.ready.set() if reconnects: n=notice('first' if number<9 else 'ninth') self.wfile.write(frame('notice',n,n['cursor']));self.wfile.flush() if number<9: self.close_connection=True return elif delay and number==1: if not delayed_headers and owner.closed.wait(delay): return n=notice('delayed');self.wfile.write(frame('notice',n,n['cursor']));self.wfile.flush() self.connection.settimeout(5 if not delay else 20) if self.connection.recv(1)==b'': owner.peer_closed.set() except OSError: pass self.server=ThreadingHTTPServer(('127.0.0.1',0),H);self.server.daemon_threads=True self.origin='http://127.0.0.1:'+str(self.server.server_port) threading.Thread(target=self.server.serve_forever,daemon=True).start() def close(self): self.closed.set();self.server.shutdown();self.server.server_close() @pytest.mark.parametrize('delayed_headers',[False,True]) def test_quiet_watch_delivers_after_more_than_ten_seconds(tmp_path,delayed_headers): server=QuietWatchServer(delay=11.2,delayed_headers=delayed_headers) mux=NativeSpeechMux(['unused'],observer=True);started(mux,'A') owner=WatchOwner(mux,Ledger(tmp_path/'quiet'),origin=server.origin,test_loopback=True) try: assert owner.accept(meta(),capture(owner,'A'),'member',server.origin)=='subscribing' assert server.ready.wait(14) until=time.monotonic()+14 while mux._queue.empty() and time.monotonic()=9) wait(lambda:owner.ledger.cursor(scope)=='cursor-ninth') assert mux._queue.qsize()==2 assert all(req.get('cursor')=='cursor-first' for req in server.requests[1:9]) finally: owner.close();worker.join(timeout=3);server.close() assert not worker.is_alive()