"""Unit tests for anonymous number methods — login codes and session management."""
from unittest.mock import AsyncMock, patch
import pytest
from pyfragment import AnonymousNumberError, FragmentClient, LoginCodeResult, TerminateSessionsResult
from tests.shared import FAKE_HTML_NO_CODE, FAKE_HTML_WITH_CODE, FAKE_TERMINATE_HASH
# get_login_code mocked tests
@pytest.mark.asyncio
async def test_get_login_code_returns_code(client: FragmentClient) -> None:
with patch.object(client, "call", AsyncMock(return_value={"html": FAKE_HTML_WITH_CODE})):
result = await client.get_login_code("+1234567890")
assert isinstance(result, LoginCodeResult)
assert result.number == "+1234567890"
assert result.code == "12345"
assert result.active_sessions == 2 # 2
in the HTML
@pytest.mark.asyncio
async def test_get_login_code_no_pending_code(client: FragmentClient) -> None:
with patch.object(client, "call", AsyncMock(return_value={"html": FAKE_HTML_NO_CODE})):
result = await client.get_login_code("1234567890")
assert result.code is None
assert result.active_sessions == 1
@pytest.mark.asyncio
async def test_get_login_code_no_html_returns_none(client: FragmentClient) -> None:
with patch.object(client, "call", AsyncMock(return_value={})):
result = await client.get_login_code("+1234567890")
assert result.code is None
assert result.active_sessions == 0
# terminate_sessions mocked tests
@pytest.mark.asyncio
async def test_terminate_sessions_success(client: FragmentClient) -> None:
with patch.object(
client,
"call",
AsyncMock(
side_effect=[
{"terminate_hash": FAKE_TERMINATE_HASH}, # step 1: confirmation
{"msg": "All sessions terminated"}, # step 2: confirmed
]
),
):
result = await client.terminate_sessions("+1234567890")
assert isinstance(result, TerminateSessionsResult)
assert result.number == "+1234567890"
assert result.message == "All sessions terminated"
@pytest.mark.asyncio
async def test_terminate_sessions_not_owned_raises(client: FragmentClient) -> None:
with patch.object(client, "call", AsyncMock(return_value={})):
with pytest.raises(AnonymousNumberError, match="not associated"):
await client.terminate_sessions("+1234567890")
@pytest.mark.asyncio
async def test_terminate_sessions_api_error_raises(client: FragmentClient) -> None:
with patch.object(client, "call", AsyncMock(return_value={"error": "SESSION_ALREADY_TERMINATED"})):
with pytest.raises(AnonymousNumberError, match="SESSION_ALREADY_TERMINATED"):
await client.terminate_sessions("+1234567890")
@pytest.mark.asyncio
async def test_terminate_sessions_confirm_error_raises(client: FragmentClient) -> None:
with patch.object(
client,
"call",
AsyncMock(
side_effect=[
{"terminate_hash": FAKE_TERMINATE_HASH},
{"error": "INTERNAL_ERROR"},
]
),
):
with pytest.raises(AnonymousNumberError, match="INTERNAL_ERROR"):
await client.terminate_sessions("+1234567890")
# toggle_login_codes mocked tests
@pytest.mark.asyncio
async def test_toggle_login_codes_enable(client: FragmentClient) -> None:
mock_call = AsyncMock(return_value={"ok": True})
with patch.object(client, "call", mock_call):
await client.toggle_login_codes("+1234567890", can_receive=True)
call_data = mock_call.call_args[0][1]
assert call_data["can_receive"] == 1
@pytest.mark.asyncio
async def test_toggle_login_codes_disable(client: FragmentClient) -> None:
mock_call = AsyncMock(return_value={"ok": True})
with patch.object(client, "call", mock_call):
await client.toggle_login_codes("+1234567890", can_receive=False)
call_data = mock_call.call_args[0][1]
assert call_data["can_receive"] == 0
# strip_plus mocked tests
@pytest.mark.asyncio
async def test_get_login_code_strips_plus(client: FragmentClient) -> None:
"""Number passed with '+' is stripped before the API call."""
mock_call = AsyncMock(return_value={})
with patch.object(client, "call", mock_call):
await client.get_login_code("+1234567890")
call_data = mock_call.call_args[0][1]
assert call_data["number"] == "1234567890"