basic working mvp
This commit is contained in:
2026-10-07 15:12:38 +03:00
parent 785a6980f6
commit fac12b2ea8
24 changed files with 1696 additions and 1 deletions

View File

@@ -0,0 +1,114 @@
"""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))