A Python port of the Invisible Internet Project (I2P)
at main 128 lines 4.2 kB view raw
1"""Tests for GenericClientTask — TCP client tunnel.""" 2 3import asyncio 4from unittest.mock import AsyncMock, MagicMock, patch 5 6import pytest 7 8from i2p_apps.i2ptunnel.client_task import GenericClientTask 9 10 11def _make_config(**overrides): 12 config = MagicMock() 13 config.name = "test-client" 14 config.interface = "127.0.0.1" 15 config.listen_port = 0 16 config.target_destination = overrides.get("target_destination", "example.i2p") 17 config.type = MagicMock() 18 return config 19 20 21def _make_task(**overrides): 22 config = _make_config(**overrides) 23 session = AsyncMock() 24 return GenericClientTask(config, session) 25 26 27def _mock_writer(): 28 writer = MagicMock() 29 writer.write = MagicMock() 30 writer.drain = AsyncMock() 31 writer.close = MagicMock() 32 writer.wait_closed = AsyncMock() 33 return writer 34 35 36class TestParseDestinations: 37 def test_single(self): 38 result = GenericClientTask._parse_destinations("example.i2p") 39 assert result == ["example.i2p"] 40 41 def test_comma_separated(self): 42 result = GenericClientTask._parse_destinations("a.i2p,b.i2p") 43 assert result == ["a.i2p", "b.i2p"] 44 45 def test_space_separated(self): 46 result = GenericClientTask._parse_destinations("a.i2p b.i2p") 47 assert result == ["a.i2p", "b.i2p"] 48 49 def test_mixed(self): 50 result = GenericClientTask._parse_destinations("a.i2p, b.i2p c.i2p") 51 assert result == ["a.i2p", "b.i2p", "c.i2p"] 52 53 def test_empty(self): 54 result = GenericClientTask._parse_destinations("") 55 assert result == [] 56 57 58class TestPickDestination: 59 def test_single_always_picked(self): 60 task = _make_task(target_destination="only.i2p") 61 assert task._pick_destination() == "only.i2p" 62 63 def test_multiple_picks_from_list(self): 64 task = _make_task(target_destination="a.i2p,b.i2p,c.i2p") 65 for _ in range(20): 66 assert task._pick_destination() in ("a.i2p", "b.i2p", "c.i2p") 67 68 69class TestResolveDestination: 70 @pytest.mark.asyncio 71 async def test_b32_passthrough(self): 72 task = _make_task() 73 result = await task._resolve_destination("abc.b32.i2p") 74 assert result == "abc.b32.i2p" 75 76 @pytest.mark.asyncio 77 async def test_i2p_lookup(self): 78 task = _make_task() 79 task._session.lookup = AsyncMock(return_value="resolved-dest") 80 result = await task._resolve_destination("example.i2p") 81 assert result == "resolved-dest" 82 83 @pytest.mark.asyncio 84 async def test_i2p_lookup_fail(self): 85 task = _make_task() 86 task._session.lookup = AsyncMock(return_value=None) 87 result = await task._resolve_destination("unknown.i2p") 88 assert result is None 89 90 @pytest.mark.asyncio 91 async def test_base64_passthrough(self): 92 task = _make_task() 93 result = await task._resolve_destination("AAAA...base64dest") 94 assert result == "AAAA...base64dest" 95 96 97class TestHandleClient: 98 @pytest.mark.asyncio 99 async def test_resolve_fail_closes(self): 100 task = _make_task(target_destination="unknown.i2p") 101 task._session.lookup = AsyncMock(return_value=None) 102 reader = AsyncMock() 103 writer = _mock_writer() 104 await task.handle_client(reader, writer) 105 writer.close.assert_called() 106 107 @pytest.mark.asyncio 108 async def test_connect_fail_closes(self): 109 task = _make_task(target_destination="abc.b32.i2p") 110 task._session.connect = AsyncMock(side_effect=ConnectionError("fail")) 111 reader = AsyncMock() 112 writer = _mock_writer() 113 await task.handle_client(reader, writer) 114 writer.close.assert_called() 115 116 @pytest.mark.asyncio 117 async def test_success_bridges(self): 118 task = _make_task(target_destination="abc.b32.i2p") 119 remote_reader = AsyncMock() 120 remote_writer = MagicMock() 121 remote_writer.close = MagicMock() 122 task._session.connect = AsyncMock(return_value=(remote_reader, remote_writer)) 123 reader = AsyncMock() 124 writer = _mock_writer() 125 126 with patch("i2p_apps.i2ptunnel.client_task.bridge", new_callable=AsyncMock) as mock_bridge: 127 await task.handle_client(reader, writer) 128 mock_bridge.assert_called_once()