forked from sammchardy/python-binance
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_ws_api.py
More file actions
244 lines (194 loc) · 8.55 KB
/
Copy pathtest_ws_api.py
File metadata and controls
244 lines (194 loc) · 8.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
import json
import sys
import re
import pytest
import asyncio
from binance import AsyncClient
from binance.exceptions import BinanceAPIException, BinanceWebsocketUnableToConnect
from binance.ws.constants import WSListenerState
from .test_get_order_book import assert_ob
from .conftest import proxy
@pytest.mark.skipif(sys.version_info < (3, 8), reason="websockets_proxy Python 3.8+")
@pytest.mark.asyncio
async def test_ws_api_public_endpoint(clientAsync):
"""Test normal order book request"""
order_book = await clientAsync.ws_get_order_book(symbol="BTCUSDT")
assert_ob(order_book)
@pytest.mark.skipif(sys.version_info < (3, 8), reason="websockets_proxy Python 3.8+")
@pytest.mark.asyncio
async def test_ws_api_private_endpoint(clientAsync):
"""Test normal order book request"""
orders = await clientAsync.ws_get_all_orders(symbol="BTCUSDT")
@pytest.mark.skipif(sys.version_info < (3, 8), reason="websockets_proxy Python 3.8+")
@pytest.mark.asyncio
async def test_ws_futures_public_endpoint(futuresClientAsync):
"""Test normal order book request"""
order_book = await futuresClientAsync.ws_futures_get_order_book(symbol="BTCUSDT")
assert_ob(order_book)
@pytest.mark.skipif(sys.version_info < (3, 8), reason="websockets_proxy Python 3.8+")
@pytest.mark.asyncio
async def test_ws_futures_private_endpoint(futuresClientAsync):
"""Test normal order book request"""
await futuresClientAsync.ws_futures_v2_account_position(symbol="BTCUSDT")
@pytest.mark.skipif(sys.version_info < (3, 8), reason="websockets_proxy Python 3.8+")
@pytest.mark.asyncio
async def test_ws_get_symbol_ticker(clientAsync):
"""Test symbol ticker request"""
ticker = await clientAsync.ws_get_symbol_ticker(symbol="BTCUSDT")
assert "symbol" in ticker
assert ticker["symbol"] == "BTCUSDT"
@pytest.mark.asyncio
async def test_invalid_request(clientAsync):
"""Test error handling for invalid symbol"""
with pytest.raises(
BinanceAPIException,
match=re.escape(
"APIError(code=-1100): Illegal characters found in parameter 'symbol'; legal range is '^[A-Z0-9-_.]{1,20}$'."
),
):
await clientAsync.ws_get_order_book(symbol="send error")
@pytest.mark.asyncio
async def test_connection_handling(clientAsync):
"""Test connection handling and reconnection"""
# First request should establish connection
await clientAsync.ws_get_order_book(symbol="BTCUSDT")
assert clientAsync.ws_api.ws_state == WSListenerState.STREAMING
# Force connection close
await clientAsync.close_connection()
assert clientAsync.ws_api.ws_state == WSListenerState.EXITING
assert clientAsync.ws_api.ws is None
# Next request should reconnect
order_book = await clientAsync.ws_get_order_book(symbol="LTCUSDT")
assert_ob(order_book)
assert clientAsync.ws_api.ws_state == WSListenerState.STREAMING
@pytest.mark.asyncio
async def test_timeout_handling(clientAsync):
"""Test request timeout handling"""
# Set very short timeout to force timeout error
original_timeout = clientAsync.ws_api.TIMEOUT
clientAsync.ws_api.TIMEOUT = 0.0001
try:
with pytest.raises(BinanceWebsocketUnableToConnect, match="Request timed out"):
await clientAsync.ws_get_order_book(symbol="BTCUSDT")
finally:
clientAsync.ws_api.TIMEOUT = original_timeout
@pytest.mark.asyncio
async def test_multiple_requests(clientAsync):
"""Test multiple concurrent requests"""
symbols = ["BTCUSDT", "ETHUSDT", "BNBUSDT"]
tasks = [clientAsync.ws_get_order_book(symbol=symbol) for symbol in symbols]
results = await asyncio.gather(*tasks)
assert len(results) == len(symbols)
for result in results:
assert_ob(result)
@pytest.mark.asyncio
async def test_testnet_url():
"""Test testnet URL configuration"""
testnet_client = AsyncClient(testnet=True, https_proxy=proxy)
try:
assert testnet_client.ws_api._url == testnet_client.WS_API_TESTNET_URL
order_book = await testnet_client.ws_get_order_book(symbol="BTCUSDT")
assert_ob(order_book)
finally:
await testnet_client.close_connection()
@pytest.mark.asyncio
async def test_message_handling(clientAsync):
"""Test message handling with various message types"""
try:
# Test valid message
future = asyncio.Future()
clientAsync.ws_api._responses["123"] = future
valid_msg = {"id": "123", "status": 200, "result": {"test": "data"}}
clientAsync.ws_api._handle_message(json.dumps(valid_msg))
result = await clientAsync.ws_api._responses["123"]
assert result == valid_msg
finally:
await clientAsync.close_connection()
@pytest.mark.asyncio
async def test_message_handling_raise_exception(clientAsync):
try:
with pytest.raises(BinanceAPIException):
future = asyncio.Future()
clientAsync.ws_api._responses["123"] = future
valid_msg = {"id": "123", "status": 400, "error": {"code": "0", "msg": "error message"}}
clientAsync.ws_api._handle_message(json.dumps(valid_msg))
await future
finally:
await clientAsync.close_connection()
@pytest.mark.asyncio
async def test_message_handling_raise_exception_without_id(clientAsync):
try:
with pytest.raises(BinanceAPIException):
future = asyncio.Future()
clientAsync.ws_api._responses["123"] = future
valid_msg = {"id": "123", "status": 400, "error": {"code": "0", "msg": "error message"}}
clientAsync.ws_api._handle_message(json.dumps(valid_msg))
await future
finally:
await clientAsync.close_connection()
@pytest.mark.asyncio
async def test_message_handling_invalid_json(clientAsync):
try:
with pytest.raises(json.JSONDecodeError):
clientAsync.ws_api._handle_message("invalid json")
with pytest.raises(json.JSONDecodeError):
clientAsync.ws_api._handle_message("invalid json")
finally:
# Ensure cleanup
await clientAsync.close_connection()
@pytest.mark.asyncio(scope="function")
async def test_connection_failure(clientAsync):
"""Test handling of connection failures"""
# Set invalid URL
clientAsync.ws_api._url = "wss://invalid.url"
with pytest.raises(BinanceWebsocketUnableToConnect, match="Connection failed"):
await clientAsync.ws_get_order_book(symbol="BTCUSDT")
@pytest.mark.asyncio(scope="function")
async def test_cleanup_on_exit(clientAsync):
"""Test cleanup of resources on exit"""
# Create some pending requests
future = asyncio.Future()
clientAsync.ws_api._responses["test"] = future
# Close connection
await clientAsync.close_connection()
# Check cleanup
assert "test" not in clientAsync.ws_api._responses
assert future.exception() is not None
@pytest.mark.skipif(sys.version_info < (3, 8), reason="websockets_proxy Python 3.8+")
@pytest.mark.asyncio
async def test_ws_queue_overflow(clientAsync):
"""WebSocket API should not overflow queue"""
#
original_size = clientAsync.ws_api.max_queue_size
clientAsync.ws_api.max_queue_size = 1
try:
# Request multiple order books concurrently
symbols = ["BTCUSDT", "ETHUSDT", "BNBUSDT"]
tasks = [clientAsync.ws_get_order_book(symbol=symbol) for symbol in symbols]
# Execute all requests concurrently and wait for results
results = await asyncio.gather(*tasks, return_exceptions=True)
# Check that we got valid responses or expected overflow errors
valid_responses = [r for r in results if not isinstance(r, Exception)]
assert len(valid_responses) == len(symbols), "Should get at least one valid response"
for result in valid_responses:
assert_ob(result)
finally:
# Restore original queue size
clientAsync.ws_api.MAX_QUEUE_SIZE = original_size
@pytest.mark.skipif(sys.version_info < (3, 8), reason="websockets_proxy Python 3.8+")
@pytest.mark.asyncio
async def test_ws_api_with_stream(clientAsync):
"""Test combining WebSocket API requests with stream listening"""
from binance import BinanceSocketManager
# Create socket manager and trade socket
bm = BinanceSocketManager(clientAsync)
ts = bm.trade_socket("BTCUSDT")
async with ts:
# Make WS API request while stream is active
order_book = await clientAsync.ws_get_order_book(symbol="BTCUSDT")
assert_ob(order_book)
# Verify we can still receive stream data
trade = await ts.recv()
assert "s" in trade # Symbol
assert "p" in trade # Price
assert "q" in trade # Quantity