新增了Twitch、Telegram、Discord、Slack、Mattermost、WeChat、Zalo等多渠道的单元测试用例,覆盖了令牌处理、速率限制、消息去重、会话解析、格式转换、安全策略等模块 同时在测试配置中添加了测试用的OpenAI API密钥环境变量
179 lines
5.5 KiB
Python
179 lines
5.5 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from yuxi.channels.adapters.zalo_user.gateway import ZaloGateway, GatewayContext, get_zalo_gateway
|
|
|
|
|
|
class FakeBridge:
|
|
base_url = "http://fake:5556"
|
|
_started = False
|
|
|
|
async def start(self):
|
|
self._started = True
|
|
|
|
async def stop(self):
|
|
self._started = False
|
|
|
|
async def start_qr_login(self):
|
|
return {"qr_url": "http://qr.example.com", "qr_id": "qr_001", "expires_in": 120}
|
|
|
|
async def check_login_status(self, qr_id=""):
|
|
return {"status": "success", "logged_in": True}
|
|
|
|
async def check_raw_login_status(self):
|
|
return {"logged_in": True}
|
|
|
|
def get_websocket_url(self):
|
|
return "ws://fake:5556/ws"
|
|
|
|
|
|
class FakeBridgeDelayLogin:
|
|
base_url = "http://fake:5556"
|
|
_started = False
|
|
|
|
async def start(self):
|
|
self._started = True
|
|
|
|
async def stop(self):
|
|
self._started = False
|
|
|
|
async def start_qr_login(self):
|
|
return {"qr_url": "http://qr.example.com", "qr_id": "qr_002", "expires_in": 5}
|
|
|
|
async def check_login_status(self, qr_id=""):
|
|
return {"status": "pending"}
|
|
|
|
async def check_raw_login_status(self):
|
|
return {"logged_in": False}
|
|
|
|
def get_websocket_url(self):
|
|
return "ws://fake:5556/ws"
|
|
|
|
|
|
class TestZaloGateway:
|
|
@pytest.fixture
|
|
def gateway(self):
|
|
gw = ZaloGateway()
|
|
yield gw
|
|
|
|
def test_singleton(self):
|
|
gw1 = get_zalo_gateway()
|
|
gw2 = get_zalo_gateway()
|
|
assert gw1 is gw2
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_start_account(self):
|
|
gw = ZaloGateway()
|
|
config = {"bridge_url": "http://test:5556", "profile": "test"}
|
|
ctx = await gw.start_account("test", config)
|
|
assert isinstance(ctx, GatewayContext)
|
|
assert ctx.bridge_url == "http://test:5556"
|
|
assert "test" in gw.list_accounts()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_start_duplicate_account(self):
|
|
gw = ZaloGateway()
|
|
config = {"bridge_url": "http://test:5556"}
|
|
ctx1 = await gw.start_account("dup", config)
|
|
ctx2 = await gw.start_account("dup", config)
|
|
assert ctx1 is ctx2
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_accounts(self):
|
|
gw = ZaloGateway()
|
|
config = {"bridge_url": "http://test:5556"}
|
|
await gw.start_account("a", config)
|
|
await gw.start_account("b", config)
|
|
accounts = gw.list_accounts()
|
|
assert sorted(accounts) == ["a", "b"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_logout_account(self):
|
|
gw = ZaloGateway()
|
|
config = {"bridge_url": "http://test:5556"}
|
|
await gw.start_account("test_logout", config)
|
|
result = await gw.logout_account("test_logout")
|
|
assert result["status"] == "logged_out"
|
|
assert "test_logout" not in gw.list_accounts()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_logout_nonexistent(self):
|
|
gw = ZaloGateway()
|
|
result = await gw.logout_account("nonexistent")
|
|
assert result["status"] == "not_found"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_login_qr_start_not_started(self):
|
|
gw = ZaloGateway()
|
|
with pytest.raises(ValueError, match="not started"):
|
|
await gw.login_with_qr_start("nonexistent")
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_login_qr_wait_not_started(self):
|
|
gw = ZaloGateway()
|
|
with pytest.raises(ValueError, match="not started"):
|
|
await gw.login_with_qr_wait("nonexistent")
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_login_qr_start(self):
|
|
gw = ZaloGateway()
|
|
config = {"bridge_url": "http://test:5556"}
|
|
await gw.start_account("qr_test", config)
|
|
|
|
ctx = gw._accounts["qr_test"]
|
|
fake_bridge = FakeBridge()
|
|
ctx.bridge = fake_bridge
|
|
ctx.login_flow._bridge = fake_bridge
|
|
|
|
result = await gw.login_with_qr_start("qr_test")
|
|
assert result["qr_url"] == "http://qr.example.com"
|
|
assert result["qr_id"] == "qr_001"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_login_qr_wait_success(self):
|
|
gw = ZaloGateway()
|
|
config = {"bridge_url": "http://test:5556"}
|
|
await gw.start_account("qr_wait", config)
|
|
|
|
ctx = gw._accounts["qr_wait"]
|
|
fake_bridge = FakeBridge()
|
|
ctx.bridge = fake_bridge
|
|
ctx.login_flow._bridge = fake_bridge
|
|
|
|
result = await gw.login_with_qr_wait("qr_wait")
|
|
assert result["logged_in"] is True
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_login_qr_wait_timeout(self):
|
|
gw = ZaloGateway()
|
|
config = {"bridge_url": "http://test:5556"}
|
|
await gw.start_account("qr_timeout", config)
|
|
|
|
ctx = gw._accounts["qr_timeout"]
|
|
fake_bridge = FakeBridgeDelayLogin()
|
|
ctx.bridge = fake_bridge
|
|
ctx.login_flow._bridge = fake_bridge
|
|
|
|
result = await gw.login_with_qr_wait("qr_timeout", poll_interval=0.5, timeout=1.0)
|
|
assert result["logged_in"] is False
|
|
assert result["status"] == "error"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_shutdown(self):
|
|
gw = ZaloGateway()
|
|
config = {"bridge_url": "http://test:5556"}
|
|
await gw.start_account("s1", config)
|
|
await gw.start_account("s2", config)
|
|
await gw.shutdown()
|
|
assert len(gw.list_accounts()) == 0
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_gateway_context_defaults(self):
|
|
ctx = GatewayContext(
|
|
bridge_url="http://test:5556",
|
|
config={"profile": "default"},
|
|
)
|
|
assert ctx.bridge is None
|
|
assert ctx.login_flow is None
|
|
assert ctx.bridge_url == "http://test:5556" |