Files
workphone-sdk/sdk/tests/test_connection_provider.py

141 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
连接方案可切换驱动层 · 离线回归(不依赖真机/8899
验证:
- 默认 4 类方案存在jiqing 默认 + aochuang/legacy 内置 + 自定义可注册)
- 三级开关global/project/device解析优先级
- 禁用方案回退 jiqing
- 自定义方案注册 / 删除 / 内置不可删
- http 驱动字段映射body_map / query_map
注意:本测试为离线 mock 回归,按真机铁律仅作回归,不替代真机 E2E。
"""
import os
import sys
import tempfile
import importlib
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "app"))
@pytest.fixture()
def mgr(tmp_path, monkeypatch):
import services.connection_provider as cp
importlib.reload(cp)
# 重定向持久化到临时目录
monkeypatch.setattr(cp, "_DATA_DIR", tmp_path)
monkeypatch.setattr(cp, "_CONFIG_PATH", tmp_path / "connection_providers.json")
m = cp.ConnectionProviderManager()
m.load(force=True)
return m, cp
def test_builtin_providers(mgr):
m, cp = mgr
ids = {p["id"] for p in m.list_providers()}
assert {"jiqing", "aochuang", "legacy"} <= ids
# 默认 active = jiqing
assert m.resolve_active_id() == "jiqing"
jq = m.get("jiqing")
assert jq.kind == "native" and jq.enabled and jq.builtin
def test_switch_requires_enabled(mgr):
m, cp = mgr
# aochuang 默认未启用 → 切换应拒绝
with pytest.raises(ValueError):
m.switch("aochuang", scope=cp.SCOPE_GLOBAL)
# 启用后可切
m.register({"id": "aochuang", "enabled": True, "base_url": "https://007.example/api"})
res = m.switch("aochuang", scope=cp.SCOPE_GLOBAL)
assert res["switched_to"] == "aochuang"
assert m.resolve_active_id() == "aochuang"
def test_scope_priority(mgr):
m, cp = mgr
m.register({"id": "aochuang", "enabled": True, "base_url": "https://007.example/api"})
m.register({"id": "legacy", "enabled": True, "base_url": "https://legacy.example"})
m.switch("aochuang", scope=cp.SCOPE_GLOBAL)
m.switch("legacy", scope=cp.SCOPE_PROJECT, project_id="cunkebao")
m.switch("jiqing", scope=cp.SCOPE_DEVICE, device_id="dev1")
# 设备级最高
assert m.resolve_active_id(device_id="dev1", project_id="cunkebao") == "jiqing"
# 项目级次之
assert m.resolve_active_id(project_id="cunkebao") == "legacy"
# 全局兜底
assert m.resolve_active_id() == "aochuang"
def test_disabled_active_falls_back(mgr):
m, cp = mgr
m.register({"id": "aochuang", "enabled": True, "base_url": "https://007.example/api"})
m.switch("aochuang", scope=cp.SCOPE_GLOBAL)
assert m.resolve_active_id() == "aochuang"
# 再禁用 aochuang → 回退 jiqing
m.register({"id": "aochuang", "enabled": False})
assert m.resolve_active_id() == "jiqing"
def test_custom_register_and_remove(mgr):
m, cp = mgr
meta = m.register({
"id": "custom_x", "name": "我的自建方案", "kind": "http", "enabled": True,
"base_url": "https://x.example", "endpoints": {"send_message": {"method": "POST", "path": "/s"}},
})
assert meta["id"] == "custom_x" and meta["builtin"] is False
assert "send_message" in meta["capabilities"]
m.remove("custom_x")
assert m.get("custom_x") is None
# 内置不可删
with pytest.raises(ValueError):
m.remove("jiqing")
def test_http_field_mapping(mgr):
m, cp = mgr
prov = cp.HttpConnectionProvider({
"id": "t", "kind": "http", "base_url": "https://x",
"endpoints": {
"send_message": {"method": "POST", "path": "/send",
"body_map": {"device_id": "deviceId", "to_id": "wxid", "content": "content"}},
},
})
src = {"device_id": "d1", "platform": "wechat", "to_id": "u1", "content": "hi", "msg_type": "text"}
body = prov._apply_map(src, {"device_id": "deviceId", "to_id": "wxid", "content": "content"})
assert body["deviceId"] == "d1" and body["wxid"] == "u1" and body["content"] == "hi"
# 未映射字段透传保留
assert body.get("platform") == "wechat"
def test_persistence_roundtrip(mgr):
m, cp = mgr
m.register({"id": "aochuang", "enabled": True, "base_url": "https://007.example/api"})
m.switch("aochuang", scope=cp.SCOPE_GLOBAL)
# 新实例从磁盘加载
m2 = cp.ConnectionProviderManager()
m2.load(force=True)
assert m2.resolve_active_id() == "aochuang"
def test_http_execute_unknown_action_501(mgr):
import asyncio
m, cp = mgr
prov = cp.HttpConnectionProvider({"id": "t", "kind": "http", "base_url": "https://x", "endpoints": {}})
res = asyncio.run(prov.execute("d1", "wechat", "send_message", {"to_id": "u", "content": "c"}))
assert res["code"] == 501 and res["success"] is False
def test_jiqing_execute_offline_payload(mgr):
"""jiqing 原生方案在无 WS 设备时应返回离线 503复用 device_transport不 mock 成功)。"""
import asyncio
m, cp = mgr
prov = m.get("jiqing")
res = asyncio.run(prov.execute("nonexist_dev", "wechat", "send_message",
{"to_id": "filehelper", "content": "hi"}))
assert res.get("provider") == "jiqing"
assert res.get("success") is False # 离线设备,不得假成功