"""Binary transport regressions plus Google request/cache boundary tests.""" import base64 import importlib.util import http.client import io import json import random import tempfile import threading import unittest import wave from pathlib import Path from policy_harness import ROOT, body, run path = ROOT / 'addons/core/Scripts/Game/ERR/00_Core/ERR_WelcomeVoice.c' decode = body(path, 'static bool Decode(').replace('array bytes', 'Array& bytes').replace('Math.Min', 'std::min') ready = body(path, 'static bool Ready(') weather = body(path, 'static string Weather(') briefing = body(path, 'static string Briefing(') key = body(path, 'static bool ClipKey(') connection = 'class Connection { bool m_Attempted = false; public: ' + '\n'.join(body(path, signature) for signature in ['void MainMenu()', 'bool Claim()', 'bool Attempted()']) + '};' cases = [] for size in [44, 45, 46, 47, 255, 256, 257, 8192]: data = random.Random(size).randbytes(size) text = base64.b64encode(data).decode() cases.append('assert(Decode("' + text + '",v)); assert(v==Array({' + ','.join(map(str, data)) + '}));') valid = base64.b64encode(bytes(44)).decode() for invalid in ['', 'RIFF', valid[:-1], valid[:5] + '?' + valid[6:], valid[:8] + '=' + valid[9:], valid[:-2] + 'B=', valid[:-2] + '=A']: cases.append('assert(!Decode(' + json.dumps(invalid) + ',v));') run(r''' #include #include #include struct string:std::string { using std::string::string;string(std::string s):std::string(s){} bool IsEmpty(){return empty();} void ToLower(){for(auto &c:*this)c=std::tolower(c);} int Length(){return size();} string Substring(int i,int n){return substr(i,n);} int IndexOf(string s){auto p=find(s);return p==npos?-1:int(p);} }; struct Array:std::vector{using std::vector::vector;void Clear(){clear();}void Insert(int v){push_back(v);}int Count(){return size();}}; ''' + decode + '\n' + ready + '\n' + weather + '\n' + briefing + '\n' + key + '\n' + connection + '\nint main(){Array v;\n' + '\n'.join(cases) + r''' Connection c; assert(!c.Attempted()); assert(c.Claim()); for(int i=0;i<100;i++) assert(!c.Claim()); // repeated ticks/respawns share this object assert(c.Attempted()); c.MainMenu(); assert(c.Claim()); // actual new connection assert(Briefing("Nogova","Clear","Rainy").find("This is Nogova.")!=string::npos); assert(Briefing("Chernarus","Clear","Rainy").find("Expected next: rain.")!=string::npos); assert(Briefing("Everon","Clear","Clear").find("Expected next:")==string::npos); assert(Briefing("","alien","unknown").find("Current weather:")==string::npos); assert(Briefing("","alien","unknown").find("Expected next:")==string::npos); assert(ClipKey(string(64,'a'))); assert(!ClipKey(string(64,'z'))); assert(!ClipKey("../welcome")); assert(Ready(false,true,true,false)); assert(!Ready(true,true,true,false)); assert(!Ready(false,false,true,false)); assert(!Ready(false,true,false,false)); assert(!Ready(false,true,true,true)); }''') spec = importlib.util.spec_from_file_location('welcome_voice', ROOT/'tools/welcome_voice.py') tool = importlib.util.module_from_spec(spec) spec.loader.exec_module(tool) def clip(): output = io.BytesIO() with wave.open(output, 'wb') as wav: wav.setnchannels(1) wav.setsampwidth(2) wav.setframerate(24000) wav.writeframes(bytes(4800)) return output.getvalue() class Response: status_code = 200 def __enter__(self): return self def __exit__(self, *_): pass def iter_content(self, _): yield json.dumps({'audioContent': base64.b64encode(clip()).decode()}).encode() class Session: def __init__(self): self.calls = [] def post(self, endpoint, **kwargs): self.calls.append((endpoint, kwargs)) return Response() class WelcomeTests(unittest.TestCase): def test_large_cached_audio_downloads_in_bounded_parts(self): # A 25-second Google-format clip exceeds Enfusion's single-response # ceiling. Existing disk caches must work without another synthesis. output = io.BytesIO() with wave.open(output, 'wb') as wav: wav.setnchannels(1) wav.setsampwidth(2) wav.setframerate(24000) wav.writeframes(random.Random(53).randbytes(25 * 24000 * 2)) audio = output.getvalue() encoded = base64.b64encode(audio) self.assertGreater(len(encoded), 1024 * 1024) with tempfile.TemporaryDirectory() as directory: def forbidden(text): self.fail('GET must never generate speech') cache = tool.BriefingCache(directory, 'x'*40, forbidden, 'transport-test') key = 'a'*64 tool.save_clip(Path(directory)/(key + '.wav'), audio) server = tool.ThreadingHTTPServer(('127.0.0.1', 0), tool.handler_for(audio, cache)) worker = threading.Thread(target=server.serve_forever, daemon=True) worker.start() connection = http.client.HTTPConnection(*server.server_address, timeout=3) try: for path in ('/welcome.b64', '/clips/' + key + '.b64'): chunks = [] for part, offset in enumerate(range(0, len(encoded), tool.VOICE_CHUNK)): connection.request('GET', path + '?part=' + str(part)) response = connection.getresponse() payload = response.read() self.assertEqual(response.status, 200) self.assertEqual(int(response.getheader('Content-Length')), len(payload)) self.assertLessEqual(len(payload), 262272) data = json.loads(payload) self.assertEqual(data['offset'], offset) self.assertEqual(data['total'], len(encoded)) chunks.append(data['data'].encode()) self.assertEqual(base64.b64decode(b''.join(chunks)), audio) # Legacy clients still receive the original, unmodified clip. connection.request('GET', path) response = connection.getresponse() self.assertEqual(response.read(), encoded) for path, status in [('/welcome.b64?part=99', 416), ('/welcome.b64?part=-1', 400), ('/welcome.b64?part=0&part=1', 400), ('/welcome.wav?part=0', 400), ('/welcome.b64?part=10000000', 400)]: connection.request('GET', path) response = connection.getresponse() response.read() self.assertEqual(response.status, status) self.assertEqual(cache.read(key), audio) finally: connection.close() server.shutdown() server.server_close() worker.join(timeout=3) def test_bounded_http_request_framing(self): read = tool.read_request_body self.assertEqual(read({'Content-Length': '2'}, io.BytesIO(b'{}')), b'{}') chunked = {'Transfer-Encoding': 'chunked'} self.assertEqual(read(chunked, io.BytesIO(b'1\r\n{\r\n1;ext=yes\r\n}\r\n0\r\nX-Test: ok\r\n\r\n')), b'{}') for headers, data in [ ({}, b'{}'), ({'Content-Length': '0'}, b''), ({'Content-Length': '8193'}, b'x'), ({'Content-Length': '4'}, b'{}'), ({'Transfer-Encoding': 'gzip'}, b'{}'), ({'Transfer-Encoding': 'chunked', 'Content-Length': '2'}, b'{}'), (chunked, b'2001\r\n'), (chunked, b'3\r\n{}'), (chunked, b'2\r\n{}XX0\r\n\r\n'), (chunked, b'Q\r\n'), (chunked, b'0\r\n\r\n'), (chunked, b'2\r\n{}\r\n0\r\n'), (chunked, b'2\r\n{}\r\n0\r\n' + b'a'*1025 + b'\r\n'), (chunked, b'1\r\na\r\n'*2049 + b'0\r\n\r\n'), ]: with self.subTest(headers=headers, prefix=data[:12]), self.assertRaises(ValueError): read(headers, io.BytesIO(data)) with self.assertRaises(ValueError): read(chunked, io.BytesIO(b'2\r\nab\r\n2\r\ncd\r\n0\r\n\r\n'), limit=3) def test_real_chunked_http_generation_and_playback(self): # An iterable body makes http.client send Transfer-Encoding: chunked # without Content-Length, as allowed by HTTP/1.1. No Google call. with tempfile.TemporaryDirectory() as directory: calls = [] def generate(text): calls.append(text) return clip() token = 'test-only-secret-' + 'x'*32 cache = tool.BriefingCache(directory, token, generate, 'chunked-test') server = tool.ThreadingHTTPServer(('127.0.0.1', 0), tool.handler_for(clip(), cache)) worker = threading.Thread(target=server.serve_forever, daemon=True) worker.start() connection = http.client.HTTPConnection(*server.server_address, timeout=3) try: payload = json.dumps({'token': token, 'text': 'Welcome to Chernarus.'}).encode() chunks = [payload[:15], payload[15:50], payload[50:]] connection.request('POST', '/', iter(chunks), {'Content-Type': 'application/json'}, encode_chunked=True) response = connection.getresponse() self.assertEqual(response.status, 200) key = json.loads(response.read())['clip'] connection.request('GET', '/clips/' + key + '.b64') response = connection.getresponse() self.assertEqual(response.status, 200) self.assertEqual(base64.b64decode(response.read()), clip()) connection.request('POST', '/', payload, {'Content-Type': 'application/json'}) response = connection.getresponse() self.assertEqual(response.status, 200) self.assertEqual(json.loads(response.read())['clip'], key) self.assertEqual(calls, ['Welcome to Chernarus.']) finally: connection.close() server.shutdown() server.server_close() worker.join(timeout=3) def test_http_generation_requires_token_and_get_only_reads_cache(self): with tempfile.TemporaryDirectory() as directory: calls = [] def generate(text): calls.append(text) return clip() cache = tool.BriefingCache(directory, 'x'*40, generate, 'veteran') handler = tool.handler_for(clip(), cache) def request(method, path, payload=None): instance = handler.__new__(handler) data = json.dumps(payload).encode() if payload is not None else b'' instance.path, instance.headers = path, {'Content-Length': str(len(data))} instance.rfile, instance.wfile = io.BytesIO(data), io.BytesIO() statuses = [] instance.send_response = statuses.append instance.send_error = statuses.append instance.send_header = lambda *args: None instance.end_headers = lambda: None getattr(instance, 'do_' + method)() return statuses[-1], instance.wfile.getvalue() self.assertEqual(request('POST', '/', {'text': 'Nogova'})[0], 403) self.assertEqual(request('GET', '/?text=Nogova')[0], 404) self.assertEqual(calls, []) status, payload = request('POST', '/', {'token': 'x'*40, 'text': 'Nogova'}) self.assertEqual(status, 200) key = json.loads(payload)['clip'] status, audio = request('GET', '/clips/' + key + '.b64') self.assertEqual(status, 200) self.assertEqual(base64.b64decode(audio), clip()) self.assertNotIn(b'x'*40, payload + audio) self.assertEqual(calls, ['Nogova']) def test_veteran_style_and_dynamic_text(self): session = Session() tool.synthesize(session, tool.ENDPOINT, 'Algenib', 'en-US', 'This is Nogova. Current weather: rain.', 'gemini-2.5-flash-tts', tool.VETERAN_PROMPT) request = session.calls[0][1]['json'] self.assertIn('older war veteran', request['input']['prompt']) self.assertIn('Nogova', request['input']['text']) self.assertEqual(request['voice']['modelName'], 'gemini-2.5-flash-tts') def test_authenticated_cache_and_billing_bounds(self): with tempfile.TemporaryDirectory() as directory: calls, now = [], [100.0] def generate(text): calls.append(text) return clip() token = 'x' * 40 cache = tool.BriefingCache(directory, token, generate, 'veteran', lambda: now[0]) for request in [{}, {'token': 'wrong', 'text': 'hello'}]: with self.assertRaises(PermissionError): cache.prepare(request) self.assertEqual(calls, []) request = {'token': token, 'text': 'Nogova, clear skies.'} key = cache.prepare(request) self.assertEqual(cache.prepare(request), key) self.assertEqual(cache.read(key), clip()) self.assertEqual(len(calls), 1) with self.assertRaises(BlockingIOError): cache.prepare({'token': token, 'text': 'Chernarus, rain.'}) now[0] += 61 changed = cache.prepare({'token': token, 'text': 'Chernarus, rain.'}) self.assertNotEqual(changed, key) self.assertEqual(len(calls), 2) for invalid in ['../welcome', 'z' * 64, key + '?token=secret']: with self.assertRaises(FileNotFoundError): cache.read(invalid) # Service restart still reuses completed clips, with no Google call. restarted = tool.BriefingCache(directory, token, generate, 'veteran') self.assertEqual(restarted.prepare(request), key) self.assertEqual(len(calls), 2) for text in ['', 'a' * 801, 'line\nline', 42]: with self.assertRaises(ValueError): cache.prepare({'token': token, 'text': text}) def test_cache_failure_is_not_retried_immediately(self): with tempfile.TemporaryDirectory() as directory: calls = [] def failed(text): calls.append(text) raise RuntimeError('provider failed') cache = tool.BriefingCache(directory, 'x'*40, failed, 'veteran') request = {'token': 'x'*40, 'text': 'hello'} with self.assertRaises(RuntimeError): cache.prepare(request) with self.assertRaises(BlockingIOError): cache.prepare(request) self.assertEqual(calls, ['hello']) self.assertEqual(list(Path(directory).iterdir()), []) def test_provider_failure_is_not_retried(self): class FailedSession(Session): def post(self, endpoint, **kwargs): response = super().post(endpoint, **kwargs) response.status_code = 503 return response session = FailedSession() with self.assertRaises(RuntimeError): tool.synthesize(session, tool.ENDPOINT, 'en-GB-Neural2-B', 'en-GB') self.assertEqual(len(session.calls), 1) def test_google_request_and_response(self): session = Session() self.assertEqual(tool.synthesize(session, tool.ENDPOINT, 'en-GB-Neural2-B', 'en-GB'), clip()) endpoint, args = session.calls[0] self.assertEqual(endpoint, tool.ENDPOINT) self.assertEqual(args['json']['input']['text'], tool.WELCOME) self.assertEqual(args['json']['audioConfig']['audioEncoding'], 'LINEAR16') self.assertFalse(args['allow_redirects']) self.assertEqual(len(session.calls), 1) def test_no_insecure_or_secret_url(self): for endpoint in ['http://example.com', 'https://user:secret@example.com/', 'https://example.com/?key=secret']: session = Session() with self.assertRaises(ValueError): tool.synthesize(session, endpoint, '', '') self.assertEqual(session.calls, []) def test_bad_clip_does_not_replace_cached_clip(self): with tempfile.TemporaryDirectory() as directory: path = Path(directory)/'welcome.wav' tool.save_clip(path, clip()) for bad in [b'error', clip()[:-10], bytes(tool.MAX_AUDIO+1)]: with self.assertRaises(ValueError): tool.save_clip(path, bad) self.assertEqual(path.read_bytes(), clip()) if __name__ == '__main__': unittest.main()