11"""Unit tests for ResponseExecutor WebSocket support."""
22
3- from __future__ import annotations
4-
5- from typing import Any , cast
6- from unittest .mock import AsyncMock , MagicMock , patch
7-
8- import pytest
9- from src .connectors .contracts import ConnectorRequestContext
10- from src .connectors .openai_codex .executor import _CodexTransportAdapter
11- from src .core .common .exceptions import AuthenticationError
12- from src .core .domain .responses import ProcessedResponse , StreamingResponseHandle
3+ from __future__ import annotations
4+
5+ from typing import Any , cast
6+ from unittest .mock import AsyncMock , MagicMock , patch
7+
8+ import pytest
9+ from src .connectors .contracts import ConnectorRequestContext
10+ from src .connectors .openai_codex .executor import _CodexTransportAdapter
11+ from src .core .common .exceptions import AuthenticationError
12+ from src .core .domain .responses import ProcessedResponse , StreamingResponseHandle
1313
1414
1515@pytest .mark .asyncio
@@ -62,13 +62,13 @@ async def mock_send_response_create(*args, **kwargs):
6262 async for chunk in handle .iterator :
6363 chunks .append (chunk )
6464
65- # Verify chunks
66- assert len (chunks ) == 2
67- # Websocket transport adapter yields ProcessedResponse objects directly
68- first_content = cast (dict [str , Any ], chunks [0 ].content )
69- second_content = cast (dict [str , Any ], chunks [1 ].content )
70- assert cast (dict [str , Any ], first_content ["message" ])["content" ] == "Hello"
71- assert cast (dict [str , Any ], second_content ["message" ])["content" ] == "World"
65+ # Verify chunks
66+ assert len (chunks ) == 2
67+ # Websocket transport adapter yields ProcessedResponse objects directly
68+ first_content = cast (dict [str , Any ], chunks [0 ].content )
69+ second_content = cast (dict [str , Any ], chunks [1 ].content )
70+ assert cast (dict [str , Any ], first_content ["message" ])["content" ] == "Hello"
71+ assert cast (dict [str , Any ], second_content ["message" ])["content" ] == "World"
7272
7373 async def test_recreates_websocket_client_when_auth_token_changes (self ) -> None :
7474 """Auth refresh retries must not reuse stale WebSocket credentials."""
@@ -117,17 +117,17 @@ async def test_initiate_websocket_streaming_no_auth(self) -> None:
117117
118118 url = "https://chatgpt.com/backend-api/codex/responses"
119119 payload = {"model" : "gpt-4" , "input" : []}
120- headers : dict [str , str ] = {} # No authorization header
120+ headers : dict [str , str ] = {} # No authorization header
121121 session_id = "test_session"
122122
123123 with pytest .raises (AuthenticationError , match = "No API key" ):
124124 await adapter .initiate_streaming_request (url , payload , headers , session_id )
125125
126- async def test_http_fallback_when_websocket_disabled (self ) -> None :
127- """Test fallback to HTTP/SSE when WebSocket is disabled."""
128- mock_connector = MagicMock ()
129- mock_connector ._handle_streaming_response = AsyncMock (
130- return_value = StreamingResponseHandle (
126+ async def test_http_fallback_when_websocket_disabled (self ) -> None :
127+ """Test fallback to HTTP/SSE when WebSocket is disabled."""
128+ mock_connector = MagicMock ()
129+ mock_connector ._handle_streaming_response = AsyncMock (
130+ return_value = StreamingResponseHandle (
131131 iterator = AsyncMock (), headers = {}, cancel_callback = AsyncMock ()
132132 )
133133 )
@@ -143,54 +143,59 @@ async def test_http_fallback_when_websocket_disabled(self) -> None:
143143 url , payload , headers , session_id
144144 )
145145
146- # Verify HTTP/SSE method was called
147- mock_connector ._handle_streaming_response .assert_called_once_with (
148- url , payload , headers , session_id , "responses"
149- )
150- assert isinstance (handle , StreamingResponseHandle )
151-
152- async def test_http_fallback_accepts_transport_metadata_kwargs (self ) -> None :
153- """Transport adapter should accept the executor's keyword metadata contract."""
154- mock_connector = MagicMock ()
155- mock_connector ._handle_streaming_response = AsyncMock (
156- return_value = StreamingResponseHandle (
157- iterator = AsyncMock (), headers = {}, cancel_callback = AsyncMock ()
158- )
159- )
160-
161- adapter = _CodexTransportAdapter (mock_connector , use_websocket = False )
162-
163- url = "https://chatgpt.com/backend-api/codex/responses"
164- payload = {"model" : "gpt-4" , "input" : []}
165- headers = {"Authorization" : "Bearer test_key" }
166- session_id = "test_session"
167- request_context = ConnectorRequestContext (
168- request_id = "req-1" ,
169- session_id = "sess-1" ,
170- client_host = "127.0.0.1" ,
171- extensions = {},
172- )
173-
174- handle = await adapter .initiate_streaming_request (
175- url ,
176- payload ,
177- headers ,
178- session_id ,
179- context = request_context ,
180- backend = "openai-codex" ,
181- model = "gpt-4" ,
182- key_name = "openai-codex" ,
183- )
184-
185- mock_connector ._handle_streaming_response .assert_called_once_with (
186- url , payload , headers , session_id , "responses"
187- )
188- assert isinstance (handle , StreamingResponseHandle )
189-
190- async def test_cleanup_closes_websocket_client (self ) -> None :
191- """Test cleanup properly disconnects WebSocket client."""
192- mock_connector = MagicMock ()
193- adapter = _CodexTransportAdapter (mock_connector , use_websocket = True )
146+ # Verify HTTP/SSE method was called with wire-capture context slot
147+ mock_connector ._handle_streaming_response .assert_called_once_with (
148+ url , payload , headers , session_id , "responses" , context = None
149+ )
150+ assert isinstance (handle , StreamingResponseHandle )
151+
152+ async def test_http_fallback_accepts_transport_metadata_kwargs (self ) -> None :
153+ """Transport adapter should accept the executor's keyword metadata contract."""
154+ mock_connector = MagicMock ()
155+ mock_connector ._handle_streaming_response = AsyncMock (
156+ return_value = StreamingResponseHandle (
157+ iterator = AsyncMock (), headers = {}, cancel_callback = AsyncMock ()
158+ )
159+ )
160+
161+ adapter = _CodexTransportAdapter (mock_connector , use_websocket = False )
162+
163+ url = "https://chatgpt.com/backend-api/codex/responses"
164+ payload = {"model" : "gpt-4" , "input" : []}
165+ headers = {"Authorization" : "Bearer test_key" }
166+ session_id = "test_session"
167+ request_context = ConnectorRequestContext (
168+ request_id = "req-1" ,
169+ session_id = "sess-1" ,
170+ client_host = "127.0.0.1" ,
171+ extensions = {},
172+ )
173+
174+ handle = await adapter .initiate_streaming_request (
175+ url ,
176+ payload ,
177+ headers ,
178+ session_id ,
179+ context = request_context ,
180+ backend = "openai-codex" ,
181+ model = "gpt-4" ,
182+ key_name = "openai-codex" ,
183+ )
184+
185+ mock_connector ._handle_streaming_response .assert_called_once_with (
186+ url ,
187+ payload ,
188+ headers ,
189+ session_id ,
190+ "responses" ,
191+ context = request_context ,
192+ )
193+ assert isinstance (handle , StreamingResponseHandle )
194+
195+ async def test_cleanup_closes_websocket_client (self ) -> None :
196+ """Test cleanup properly disconnects WebSocket client."""
197+ mock_connector = MagicMock ()
198+ adapter = _CodexTransportAdapter (mock_connector , use_websocket = True )
194199
195200 # Create mock WebSocket client
196201 mock_ws_client = AsyncMock ()
0 commit comments