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"