168 lines
5.6 KiB
Python
168 lines
5.6 KiB
Python
import pytest
|
|
import asyncio
|
|
import hashlib
|
|
from openttd.protocol import PacketGameType
|
|
from openttd.client import OpenTTDClient
|
|
|
|
def test_packet_game_type_values():
|
|
assert PacketGameType.ServerFull == 0
|
|
assert PacketGameType.ClientJoin == 2
|
|
assert PacketGameType.ServerWelcome == 21
|
|
assert PacketGameType.ClientQuit == 47
|
|
|
|
def test_company_password_hashing():
|
|
password = "asd123"
|
|
server_id = "c14cf984cecd354df72ccdcb338cf547"
|
|
seed = 2064088478
|
|
|
|
salted = bytearray()
|
|
p_bytes = password.encode('utf-8')
|
|
s_bytes = server_id.encode('utf-8')
|
|
for i in range(32):
|
|
p_char = p_bytes[i] if i < len(p_bytes) else 0
|
|
s_char = s_bytes[i] if i < len(s_bytes) else 0
|
|
seed_char = (seed >> (i % 32)) & 0xFF
|
|
salted.append(p_char ^ s_char ^ seed_char)
|
|
expected_hash = hashlib.md5(salted, usedforsecurity=False).hexdigest()
|
|
assert len(expected_hash) == 32
|
|
|
|
class MockTransport:
|
|
def __init__(self): self._closing = False
|
|
def is_closing(self): return self._closing
|
|
def close(self): self._closing = True
|
|
def write(self, data): return len(data)
|
|
|
|
class MockProtocol:
|
|
def __init__(self): self.sent = []
|
|
async def send_packet(self, data):
|
|
self.sent.append(data)
|
|
return len(data)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_client_connect_success(monkeypatch):
|
|
# Coverage for client.py:48-50
|
|
client = OpenTTDClient(host="127.0.0.1")
|
|
|
|
class FakeProto:
|
|
def __init__(self): self.sent = []
|
|
async def send_packet(self, data): self.sent.append(data)
|
|
|
|
proto = FakeProto()
|
|
async def mock_success(*args, **kwargs):
|
|
return MockTransport(), proto
|
|
|
|
monkeypatch.setattr(asyncio.get_running_loop(), "create_connection", mock_success)
|
|
|
|
await client.connect()
|
|
assert len(proto.sent) == 1 # ClientGameInfo sent
|
|
assert client._protocol == proto
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_client_connect_failure(monkeypatch):
|
|
client = OpenTTDClient(host="127.0.0.1")
|
|
async def mock_fail(*args, **kwargs):
|
|
raise Exception("Async Failure")
|
|
monkeypatch.setattr(asyncio.get_running_loop(), "create_connection", mock_fail)
|
|
with pytest.raises(Exception, match="Async Failure"):
|
|
await client.connect()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_client_error_handling():
|
|
client = OpenTTDClient(host="127.0.0.1")
|
|
client._transport = MockTransport()
|
|
client._protocol = MockProtocol()
|
|
|
|
await client.receive_ServerError(None, 8) # WrongRevision
|
|
assert client.shutdown_event.is_set()
|
|
|
|
client.shutdown_event.clear()
|
|
await client.receive_ServerError(None, 10) # WrongPassword
|
|
await client.receive_ServerError(None, 11) # NameInUse
|
|
await client.receive_ServerError(None, 17) # Timeout
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_client_server_full_banned():
|
|
client = OpenTTDClient(host="127.0.0.1")
|
|
client._transport = MockTransport()
|
|
client._protocol = MockProtocol()
|
|
|
|
await client.receive_ServerFull(None)
|
|
await client.receive_ServerBanned(None)
|
|
client.connected(None)
|
|
client.disconnect(None)
|
|
assert client.shutdown_event.is_set()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_chat_callback():
|
|
client = OpenTTDClient(host="127.0.0.1")
|
|
received = []
|
|
def on_chat(cid, msg):
|
|
received.append((cid, msg))
|
|
client.on_chat = on_chat
|
|
|
|
await client.receive_ServerChat(None, 42, "Hello World")
|
|
assert received == [(42, "Hello World")]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_fallback_handlers():
|
|
client = OpenTTDClient(host="127.0.0.1")
|
|
client.log.setLevel(100)
|
|
|
|
await client.receive_ServerUnused(None)
|
|
await client.receive_ServerSync(None)
|
|
await client.receive_ServerClientJoined(None)
|
|
await client.receive_ServerMapBegin(None)
|
|
await client.receive_ServerMapSize(None, size=100)
|
|
await client.receive_ServerMapData(None, data=b"data")
|
|
await client.receive_ServerConfigurationUpdate(None)
|
|
await client.receive_ServerClientInfo(None)
|
|
await client.receive_ServerExternalChat(None)
|
|
await client.receive_ServerCommand(None)
|
|
await client.receive_ClientAck(None)
|
|
await client.receive_ClientIdentify(None)
|
|
await client.receive_ServerCompanyUpdate(None)
|
|
|
|
client.joined.set()
|
|
await client.join_company(0)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_client_full_handshake_flow():
|
|
client = OpenTTDClient(host="127.0.0.1", username="TestUser")
|
|
client._protocol = MockProtocol()
|
|
client._transport = MockTransport()
|
|
|
|
await client.receive_ServerGameInfo(None, name="TestSrv", openttd_version="14.0")
|
|
assert len(client._protocol.sent) == 1
|
|
|
|
pake_data = b"S" * 32 + b"N" * 24
|
|
await client.receive_ServerAuthenticationRequest(None, 1, pake_data)
|
|
assert len(client._protocol.sent) == 2
|
|
|
|
await client.receive_ServerEnableEncryption(None, b"E" * 24)
|
|
assert client.encryption_enabled
|
|
assert len(client._protocol.sent) == 3
|
|
|
|
await client.receive_ServerCheckNewGRFs(None)
|
|
assert len(client._protocol.sent) == 4
|
|
|
|
await client.join_company(0, "comp_pw")
|
|
await client.receive_ServerNeedCompanyPassword(None, 1234, "srv_id")
|
|
assert len(client._protocol.sent) == 5
|
|
|
|
await client.receive_ServerWelcome(None, client_id=42)
|
|
assert client.client_id == 42
|
|
assert len(client._protocol.sent) == 6
|
|
|
|
await client.receive_ServerMapDone(None)
|
|
assert len(client._protocol.sent) == 7
|
|
assert client.joined.is_set()
|
|
|
|
await client.receive_ServerFrame(None, 100, 7)
|
|
assert len(client._protocol.sent) == 8
|
|
|
|
await client.quit()
|
|
assert len(client._protocol.sent) == 9
|
|
|
|
client._transport.close()
|
|
await client.quit()
|