Files
qmt_bridge/tests/test_subscription.py
T

119 lines
4.2 KiB
Python

# -*- coding: utf-8 -*-
"""Local mock test for subscription endpoints (subscribe/unsubscribe/tick)."""
import sys, io, os, json, time, threading, urllib.request, urllib.error
if sys.stdout is None:
sys.stdout = io.StringIO()
if sys.stderr is None:
sys.stderr = io.StringIO()
RESULT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "sub_test.txt")
SRC = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "src")
sys.path.insert(0, SRC)
def log(msg):
with open(RESULT, "a") as f:
f.write(msg + "\n")
import bridge_util, bridge_http_server, bridge_ws_server, bridge_subscription
# ---- Fake QMT ContextInfo ----
class FakeCtx:
def __init__(self):
self.subs = {} # handle -> codes
self._next_handle = 1
self.pushed = [] # records of (handle, data) pushed
def subscribe_whole_quote(self, codes, callback=None):
handle = self._next_handle
self._next_handle += 1
self.subs[handle] = list(codes)
# remember callback so tests can trigger pushes
self.callback = callback
return handle
def unsubscribe_quote(self, handle):
self.subs.pop(handle, None)
def get_full_tick(self, codes):
return {c: {"lastPrice": 10.0, "volume": 100} for c in codes}
fake = FakeCtx()
bridge_util.CTX = fake
bridge_util.ACCOUNT = "TEST"
bridge_http_server.PORT = 18690
bridge_http_server.TOKEN = ""
bridge_http_server.start_server()
time.sleep(0.6)
BASE = "http://127.0.0.1:18690"
def post(path, obj):
data = json.dumps(obj).encode("utf-8")
req = urllib.request.Request(BASE + path, data=data, method="POST",
headers={"Content-Type": "application/json"})
try:
with urllib.request.urlopen(req, timeout=5) as r:
return r.status, json.loads(r.read().decode())
except urllib.error.HTTPError as e:
try:
return e.code, json.loads(e.read().decode())
except Exception:
return e.code, None
def get(path):
try:
with urllib.request.urlopen(BASE + path, timeout=5) as r:
return r.status, json.loads(r.read().decode())
except urllib.error.HTTPError as e:
try:
return e.code, json.loads(e.read().decode())
except Exception:
return e.code, None
# ---- Tests ----
# 1. subscribe whole
st, body = post("/data/subscribe", {"type": "whole", "codes": ["SH", "SZ"]})
log("1. subscribe whole -> HTTP %d body=%s" % (st, json.dumps(body, ensure_ascii=False)))
sub_id = body.get("sub_id") if body else None
assert st == 200 and sub_id is not None, "subscribe failed"
assert fake.subs, "QMT subscribe not created"
log(" QMT subs: %s" % fake.subs)
# 2. simulate QMT push -> should broadcast via ws (no ws client, no crash)
if fake.callback:
fake.callback({"600000.SH": {"lastPrice": 10.1}})
log("2. simulated QMT push OK (no crash)")
# 3. subscribe invalid type
st, body = post("/data/subscribe", {"type": "bogus", "codes": ["SH"]})
log("3. subscribe bogus type -> HTTP %d body=%s" % (st, json.dumps(body, ensure_ascii=False)))
assert st == 400, "bogus type should be 400"
# 4. subscribe missing codes
st, body = post("/data/subscribe", {"type": "whole"})
log("4. subscribe no codes -> HTTP %d body=%s" % (st, json.dumps(body, ensure_ascii=False)))
assert st == 400, "missing codes should be 400"
# 5. unsubscribe
st, body = post("/data/unsubscribe", {"sub_id": sub_id})
log("5. unsubscribe -> HTTP %d body=%s" % (st, json.dumps(body, ensure_ascii=False)))
assert st == 200, "unsubscribe failed"
assert not fake.subs, "QMT sub should be removed"
# 6. unsubscribe unknown
st, body = post("/data/unsubscribe", {"sub_id": 999999})
log("6. unsubscribe unknown -> HTTP %d body=%s" % (st, json.dumps(body, ensure_ascii=False)))
assert st == 404, "unknown sub should be 404"
# 7. GET /data/tick
st, body = get("/data/tick?codes=600000.SH,000001.SZ")
log("7. tick -> HTTP %d body=%s" % (st, json.dumps(body, ensure_ascii=False)))
assert st == 200 and "600000.SH" in body.get("data", {}), "tick failed"
# 8. tick missing codes
st, body = get("/data/tick")
log("8. tick no codes -> HTTP %d body=%s" % (st, json.dumps(body, ensure_ascii=False)))
assert st == 400, "tick no codes should be 400"
bridge_http_server.stop_server()
log("=== done ===")