新增了Twitch、Telegram、Discord、Slack、Mattermost、WeChat、Zalo等多渠道的单元测试用例,覆盖了令牌处理、速率限制、消息去重、会话解析、格式转换、安全策略等模块 同时在测试配置中添加了测试用的OpenAI API密钥环境变量
217 lines
7.4 KiB
Python
217 lines
7.4 KiB
Python
from __future__ import annotations
|
|
|
|
import time
|
|
|
|
import pytest
|
|
|
|
from yuxi.channels.adapters.zalo_user.constants import ZaloCredentialStage
|
|
from yuxi.channels.adapters.zalo_user.credentials import CredentialManager, _compute_signature
|
|
|
|
|
|
class TestCredentialStageTransitions:
|
|
def test_initial_uninitialized(self):
|
|
mgr = CredentialManager()
|
|
assert mgr.stage == ZaloCredentialStage.UNINITIALIZED
|
|
assert mgr.is_authenticated is False
|
|
|
|
def test_mark_qr_pending(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_qr_pending("qr_test_123")
|
|
assert mgr.stage == ZaloCredentialStage.QR_PENDING
|
|
|
|
def test_mark_qr_scanned(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_qr_pending("qr_1")
|
|
mgr.mark_qr_scanned()
|
|
assert mgr.stage == ZaloCredentialStage.QR_SCANNED
|
|
|
|
def test_mark_qr_declined(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_qr_declined()
|
|
assert mgr.stage == ZaloCredentialStage.QR_DECLINED
|
|
assert mgr.error_message is not None
|
|
assert "declined" in mgr.error_message.lower()
|
|
|
|
def test_mark_qr_expired(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_qr_expired()
|
|
assert mgr.stage == ZaloCredentialStage.QR_EXPIRED
|
|
assert mgr.error_message is not None
|
|
assert "expired" in mgr.error_message.lower()
|
|
|
|
def test_mark_connected_no_data(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected()
|
|
assert mgr.stage == ZaloCredentialStage.CONNECTED
|
|
assert mgr.is_authenticated is True
|
|
|
|
def test_mark_connected_with_data(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123", "display_name": "Test"})
|
|
assert mgr.stage == ZaloCredentialStage.CONNECTED
|
|
assert mgr.credential_signature is not None
|
|
|
|
def test_mark_logged_out(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
mgr.mark_logged_out()
|
|
assert mgr.stage == ZaloCredentialStage.LOGGED_OUT
|
|
assert mgr.is_authenticated is False
|
|
assert mgr.credential_signature is None
|
|
|
|
def test_mark_error(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_error("something went wrong")
|
|
assert mgr.stage == ZaloCredentialStage.ERROR
|
|
assert mgr.error_message == "something went wrong"
|
|
|
|
def test_full_lifecycle(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_qr_pending("qr_001")
|
|
assert mgr.stage == ZaloCredentialStage.QR_PENDING
|
|
mgr.mark_qr_scanned()
|
|
assert mgr.stage == ZaloCredentialStage.QR_SCANNED
|
|
mgr.mark_connected({"user_id": "u1"})
|
|
assert mgr.stage == ZaloCredentialStage.CONNECTED
|
|
mgr.mark_logged_out()
|
|
assert mgr.stage == ZaloCredentialStage.LOGGED_OUT
|
|
|
|
|
|
class TestCredentialSignature:
|
|
def test_no_signature_initially(self):
|
|
mgr = CredentialManager()
|
|
assert mgr.credential_signature is None
|
|
|
|
def test_signature_after_connect(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
assert mgr.credential_signature is not None
|
|
|
|
def test_signature_cleared_on_logout(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
mgr.mark_logged_out()
|
|
assert mgr.credential_signature is None
|
|
|
|
def test_same_data_same_signature(self):
|
|
sig1 = _compute_signature({"user_id": "123", "name": "A"})
|
|
sig2 = _compute_signature({"name": "A", "user_id": "123"})
|
|
assert sig1 == sig2
|
|
|
|
def test_different_data_different_signature(self):
|
|
sig1 = _compute_signature({"user_id": "123"})
|
|
sig2 = _compute_signature({"user_id": "456"})
|
|
assert sig1 != sig2
|
|
|
|
def test_has_credential_changed_no_signature(self):
|
|
mgr = CredentialManager()
|
|
assert mgr.has_credential_changed({"user_id": "123"}) is True
|
|
|
|
def test_has_credential_changed_same_data(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
assert mgr.has_credential_changed({"user_id": "123"}) is False
|
|
|
|
def test_has_credential_changed_different_data(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
assert mgr.has_credential_changed({"user_id": "456"}) is True
|
|
|
|
def test_update_credentials_same_data(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
assert mgr.update_credentials({"user_id": "123"}) is False
|
|
|
|
def test_update_credentials_different_data(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
old_sig = mgr.credential_signature
|
|
changed = mgr.update_credentials({"user_id": "456"})
|
|
assert changed is True
|
|
assert mgr.credential_signature != old_sig
|
|
|
|
|
|
class TestSessionStatus:
|
|
@pytest.mark.asyncio
|
|
async def test_session_alive(self):
|
|
class FakeBridge:
|
|
async def check_login_status(self):
|
|
return {"logged_in": True}
|
|
|
|
mgr = CredentialManager()
|
|
result = await mgr.check_session_status(FakeBridge())
|
|
assert result is True
|
|
assert mgr.stage == ZaloCredentialStage.CONNECTED
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_session_dead(self):
|
|
class FakeBridge:
|
|
async def check_login_status(self):
|
|
return {"logged_in": False}
|
|
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
result = await mgr.check_session_status(FakeBridge())
|
|
assert result is False
|
|
assert mgr.stage == ZaloCredentialStage.LOGGED_OUT
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_session_check_error(self):
|
|
class FakeBridge:
|
|
async def check_login_status(self):
|
|
raise RuntimeError("network error")
|
|
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
result = await mgr.check_session_status(FakeBridge())
|
|
assert result is True
|
|
|
|
|
|
class TestLogout:
|
|
@pytest.mark.asyncio
|
|
async def test_logout_success(self):
|
|
class FakeBridge:
|
|
async def post(self, path, json=None, **kwargs):
|
|
class FakeResp:
|
|
@staticmethod
|
|
def json():
|
|
return {"success": True}
|
|
return FakeResp()
|
|
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
result = await mgr.logout(FakeBridge())
|
|
assert result is True
|
|
assert mgr.stage == ZaloCredentialStage.LOGGED_OUT
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_logout_error_graceful(self):
|
|
class FakeBridge:
|
|
async def post(self, path, json=None, **kwargs):
|
|
raise RuntimeError("network down")
|
|
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
result = await mgr.logout(FakeBridge())
|
|
assert result is False
|
|
assert mgr.stage == ZaloCredentialStage.LOGGED_OUT
|
|
|
|
|
|
class TestReset:
|
|
def test_reset(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
mgr.reset()
|
|
assert mgr.stage == ZaloCredentialStage.UNINITIALIZED
|
|
assert mgr.credential_signature is None
|
|
|
|
|
|
class TestToDict:
|
|
def test_to_dict(self):
|
|
mgr = CredentialManager()
|
|
mgr.mark_connected({"user_id": "123"})
|
|
d = mgr.to_dict()
|
|
assert d["stage"] == "connected"
|
|
assert d["is_authenticated"] is True
|
|
assert "last_check_time" in d
|
|
assert "error" in d |