from __future__ import annotations from unittest.mock import AsyncMock, MagicMock, patch import pytest from yuxi.channels.adapters.nostr.nip05 import verify_nip05 class AsyncContextManagerMock: def __init__(self, return_value=None): self.return_value = return_value async def __aenter__(self): return self.return_value async def __aexit__(self, *args, **kwargs): pass class TestNip05: @pytest.fixture def mock_session_factory(self): pubkey_hex = "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6" async def json_response(): return {"names": {"alice": pubkey_hex, "eve": pubkey_hex}} mock_resp = MagicMock() mock_resp.status = 200 mock_resp.json = json_response mock_get_cm = AsyncContextManagerMock(return_value=mock_resp) mock_session = AsyncContextManagerMock() mock_session.return_value = mock_session mock_session.get = MagicMock(return_value=mock_get_cm) return mock_session @pytest.mark.asyncio async def test_verify_valid(self, mock_session_factory): pubkey_hex = "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6" with patch("aiohttp.ClientSession", return_value=mock_session_factory): result = await verify_nip05("alice@example.com", pubkey_hex) assert result is True @pytest.mark.asyncio async def test_verify_no_at_sign(self): result = await verify_nip05("invalid_name", "some_pubkey_hex") assert result is False @pytest.mark.asyncio async def test_verify_http_error(self): pubkey_hex = "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6" mock_resp = MagicMock() mock_resp.status = 404 mock_get_cm = AsyncContextManagerMock(return_value=mock_resp) mock_session = AsyncContextManagerMock() mock_session.return_value = mock_session mock_session.get = MagicMock(return_value=mock_get_cm) with patch("aiohttp.ClientSession", return_value=mock_session): result = await verify_nip05("alice@example.com", pubkey_hex) assert result is False @pytest.mark.asyncio async def test_verify_network_error(self): pubkey_hex = "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6" mock_session = AsyncContextManagerMock() mock_session.return_value = mock_session mock_session.get = MagicMock(side_effect=Exception("Network error")) with patch("aiohttp.ClientSession", return_value=mock_session): result = await verify_nip05("bob@example.com", pubkey_hex) assert result is False @pytest.mark.asyncio async def test_verify_name_not_found(self): pubkey_hex = "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6" async def json_empty(): return {"names": {}} mock_resp = MagicMock() mock_resp.status = 200 mock_resp.json = json_empty mock_get_cm = AsyncContextManagerMock(return_value=mock_resp) mock_session = AsyncContextManagerMock() mock_session.return_value = mock_session mock_session.get = MagicMock(return_value=mock_get_cm) with patch("aiohttp.ClientSession", return_value=mock_session): result = await verify_nip05("charlie@example.com", pubkey_hex) assert result is False @pytest.mark.asyncio async def test_verify_pubkey_mismatch(self): async def json_mismatch(): return {"names": {"dave": "different_pubkey_hex"}} mock_resp = MagicMock() mock_resp.status = 200 mock_resp.json = json_mismatch mock_get_cm = AsyncContextManagerMock(return_value=mock_resp) mock_session = AsyncContextManagerMock() mock_session.return_value = mock_session mock_session.get = MagicMock(return_value=mock_get_cm) with patch("aiohttp.ClientSession", return_value=mock_session): result = await verify_nip05("dave@example.com", "expected_pubkey") assert result is False @pytest.mark.asyncio async def test_verify_case_insensitive_pubkey_match(self, mock_session_factory): pubkey_hex = "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6" with patch("aiohttp.ClientSession", return_value=mock_session_factory): result = await verify_nip05("eve@example.com", pubkey_hex) assert result is True