"""Tests for HTTPProxy server — handle_client and error responses."""
import asyncio
from unittest.mock import AsyncMock, MagicMock
import pytest
from i2p_apps.i2ptunnel.http_proxy import HTTPProxy
def _mock_writer():
writer = MagicMock()
writer.write = MagicMock()
writer.drain = AsyncMock()
writer.close = MagicMock()
return writer
class TestHTTPProxyProperties:
def test_defaults(self):
proxy = HTTPProxy()
assert proxy.listen_address == ("127.0.0.1", 4444)
assert proxy.is_running is False
def test_custom(self):
proxy = HTTPProxy("0.0.0.0", 8080)
assert proxy.listen_address == ("0.0.0.0", 8080)
@pytest.mark.asyncio
async def test_start_stop(self):
proxy = HTTPProxy("127.0.0.1", 0)
await proxy.start()
assert proxy.is_running is True
await proxy.stop()
assert proxy.is_running is False
@pytest.mark.asyncio
async def test_stop_without_start(self):
proxy = HTTPProxy()
await proxy.stop()
assert proxy.is_running is False
class TestHTTPProxyErrorResponse:
def test_build_error_response(self):
proxy = HTTPProxy()
resp = proxy.build_error_response(404, "Not Found")
text = resp.decode()
assert "HTTP/1.1 404 Not Found" in text
assert "Content-Type: text/html" in text
assert "
404 Not Found
" in text
def test_build_error_502(self):
proxy = HTTPProxy()
resp = proxy.build_error_response(502, "Bad Gateway")
assert b"502" in resp
class TestHTTPProxyHandleClient:
@pytest.mark.asyncio
async def test_empty_read(self):
proxy = HTTPProxy()
reader = AsyncMock()
reader.readline = AsyncMock(return_value=b"")
writer = _mock_writer()
await proxy.handle_client(reader, writer)
writer.close.assert_called()
@pytest.mark.asyncio
async def test_i2p_request(self):
proxy = HTTPProxy()
reader = AsyncMock()
reader.readline = AsyncMock(return_value=b"GET http://example.i2p/ HTTP/1.1\r\n")
writer = _mock_writer()
await proxy.handle_client(reader, writer)
# Should return 502 (no I2P session)
written = writer.write.call_args[0][0]
assert b"502" in written
@pytest.mark.asyncio
async def test_non_i2p_request(self):
proxy = HTTPProxy()
reader = AsyncMock()
reader.readline = AsyncMock(return_value=b"GET http://example.com/ HTTP/1.1\r\n")
writer = _mock_writer()
await proxy.handle_client(reader, writer)
written = writer.write.call_args[0][0]
assert b"502" in written
@pytest.mark.asyncio
async def test_exception_closes_writer(self):
proxy = HTTPProxy()
reader = AsyncMock()
reader.readline = AsyncMock(side_effect=ConnectionResetError("reset"))
writer = _mock_writer()
await proxy.handle_client(reader, writer)
writer.close.assert_called()
@pytest.mark.asyncio
async def test_connect_request(self):
proxy = HTTPProxy()
reader = AsyncMock()
reader.readline = AsyncMock(return_value=b"CONNECT example.i2p:443 HTTP/1.1\r\n")
writer = _mock_writer()
await proxy.handle_client(reader, writer)
written = writer.write.call_args[0][0]
assert b"502" in written