"""Regression tests for Client.Python-003: stream timeout-kwarg fallback. `stream_events_raw` and `query_active_alarms` must tolerate a fake/older stub that does not accept a ``timeout`` keyword argument, matching the fallback already present in `galaxy.watch_deploy_events` and the unary `_unary` helper. """ from __future__ import annotations from typing import Any import pytest from mxgateway import ClientOptions, GatewayClient from mxgateway.generated import mxaccess_gateway_pb2 as pb class _NoTimeoutStream: """Sync-callable unary-stream fake that rejects a ``timeout`` kwarg.""" def __init__(self, replies: list[Any]) -> None: self._replies = list(replies) self.requests: list[Any] = [] self.metadata: tuple[tuple[str, str], ...] | None = None self.cancelled = False def __call__( self, request: Any, *, metadata: tuple[tuple[str, str], ...], ) -> "_NoTimeoutStream": self.requests.append(request) self.metadata = metadata return self def __aiter__(self) -> "_NoTimeoutStream": return self async def __anext__(self) -> Any: if not self._replies: raise StopAsyncIteration return self._replies.pop(0) def cancel(self) -> None: self.cancelled = True class _NoTimeoutStubStreamEvents: def __init__(self, stream: _NoTimeoutStream) -> None: self.StreamEvents = stream class _NoTimeoutStubQueryAlarms: def __init__(self, stream: _NoTimeoutStream) -> None: self.QueryActiveAlarms = stream @pytest.mark.asyncio async def test_stream_events_raw_falls_back_when_stub_rejects_timeout() -> None: stream = _NoTimeoutStream( [pb.MxEvent(session_id="session-1", worker_sequence=1)], ) client = await GatewayClient.connect( ClientOptions(endpoint="fake", plaintext=True, stream_timeout=5.0), stub=_NoTimeoutStubStreamEvents(stream), ) received = [ event async for event in client.stream_events_raw( pb.StreamEventsRequest(session_id="session-1"), ) ] assert len(received) == 1 assert received[0].worker_sequence == 1 @pytest.mark.asyncio async def test_query_active_alarms_falls_back_when_stub_rejects_timeout() -> None: stream = _NoTimeoutStream( [pb.ActiveAlarmSnapshot(alarm_full_reference="Tank01.Level.HiHi")], ) client = await GatewayClient.connect( ClientOptions(endpoint="fake", plaintext=True, stream_timeout=5.0), stub=_NoTimeoutStubQueryAlarms(stream), ) received = [ snapshot async for snapshot in client.query_active_alarms( pb.QueryActiveAlarmsRequest(session_id="session-1"), ) ] assert len(received) == 1 assert received[0].alarm_full_reference == "Tank01.Level.HiHi" @pytest.mark.asyncio async def test_stream_events_raw_still_passes_timeout_to_capable_stub() -> None: """A stub that accepts ``timeout`` must still receive the configured value.""" captured: dict[str, Any] = {} class _CapableStream(_NoTimeoutStream): def __call__( # type: ignore[override] self, request: Any, *, metadata: tuple[tuple[str, str], ...], timeout: float | None = None, ) -> "_CapableStream": captured["timeout"] = timeout return super().__call__(request, metadata=metadata) stream = _CapableStream([pb.MxEvent(session_id="session-1", worker_sequence=9)]) client = await GatewayClient.connect( ClientOptions(endpoint="fake", plaintext=True, stream_timeout=7.5), stub=_NoTimeoutStubStreamEvents(stream), ) received = [ event async for event in client.stream_events_raw( pb.StreamEventsRequest(session_id="session-1"), ) ] assert len(received) == 1 assert captured["timeout"] == 7.5