''' IB broker event-relay regressions. ''' from collections.abc import AsyncIterator from typing import Any import pytest import trio from piker.brokers.ib.broker import deliver_trade_events from piker.clearing import OrderDialogs from piker.clearing._messages import BrokerdError @pytest.mark.parametrize( ('error', 'expected_reason'), [ ( '[code 104] connection failed', ' connection failed', ), ( '[Errno 111] Connect call failed', ' Connect call failed', ), ( 'plain API error', 'plain API error', ), ], ) def test_trade_error_strings_are_relayed( error: str, expected_reason: str, ) -> None: ''' Raw IB API error strings must not kill the trade-event relay. ``Client.inline_errors()`` can emit bracketed ``[code N]`` and ``[Errno N]`` strings instead of structured error dictionaries. The string branch called ``str.partition()`` without a separator, raising ``TypeError`` before EMS received the error and terminating all later trade-event delivery. Feed both documented bracket forms and an unprefixed control through ``deliver_trade_events()``. The exact emitted reason and unknown request ID prove each event crossed the real relay path without crashing or being misclassified. ''' class FakeEmsStream: ''' Collect messages sent to the EMS stream. ''' def __init__(self) -> None: ''' Initialize the captured message sequence. ''' self.messages: list[Any] = [] async def send(self, msg: Any) -> None: ''' Capture one relayed broker message. ''' self.messages.append(msg) async def events() -> AsyncIterator[tuple[str, str]]: ''' Emit the raw IB error under test. ''' yield 'error', error async def main() -> list[Any]: ''' Drive the broker relay to input exhaustion. ''' ems_stream = FakeEmsStream() await deliver_trade_events( events(), ems_stream, accounts_def={}, proxies={}, ledgers={}, tables={}, flows=OrderDialogs(_flows={}), ) return ems_stream.messages messages = trio.run(main) assert len(messages) == 1 msg = messages[0] assert isinstance(msg, BrokerdError) assert msg.reqid == '' assert msg.reason == expected_reason