"""Check the real test client independently of the server implementation.""" import asyncio import json import pytest async def send_packet(writer, packet): writer.write((json.dumps(packet) + "\n").encode()) await writer.drain() def test_client_waits_for_handshake_and_checks_heartbeat(client_module): """Проверяет порядок handshake и сохранение данных, пришедших до heartbeat-ответа.""" async def scenario(): finished = asyncio.get_running_loop().create_future() async def peer(reader, writer): try: packet = json.loads(await reader.readline()) assert packet == { "command": "connection", "payload": {"name": "rover", "type": "robot", "in": ["control"], "out": ["telemetry"]}, } await send_packet(writer, {"command": "token_ask", "payload": 123, "upf": 456}) assert json.loads(await reader.readline()) == { "command": "token_answer", "payload": {"token": None, "upf": None}, } await send_packet(writer, {"command": "handshake_ask"}) assert json.loads(await reader.readline()) == { "command": "handshake_answer", "payload": {"status": True, "state": None}, } assert json.loads(await reader.readline()) == {"command": "heartbeat_ask", "payload": {}} await send_packet(writer, {"command": "control", "payload": {"turn": 1}}) await send_packet(writer, {"command": "heartbeat_answer"}) assert json.loads(await reader.readline()) == { "command": "telemetry", "payload": {"temperature": 42.0, "battery": 87}, } assert await reader.read() == b"" except BaseException as error: finished.set_exception(error) else: finished.set_result(None) finally: writer.close() await writer.wait_closed() server = await asyncio.start_server(peer, "127.0.0.1", 0) async with server: client = client_module.Client( "rover", port=server.sockets[0].getsockname()[1], node_type="robot", inputs=["control"], ) try: await client.connect() assert (client.uid, client.upf) == (123, 456) assert await client.recv() == {"command": "control", "payload": {"turn": 1}} await client.telemetry() finally: await client.close() await finished asyncio.run(asyncio.wait_for(scenario(), 5)) def test_client_times_out_when_server_does_not_answer(client_module): """Проверяет таймаут молчащего сервера и освобождение клиентского соединения.""" async def scenario(): finished = asyncio.Event() async def peer(reader, writer): try: await reader.read() finally: writer.close() await writer.wait_closed() finished.set() server = await asyncio.start_server(peer, "127.0.0.1", 0) async with server: client = client_module.Client(port=server.sockets[0].getsockname()[1], timeout=0.1) with pytest.raises(TimeoutError): await client.connect() assert client.writer is None await finished.wait() asyncio.run(asyncio.wait_for(scenario(), 5)) def test_client_reports_wrong_handshake_packet(client_module): """Проверяет понятную ошибку и закрытие клиента при неверном ответе handshake.""" async def scenario(): finished = asyncio.Event() async def peer(reader, writer): try: await reader.readline() await send_packet(writer, {"command": "handshake_ask"}) await reader.read() finally: writer.close() await writer.wait_closed() finished.set() server = await asyncio.start_server(peer, "127.0.0.1", 0) async with server: client = client_module.Client(port=server.sockets[0].getsockname()[1]) with pytest.raises(client_module.ProtocolError, match="Expected token_ask"): await client.connect() assert client.writer is None await finished.wait() asyncio.run(asyncio.wait_for(scenario(), 5))