# -*- coding: utf-8 -*- """验证「配对码被占用」的处理。 用户的要求: 一个设备设了 789113,另一台也想设 789113 -> 不行, 要弹通知说「该配对码已被使用,请换一个配对码」。 分两层验证: 1. 中转服务器层:第二台设备报同一个码 -> 必须被拒,且带 codeTaken 2. 被控端层:RelayLink 必须抛 RelayCodeTaken,并且**调用 on_alert 弹通知** 而且不能反复重试刷屏(换了码才继续) """ from __future__ import annotations import json import os import shutil import subprocess import sys import tempfile import time import urllib.error import urllib.request from pathlib import Path _UA = {"User-Agent": "LanRemote/1.4.0"} ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "windows")) RELAY_PORT = 8093 RELAY_URL = f"http://127.0.0.1:{RELAY_PORT}" CODE = "789113" OTHER_CODE = "654321" passed = 0 failed = 0 def check(name, ok, detail=""): global passed, failed if ok: passed += 1 print(" [PASS] " + name) else: failed += 1 print(" [FAIL] " + name + " " + str(detail)) def post(path, payload): body = json.dumps(payload, ensure_ascii=False).encode("utf-8") req = urllib.request.Request(RELAY_URL + path, data=body, method="POST", headers=_UA) try: with urllib.request.urlopen(req, timeout=15) as resp: return resp.status, json.loads(resp.read().decode("utf-8")) except urllib.error.HTTPError as exc: return exc.code, json.loads(exc.read().decode("utf-8", "replace")) def main() -> int: work = Path(tempfile.mkdtemp(prefix="code_conflict_")) relay = None try: # ---------- 起本地中转 ---------- relay_data = work / "relaydata" relay_data.mkdir(parents=True, exist_ok=True) env = dict(os.environ) env.update({ "RELAY_PORT": str(RELAY_PORT), "RELAY_BIND": "127.0.0.1", "RELAY_DATA": str(relay_data), "RELAY_POLL": "6", "PYTHONIOENCODING": "utf-8", "PYTHONUNBUFFERED": "1", }) relay = subprocess.Popen( [sys.executable, "-u", str(ROOT / "relay" / "relay.py")], env=env, stdout=open(work / "relay.log", "wb"), stderr=subprocess.STDOUT) up = False for _ in range(40): time.sleep(0.5) try: with urllib.request.urlopen( urllib.request.Request(RELAY_URL + "/v1/health", headers=_UA), timeout=2) as resp: if resp.status == 200: up = True break except Exception: # noqa: BLE001 continue check("本地中转起来了", up) if not up: print((work / "relay.log").read_text(encoding="utf-8", errors="replace")[-600:]) return 1 print() print("一、中转服务器层") status, first = post("/v1/agent/register", { "deviceId": "pc-A", "code": CODE, "name": "电脑A", "version": "1.4.0"}) check("第一台设备登记成功", first.get("ok") is True, first) check("第一台拿到了令牌", bool(first.get("token"))) check("第一台的码就是它设的", first.get("code") == CODE, first.get("code")) status, second = post("/v1/agent/register", { "deviceId": "pc-B", "code": CODE, "name": "电脑B", "version": "1.4.0"}) check("第二台用同一个码被拒", second.get("ok") is False, second) check("返回了 codeTaken 标记", second.get("codeTaken") is True, second) check("错误说明了要换码", "已被使用" in str(second.get("error")) and "换一个" in str(second.get("error")), second.get("error")) check("第二台没拿到令牌", not second.get("token"), second) status, third = post("/v1/agent/register", { "deviceId": "pc-B", "code": OTHER_CODE, "name": "电脑B", "version": "1.4.0"}) check("第二台换个码就能登记", third.get("ok") is True, third) check("它拿到的是新码", third.get("code") == OTHER_CODE, third.get("code")) # 单设备单码仍然有效:A 报新码应被改回 status, again = post("/v1/agent/register", { "deviceId": "pc-A", "code": "111222", "name": "电脑A", "version": "1.4.0"}) check("A 报新码被改回原来的(单设备单码)", again.get("code") == CODE, again.get("code")) print() print("二、被控端层(RelayLink 要弹通知)") from lanremote.config import Store from lanremote.relay import RelayCodeTaken, RelayLink alerts = [] def on_alert(title, text): alerts.append((title, text)) # A 的 store store_a = Store(work / "store_a") store_a.set_pairing_code(CODE) store_a.set("server_name", "电脑A") store_a.set("relay_device_id", "pc-A") link_a = RelayLink(store_a, log=lambda _m: None, on_alert=on_alert) link_a.start(RELAY_URL) time.sleep(4) check("A 连上并登记", link_a.status().get("online") is True or link_a.token != "", link_a.status()) check("A 没有冲突", not link_a.status().get("codeConflict"), link_a.status().get("codeConflict")) # B 故意用同一个码 store_b = Store(work / "store_b") store_b.set_pairing_code(CODE) store_b.set("server_name", "电脑B") store_b.set("relay_device_id", "pc-B") alerts.clear() link_b = RelayLink(store_b, log=lambda _m: None, on_alert=on_alert) link_b.start(RELAY_URL) time.sleep(6) check("B 弹了通知", len(alerts) >= 1, f"{len(alerts)} 次: {alerts[:2]}") if alerts: title, text = alerts[0] check("通知标题说了配对码", "配对码" in title, title) check("通知正文说要换码", "已被使用" in text and "换" in text, text) check("B 停在冲突状态(没有反复重试刷屏)", bool(link_b.status().get("codeConflict")), link_b.status().get("codeConflict")) check("B 的冲突信息里带着要换码的提示", "已被使用" in str(link_b.status().get("codeConflict")), link_b.status().get("codeConflict")) count_before = len(alerts) time.sleep(6) check("同一个码不会反复弹通知", len(alerts) == count_before, f"又弹了 {len(alerts) - count_before} 次") print() print("三、用户换码之后要能自己恢复") store_b.set_pairing_code(OTHER_CODE) time.sleep(8) check("换了码之后 B 连上了", link_b.status().get("online") is True or link_b.token != "", link_b.status()) check("冲突标记被清掉", not link_b.status().get("codeConflict"), link_b.status().get("codeConflict")) for link in (link_a, link_b): try: link.stop() except Exception: # noqa: BLE001 pass print() print("=" * 62) print(f"通过 {passed} 项,失败 {failed} 项") return 0 if failed == 0 else 1 finally: if relay is not None: try: relay.terminate() relay.wait(timeout=8) except Exception: # noqa: BLE001 try: relay.kill() except Exception: # noqa: BLE001 pass time.sleep(0.5) shutil.rmtree(work, ignore_errors=True) if __name__ == "__main__": raise SystemExit(main())