Merge branch 'tier2/phase2_4_5_call_site_completion_20260621' into tier2/code_path_audit_20260607

This commit is contained in:
ed
2026-06-22 00:47:33 -04:00
43 changed files with 17291 additions and 169 deletions
+15 -1
View File
@@ -19,9 +19,23 @@ from src.commands import registry
def test_palette_starts_hidden(live_gui: Any) -> None:
"""On startup, the palette should be closed."""
client = ApiHookClient()
# Force-close the palette first: live_gui is session-scoped so other
# tests may have left it open. The contract under test is that the
# palette IS closable via the callback, not that it happens to be
# closed at this moment. Resetting here makes the assertion meaningful
# without depending on test ordering.
client.push_event("custom_callback", {
"callback": "_toggle_command_palette",
"args": [],
})
deadline = time.time() + 2.0
while time.time() < deadline:
if client.get_value("show_command_palette") is False:
break
time.sleep(0.05)
state = client.get_value("show_command_palette")
assert state is not None, "show_command_palette should be a gettable field"
assert state is False, f"Palette should start hidden, got {state}"
assert state is False, f"Palette should be closable, got {state}"
def test_palette_toggles_via_callback(live_gui: Any) -> None:
+10 -4
View File
@@ -58,12 +58,12 @@ def test_gui2_click_hook_works(live_gui: Any) -> None:
time.sleep(1.5)
# Verify it was reset
assert client.get_value('ai_input') == ""
def test_gui2_custom_callback_hook_works(live_gui: Any) -> None:
"""
Tests that the 'custom_callback' GUI hook is correctly implemented.
Tests that the 'custom_callback' GUI hook is correctly implemented.
"""
client = ApiHookClient()
assert client.wait_for_server(timeout=10)
@@ -75,9 +75,15 @@ def test_gui2_custom_callback_hook_works(live_gui: Any) -> None:
}
response = client.post_gui(gui_data)
assert response == {'status': 'queued'}
time.sleep(1.5) # Give gui_2.py time to process its task queue
# Assert that the file WAS created and contains the correct data
# Poll for the callback to complete (avoids time.sleep race; per workflow anti-pattern)
temp_workspace_file = Path('tests/artifacts/temp_callback_output.txt')
deadline = time.time() + 10.0
while time.time() < deadline:
if temp_workspace_file.exists():
content = temp_workspace_file.read_text(encoding="utf-8")
if content == test_data:
return
time.sleep(0.1)
assert temp_workspace_file.exists(), f"Custom callback was NOT executed, or file path is wrong! Expected: {temp_workspace_file}"
with open(temp_workspace_file, "r") as f:
content = f.read()
+1 -2
View File
@@ -56,7 +56,6 @@ def test_logging_e2e(e2e_setup: Any) -> None:
assert session_dir.exists(), "New whitelisted session should have been kept"
# Extra check: Whitelisted sessions should be kept even if old
# Manually backdate the current session
registry.data[session_id]['start_time'] = (datetime.now() - timedelta(days=2)).isoformat()
registry.save_registry()
registry.set_session_start_time(session_id, datetime.now() - timedelta(days=2))
pruner.prune()
assert session_dir.exists(), "Whitelisted session should be kept even if it is old and small"
@@ -0,0 +1,70 @@
"""Regression test for the WebSocketServer.broadcast() runtime TypeError bug.
Phase 5 of any_type_componentization_20260621 changed
WebSocketServer.broadcast(channel, payload) -> broadcast(message: WebSocketMessage)
but did not update internal callers in src/app_controller.py + src/events.py.
This produced worker[queue_fallback] TypeError spam on the GUI thread.
This test catches the regression and is reused by code_path_audit_20260607
as a structural assertion.
CONVENTION: 1-space indentation. NO COMMENTS.
"""
from __future__ import annotations
import inspect
from pathlib import Path
from typing import Any
from src.api_hooks import WebSocketMessage, WebSocketServer
class _MockApp:
test_hooks_enabled: bool = True
def _make_server() -> WebSocketServer:
return WebSocketServer(_MockApp(), port=9001)
def test_websocket_server_broadcast_signature() -> None:
"""WebSocketServer.broadcast must accept a single WebSocketMessage argument (self + message)."""
sig = inspect.signature(WebSocketServer.broadcast)
params = list(sig.parameters.keys())
assert len(params) == 2, f"expected 2 params (self + message), got {len(params)}: {params}"
def test_websocket_server_broadcast_rejects_legacy_2arg_call() -> None:
"""Calling broadcast with 2 positional args (legacy signature) must raise TypeError."""
server = _make_server()
raised = False
try:
server.broadcast("channel", {"key": "value"})
except TypeError:
raised = True
assert raised, "broadcast should reject legacy 2-arg call"
def test_websocket_server_broadcast_accepts_websocket_message_instance() -> None:
"""The new signature accepts a WebSocketMessage instance (no-op when not started)."""
server = _make_server()
msg = WebSocketMessage(channel="test", payload={"key": "value"})
server.broadcast(msg)
def test_internal_callers_use_websocket_message_signature() -> None:
"""Grep all internal callers of broadcast() in src/ and assert they use the new signature."""
src_root = Path(__file__).resolve().parents[1] / "src"
legacy_sites: list[str] = []
for py_file in src_root.rglob("*.py"):
text = py_file.read_text(encoding="utf-8")
for lineno, line in enumerate(text.splitlines(), start=1):
if ".broadcast(" not in line:
continue
if "WebSocketMessage(" in line:
continue
if 'broadcast("' not in line and "broadcast('" not in line:
continue
rel = py_file.relative_to(src_root.parent)
legacy_sites.append(f"{rel}:{lineno}: {line.strip()}")
assert not legacy_sites, "legacy broadcast() callers found:\n" + "\n".join(legacy_sites)