From 22b1f38d6b99813b387ff7f9c3a4fa034f41914c Mon Sep 17 00:00:00 2001 From: endolith Date: Sat, 25 Oct 2025 13:18:59 -0400 Subject: [PATCH 1/2] Remove trailing whitespace (to make subsequent commits easier to read) --- tests/test_interpreter.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/test_interpreter.py b/tests/test_interpreter.py index cf71e95863..d37a6035ea 100644 --- a/tests/test_interpreter.py +++ b/tests/test_interpreter.py @@ -37,9 +37,9 @@ def test_hallucinations(): assert chunk.get("content") == "22" break - code = """{ - "language": "python", - "code": "10+12" + code = """{ + "language": "python", + "code": "10+12" }""" interpreter.messages = [ @@ -50,9 +50,9 @@ def test_hallucinations(): assert chunk.get("content") == "22" break - code = """functions.execute({ - "language": "python", - "code": "10+12" + code = """functions.execute({ + "language": "python", + "code": "10+12" })""" interpreter.messages = [ @@ -1198,7 +1198,7 @@ def test_math(): order_of_operations_message = f""" Please perform the calculation `{n1} + {n2} * ({n1} - {n2}) / ({n2} + {n1})` then reply with just the answer, nothing else. No confirmation. No explanation. No words. Do not use commas. Do not show your work. Just return the result of the calculation. Do not introduce the results with a phrase like \"The result of the calculation is...\" or \"The answer is...\" - + Round to 2 decimal places. """.strip() @@ -1215,20 +1215,20 @@ def test_break_execution(): """ code = r"""print("starting") -import time -import os - +import time +import os + # Always create a fresh file open('numbers.txt', 'w').close() - -# Open the file in append mode -with open('numbers.txt', 'a+') as f: - # Loop through the numbers 1 to 5 - for i in [1,2,3,4,5]: - # Print the number - print("adding", i, "to file") - # Append the number to the file - f.write(str(i) + '\n') + +# Open the file in append mode +with open('numbers.txt', 'a+') as f: + # Loop through the numbers 1 to 5 + for i in [1,2,3,4,5]: + # Print the number + print("adding", i, "to file") + # Append the number to the file + f.write(str(i) + '\n') # Wait for 0.5 second print("starting to sleep") time.sleep(1) From a1a0f49af9d8249467a0a22e4ddec0ca9f62f942 Mon Sep 17 00:00:00 2001 From: endolith Date: Sat, 25 Oct 2025 15:32:44 -0400 Subject: [PATCH 2/2] Fix infinite WebSocket loops in test_server causing CI timeouts Replace 6 infinite while True loops with retry-limited helper function. PROBLEM: - GitHub Actions tests were timing out after 6 hours on PRs - PRs don't have access to GitHub secrets(?), causing authentication failures - test_server() contained 6 infinite while True loops which waited for 'complete' status messages that never arrived - Server never sends 'complete' status on auth failure, causing indefinite hangs - No retry limits or timeouts caused indefinite blocking SOLUTION: - Add wait_for_websocket_complete() helper function with max 5 attempts - Replace all 6 infinite loops with calls to helper function - Fail fast with descriptive error messages instead of hanging This prevents 6-hour CI timeouts and provides clear debugging information when WebSocket communication fails. --- tests/test_interpreter.py | 134 +++++++++++--------------------------- 1 file changed, 37 insertions(+), 97 deletions(-) diff --git a/tests/test_interpreter.py b/tests/test_interpreter.py index d37a6035ea..eaae40ab1d 100644 --- a/tests/test_interpreter.py +++ b/tests/test_interpreter.py @@ -236,6 +236,37 @@ def run_server(): async_interpreter.server.run() +async def wait_for_websocket_complete(websocket, max_attempts=5): + """Wait for WebSocket 'complete' status message with retry limit.""" + + import asyncio + import json + + accumulated_content = "" + + for attempt in range(1, max_attempts + 1): + try: + message = await websocket.recv() + message_data = json.loads(message) + if "error" in message_data: + raise Exception(message_data["content"]) + print("Received from WebSocket:", message_data) + if type(message_data.get("content")) == str: + accumulated_content += message_data.get("content") + if message_data == { + "role": "server", + "type": "status", + "content": "complete", + }: + print("Received expected message from server") + return accumulated_content + except Exception as e: + print(f"WebSocket receive failed (attempt {attempt}/{max_attempts}): {e}") + await asyncio.sleep(1) + else: + raise Exception(f"Never received 'complete' status after {max_attempts} attempts") + + # @pytest.mark.skip(reason="Requires uvicorn, which we don't require by default") def test_server(): # Start the server in a new process @@ -299,22 +330,7 @@ async def test_fastapi_server(): print("WebSocket chunks sent") # Wait for a specific response - accumulated_content = "" - while True: - message = await websocket.recv() - message_data = json.loads(message) - if "error" in message_data: - raise Exception(message_data["content"]) - print("Received from WebSocket:", message_data) - if type(message_data.get("content")) == str: - accumulated_content += message_data.get("content") - if message_data == { - "role": "server", - "type": "status", - "content": "complete", - }: - print("Received expected message from server") - break + accumulated_content = await wait_for_websocket_complete(websocket) assert "crunk" in accumulated_content @@ -355,22 +371,7 @@ async def test_fastapi_server(): print("WebSocket chunks sent") # Wait for a specific response - accumulated_content = "" - while True: - message = await websocket.recv() - message_data = json.loads(message) - if "error" in message_data: - raise Exception(message_data["content"]) - print("Received from WebSocket:", message_data) - if message_data.get("content"): - accumulated_content += message_data.get("content") - if message_data == { - "role": "server", - "type": "status", - "content": "complete", - }: - print("Received expected message from server") - break + accumulated_content = await wait_for_websocket_complete(websocket) assert "barloney" in accumulated_content @@ -404,22 +405,7 @@ async def test_fastapi_server(): print("WebSocket chunks sent") # Wait for response - accumulated_content = "" - while True: - message = await websocket.recv() - message_data = json.loads(message) - if "error" in message_data: - raise Exception(message_data["content"]) - print("Received from WebSocket:", message_data) - if message_data.get("content"): - accumulated_content += message_data.get("content") - if message_data == { - "role": "server", - "type": "status", - "content": "complete", - }: - print("Received expected message from server") - break + accumulated_content = await wait_for_websocket_complete(websocket) time.sleep(5) @@ -454,23 +440,7 @@ async def test_fastapi_server(): ) # Wait for a specific response - accumulated_content = "" - while True: - message = await websocket.recv() - message_data = json.loads(message) - if "error" in message_data: - raise Exception(message_data["content"]) - print("Received from WebSocket:", message_data) - if message_data.get("content"): - if type(message_data.get("content")) == str: - accumulated_content += message_data.get("content") - if message_data == { - "role": "server", - "type": "status", - "content": "complete", - }: - print("Received expected message from server") - break + accumulated_content = await wait_for_websocket_complete(websocket) assert "18893094989" in accumulated_content.replace(",", "") @@ -525,22 +495,7 @@ async def test_fastapi_server(): print("WebSocket chunks sent") # Wait for response - accumulated_content = "" - while True: - message = await websocket.recv() - message_data = json.loads(message) - if "error" in message_data: - raise Exception(message_data["content"]) - print("Received from WebSocket:", message_data) - if type(message_data.get("content")) == str: - accumulated_content += message_data.get("content") - if message_data == { - "role": "server", - "type": "status", - "content": "complete", - }: - print("Received expected message from server") - break + accumulated_content = await wait_for_websocket_complete(websocket) # Get messages get_url = "http://localhost:8000/settings/messages" @@ -602,22 +557,7 @@ async def test_fastapi_server(): print("WebSocket chunks sent") # Wait for response - accumulated_content = "" - while True: - message = await websocket.recv() - message_data = json.loads(message) - if "error" in message_data: - raise Exception(message_data["content"]) - print("Received from WebSocket:", message_data) - if type(message_data.get("content")) == str: - accumulated_content += message_data.get("content") - if message_data == { - "role": "server", - "type": "status", - "content": "complete", - }: - print("Received expected message from server") - break + accumulated_content = await wait_for_websocket_complete(websocket) # Get messages get_url = "http://localhost:8000/settings/messages"