chore: init qmt_bridge repo (HTTP+WS bridge, MCP endpoint, docs, references)
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""End-to-end test: WS connect -> subscribe -> QMT push -> WS receive."""
|
||||
import sys, io, os, json, time, socket, struct, base64, urllib.request
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "src"))
|
||||
if sys.stdout is None:
|
||||
sys.stdout = io.StringIO()
|
||||
|
||||
import bridge_util, bridge_http_server, bridge_subscription
|
||||
|
||||
class FakeCtx:
|
||||
def __init__(self):
|
||||
self.subs = {}
|
||||
self._next = 1
|
||||
self.callback = None
|
||||
def subscribe_whole_quote(self, codes, callback=None):
|
||||
h = self._next
|
||||
self._next += 1
|
||||
self.subs[h] = list(codes)
|
||||
self.callback = callback
|
||||
return h
|
||||
def unsubscribe_quote(self, h):
|
||||
self.subs.pop(h, None)
|
||||
|
||||
fake = FakeCtx()
|
||||
bridge_util.CTX = fake
|
||||
bridge_http_server.PORT = 18691
|
||||
bridge_http_server.TOKEN = ""
|
||||
bridge_http_server.start_server()
|
||||
time.sleep(0.5)
|
||||
|
||||
def ws_connect(port):
|
||||
s = socket.create_connection(("127.0.0.1", port), timeout=5)
|
||||
key = base64.b64encode(os.urandom(16)).decode()
|
||||
req = ("GET /ws HTTP/1.1\r\nHost: 127.0.0.1:%d\r\nUpgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\nSec-WebSocket-Key: %s\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n\r\n" % (port, key))
|
||||
s.sendall(req.encode())
|
||||
resp = s.recv(4096)
|
||||
assert b"101" in resp, resp[:100]
|
||||
return s
|
||||
|
||||
def ws_recv(s, timeout=5):
|
||||
s.settimeout(timeout)
|
||||
hdr = s.recv(2)
|
||||
if len(hdr) < 2:
|
||||
return None
|
||||
b1 = hdr[1]
|
||||
ln = b1 & 0x7F
|
||||
if ln == 126:
|
||||
ln = struct.unpack(">H", s.recv(2))[0]
|
||||
payload = b""
|
||||
while len(payload) < ln:
|
||||
chunk = s.recv(ln - len(payload))
|
||||
if not chunk:
|
||||
break
|
||||
payload += chunk
|
||||
return payload.decode("utf-8", "ignore")
|
||||
|
||||
# 1. connect WS
|
||||
ws = ws_connect(18691)
|
||||
print("ws connected")
|
||||
|
||||
# 2. subscribe whole via HTTP
|
||||
req = urllib.request.Request(
|
||||
"http://127.0.0.1:18691/data/subscribe",
|
||||
data=json.dumps({"type": "whole", "codes": ["SH", "SZ"]}).encode(),
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=5) as r:
|
||||
print("subscribe:", r.read().decode())
|
||||
|
||||
# 3. trigger QMT push
|
||||
time.sleep(0.3)
|
||||
fake.callback({"600000.SH": {"lastPrice": 10.5, "volume": 200}})
|
||||
|
||||
# 4. WS should receive whole message
|
||||
msg = ws_recv(ws)
|
||||
print("ws received:", msg)
|
||||
assert msg is not None, "ws received nothing!"
|
||||
parsed = json.loads(msg)
|
||||
assert parsed.get("type") == "whole", "expected type=whole, got %s" % parsed.get("type")
|
||||
assert "600000.SH" in parsed.get("data", {}), "data missing code"
|
||||
print("=== WS PUSH CHAIN OK ===")
|
||||
ws.close()
|
||||
bridge_http_server.stop_server()
|
||||
Reference in New Issue
Block a user