Skip to content

Commit a19b291

Browse files
committed
test(ws): cover build_param + structured subscribe
Unit-test build_param for every format (forex SYMBOL, SYMBOL@exchange, ticker with/without optional currency@conversionBase, premium 7 tokens), the SYMBOL->symbol lowercase rule, and error cases (missing required, unknown token, partial optional group, None channel, mixing raw+tokens). Round-trip: structured == raw wire string. Integration: structured ws.ticker.subscribe(...) sends BTC-USDT@binance over a fake conn.
1 parent 852b648 commit a19b291

1 file changed

Lines changed: 148 additions & 1 deletion

File tree

tests/test_ws.py

Lines changed: 148 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
websockets = pytest.importorskip("websockets")
1515

1616
import datamaxi._ws_models as _ws_models # noqa: E402
17-
from datamaxi.aio.ws import AsyncDatamaxiWS # noqa: E402
17+
from datamaxi.aio.ws import AsyncDatamaxiWS, build_param # noqa: E402
1818
from datamaxi._ws_endpoints import WS_CHANNELS, WS_BASE_PATH # noqa: E402
1919
from datamaxi._ws_models import TickerMessage # noqa: E402
2020

@@ -281,3 +281,150 @@ async def run():
281281
assert len(seen) >= 2
282282
assert seen[0] == ["USD-KRW"]
283283
assert seen[1] == ["USD-KRW"] # `_open` replays sorted(self._active)
284+
285+
286+
# --- structured subscribe helpers: build_param unit tests ---
287+
288+
289+
def test_build_param_forex_single_token():
290+
# SYMBOL: no separator, one token; and the SYMBOL->symbol lowercase rule.
291+
assert build_param("SYMBOL", symbol="USD-KRW") == "USD-KRW"
292+
293+
294+
def test_build_param_symbol_exchange():
295+
assert (
296+
build_param("SYMBOL@exchange", symbol="BTC-USDT", exchange="binance")
297+
== "BTC-USDT@binance"
298+
)
299+
300+
301+
def test_build_param_ticker_without_optional_group():
302+
fmt = "SYMBOL@exchange[@currency@conversionBase]"
303+
assert build_param(fmt, symbol="BTC-USDT", exchange="binance") == "BTC-USDT@binance"
304+
305+
306+
def test_build_param_ticker_with_optional_group():
307+
fmt = "SYMBOL@exchange[@currency@conversionBase]"
308+
assert (
309+
build_param(
310+
fmt,
311+
symbol="BTC-USDT",
312+
exchange="binance",
313+
currency="KRW",
314+
conversionBase="USDT",
315+
)
316+
== "BTC-USDT@binance@KRW@USDT"
317+
)
318+
319+
320+
def test_build_param_premium_all_seven_tokens():
321+
fmt = "src:tgt:tokenId:srcQuote:tgtQuote:srcMkt:tgtMkt"
322+
assert (
323+
build_param(
324+
fmt,
325+
src="binance",
326+
tgt="upbit",
327+
tokenId="bitcoin",
328+
srcQuote="USDT",
329+
tgtQuote="KRW",
330+
srcMkt="spot",
331+
tgtMkt="spot",
332+
)
333+
== "binance:upbit:bitcoin:USDT:KRW:spot:spot"
334+
)
335+
336+
337+
def test_build_param_verbatim_token_not_lowercased():
338+
# tokenId is mixed-case, so it stays verbatim (not lowercased).
339+
with pytest.raises(ValueError):
340+
build_param(
341+
"src:tgt:tokenId:srcQuote:tgtQuote:srcMkt:tgtMkt",
342+
src="a",
343+
tgt="b",
344+
tokenid="c", # wrong: lowercased key
345+
srcQuote="d",
346+
tgtQuote="e",
347+
srcMkt="f",
348+
tgtMkt="g",
349+
)
350+
351+
352+
def test_build_param_none_channel_rejects_tokens():
353+
with pytest.raises(ValueError):
354+
build_param(None, symbol="X")
355+
356+
357+
def test_build_param_missing_required_token():
358+
with pytest.raises(ValueError) as ei:
359+
build_param("SYMBOL@exchange", symbol="BTC-USDT")
360+
assert "exchange" in str(ei.value)
361+
362+
363+
def test_build_param_unknown_token():
364+
with pytest.raises(ValueError) as ei:
365+
build_param("SYMBOL@exchange", symbol="BTC-USDT", exchange="binance", bogus="x")
366+
assert "bogus" in str(ei.value)
367+
368+
369+
def test_build_param_partial_optional_group_rejected():
370+
fmt = "SYMBOL@exchange[@currency@conversionBase]"
371+
with pytest.raises(ValueError):
372+
build_param(fmt, symbol="BTC-USDT", exchange="binance", currency="KRW")
373+
374+
375+
def test_build_param_roundtrip_matches_raw_ticker():
376+
fmt = WS_CHANNELS["/ticker/spot"]["param"]
377+
assert build_param(fmt, symbol="BTC-USDT", exchange="binance") == "BTC-USDT@binance"
378+
379+
380+
def test_build_param_roundtrip_matches_raw_premium():
381+
fmt = WS_CHANNELS["/premium"]["param"]
382+
assert (
383+
build_param(
384+
fmt,
385+
src="binance",
386+
tgt="upbit",
387+
tokenId="bitcoin",
388+
srcQuote="USDT",
389+
tgtQuote="KRW",
390+
srcMkt="spot",
391+
tgtMkt="spot",
392+
)
393+
== "binance:upbit:bitcoin:USDT:KRW:spot:spot"
394+
)
395+
396+
397+
def test_ws_structured_subscribe_produces_expected_wire_param():
398+
# A structured subscribe(...) call sends the same wire param as the raw form.
399+
async def handler(conn):
400+
async for raw in conn:
401+
m = json.loads(raw)
402+
if m.get("method") == "SUBSCRIBE":
403+
await conn.send(json.dumps({"result": m["params"], "id": m["id"]}))
404+
for p in m["params"]:
405+
sym, exch = p.split("@")[0], p.split("@")[1]
406+
await conn.send(
407+
json.dumps({"s": sym, "e": exch, "p": 1.0, "d": 1, "_param": p})
408+
)
409+
410+
async def run():
411+
async with _serve(handler) as server:
412+
async with AsyncDatamaxiWS(
413+
api_key="k", ws_url=f"ws://localhost:{_port(server)}"
414+
) as ws:
415+
stream = await ws.ticker.subscribe(
416+
symbol="BTC-USDT", exchange="binance", market="spot"
417+
)
418+
return await _first(stream)
419+
420+
msg = _run(run())
421+
assert msg["_param"] == "BTC-USDT@binance"
422+
423+
424+
def test_ws_structured_subscribe_mixing_raw_and_tokens_raises():
425+
async def run():
426+
async with AsyncDatamaxiWS(api_key="k", ws_url="ws://localhost:1") as ws:
427+
await ws.forex.subscribe("USD-KRW", symbol="USD-KRW")
428+
429+
with pytest.raises(ValueError):
430+
_run(run())

0 commit comments

Comments
 (0)