Files
easy-tdx/tests/unit/test_async_transport.py
GitHubandClaude Opus 4.7 4820b4a049 feat: v1.1.0 - MAC protocol, CLI tool, extended markets, unified client
- Add MacClient/AsyncMacClient with full MAC protocol support (quotes, kline
  with adjustment, tick charts, transactions, boards, capital flow, auction,
  unusual, symbol info, server info)
- Add MacExClient/AsyncMacExClient for extended markets (HK, US, futures)
- Add UnifiedTdxClient auto-routing between A-share and extended markets
- Add `easy-tdx` CLI tool with JSON default output, Agent-friendly
- Add field bitmap protocol for custom quote field selection
- Fix quote-list missing fields (default to BASIC+VOLUME preset)
- Add config.py with centralized host management and auto-discovery
- Add 50+ examples covering all APIs (01-20)
- Rewrite README with CLI-first, Agent-friendly documentation
- Bump version to 1.1.0

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 22:44:45 +08:00

140 lines
4.7 KiB
Python

"""异步 transport 回归测试。"""
from __future__ import annotations
import asyncio
import struct
import time
from easy_tdx import AsyncTdxClient, Market
from easy_tdx.commands.security_count import GetSecurityCountCmd
from easy_tdx.commands.setup import SETUP_COMMANDS
from easy_tdx.exceptions import TdxConnectionError
def _pack_frame(body: bytes) -> bytes:
return struct.pack("<IIIHH", 0, 0, 0, len(body), len(body)) + body
def test_async_client_serializes_concurrent_calls() -> None:
request_len = len(GetSecurityCountCmd(Market.SH).build_request())
async def handle(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
try:
for setup_cmd in SETUP_COMMANDS:
await reader.readexactly(len(setup_cmd))
writer.write(_pack_frame(b""))
await writer.drain()
await reader.readexactly(request_len)
writer.write(_pack_frame(struct.pack("<H", 5)))
await writer.drain()
await reader.readexactly(request_len)
writer.write(_pack_frame(struct.pack("<H", 6)))
await writer.drain()
finally:
writer.close()
await writer.wait_closed()
async def main() -> None:
server = await asyncio.start_server(handle, "127.0.0.1", 0)
port = server.sockets[0].getsockname()[1]
try:
client = AsyncTdxClient("127.0.0.1", port=port, timeout=0.2)
await client.connect()
try:
sh_count, sz_count = await asyncio.gather(
client.get_security_count(Market.SH),
client.get_security_count(Market.SZ),
)
finally:
await client.close()
finally:
server.close()
await server.wait_closed()
assert sh_count == 5
assert sz_count == 6
asyncio.run(main())
def test_async_client_auto_reconnect() -> None:
request_len = len(GetSecurityCountCmd(Market.SH).build_request())
connection_ids: list[int] = []
async def handle(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
connection_ids.append(len(connection_ids) + 1)
connection_id = connection_ids[-1]
try:
for setup_cmd in SETUP_COMMANDS:
await reader.readexactly(len(setup_cmd))
writer.write(_pack_frame(b""))
await writer.drain()
await reader.readexactly(request_len)
writer.write(_pack_frame(struct.pack("<H", 10 + connection_id)))
await writer.drain()
finally:
writer.close()
await writer.wait_closed()
async def main() -> None:
server = await asyncio.start_server(handle, "127.0.0.1", 0)
port = server.sockets[0].getsockname()[1]
try:
client = AsyncTdxClient("127.0.0.1", port=port, timeout=0.2)
first = await client.get_security_count(Market.SH)
second = await client.get_security_count(Market.SH)
await client.close()
finally:
server.close()
await server.wait_closed()
assert first == 11
assert second == 12
assert len(connection_ids) == 2
asyncio.run(main())
def test_async_client_request_timeout() -> None:
request_len = len(GetSecurityCountCmd(Market.SH).build_request())
async def handle(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
try:
for setup_cmd in SETUP_COMMANDS:
await reader.readexactly(len(setup_cmd))
writer.write(_pack_frame(b""))
await writer.drain()
await reader.readexactly(request_len)
await asyncio.sleep(1.0)
finally:
writer.close()
await writer.wait_closed()
async def main() -> None:
server = await asyncio.start_server(handle, "127.0.0.1", 0)
port = server.sockets[0].getsockname()[1]
try:
client = AsyncTdxClient("127.0.0.1", port=port, timeout=0.05, auto_reconnect=False)
await client.connect()
t0 = time.monotonic()
try:
await client.get_security_count(Market.SH)
except TdxConnectionError as exc:
elapsed = time.monotonic() - t0
assert "超时" in str(exc) or "timed out" in str(exc)
assert elapsed < 0.3
else: # pragma: no cover - 防御性断言
raise AssertionError("expected timeout")
finally:
await client.close()
finally:
server.close()
await server.wait_closed()
asyncio.run(main())