piker/tests/test_ib_broker.py

100 lines
2.5 KiB
Python

'''
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 == '<unknown>'
assert msg.reason == expected_reason