Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Tests & Improve Correctness Of Implementation #10

Merged
merged 7 commits into from
Jan 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ jobs:
run: poetry run ruff check . --no-fix

- name: Run Ruff formatter
run: poetry run ruff format . --check
run: poetry run ruff format . --check

- name: Run tests
run: poetry run pytest
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"python.analysis.extraPaths": [
"./tests"
]
}
15 changes: 7 additions & 8 deletions aiomcrcon/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class MessageType(enum.IntEnum):
LOGIN = 3
COMMAND = 2
RESPONSE = 0
INVALID_AUTH = -1


class Client:
Expand Down Expand Up @@ -55,7 +54,7 @@ async def connect(self, timeout: float = 2.0) -> None:
except Exception as e:
raise RCONConnectionError("The connection failed for an unknown reason.", e)

await self._send_msg(MessageType.LOGIN, self.password)
await asyncio.wait_for(self._send_msg(MessageType.LOGIN, self.password), timeout)

self._ready = True

Expand All @@ -72,14 +71,14 @@ async def _read(self, n: int) -> bytes:

return out

async def _send_msg(self, type_: int, msg: str) -> t.Tuple[str, int]:
async def _send_msg(self, msg_type: int, msg: str) -> tuple[str, int]:
"""Sends data to the server, and returns the response."""

# randomly generate request id
req_id = random.randint(0, 2147483647)

# pack request id, packet type, and the actual message
packet_data = struct.pack("<ii", req_id, type_) + msg.encode("utf8") + b"\x00\x00"
packet_data = struct.pack("<ii", req_id, msg_type) + msg.encode("utf8") + b"\x00\x00"

# pack length of packet + rest of packet data
packet = struct.pack("<i", len(packet_data)) + packet_data
Expand All @@ -98,17 +97,17 @@ async def _send_msg(self, type_: int, msg: str) -> t.Tuple[str, int]:
raise ValueError("Invalid data received from server.")

# decode the incoming request id and packet type
in_type, in_req_id = struct.unpack("<ii", in_data[0:8])
in_req_id, in_type = struct.unpack("<ii", in_data[0:8])

if in_type == MessageType.INVALID_AUTH:
if in_req_id == -1:
raise IncorrectPasswordError

# decode the received message
in_msg = in_data[8:-2].decode("utf8")

return in_msg, in_type
return in_msg, in_req_id

async def send_cmd(self, cmd: str, timeout: float = 2.0) -> t.Tuple[str, int]:
async def send_cmd(self, cmd: str, timeout: float = 2.0) -> tuple[str, int]:
"""Sends a command to the server."""

if not self._ready:
Expand Down
4 changes: 2 additions & 2 deletions aiomcrcon/errors.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import typing as t
from __future__ import annotations


class RCONConnectionError(Exception):
"""Raised when the Client.connect() method fails."""

def __init__(self, msg: t.Optional[str] = None, error: t.Optional[Exception] = None) -> None:
def __init__(self, msg: str | None = None, error: Exception | None = None) -> None:
super().__init__(msg)

self.message = msg
Expand Down
156 changes: 155 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "aio-mc-rcon"
version = "3.3.0"
version = "3.4.0"
description = "An async library for utilizing remote console on Minecraft Java Edition servers"
authors = ["Milo Weinberg <iapetus011@gmail.com>"]
license = "MIT"
Expand All @@ -14,6 +14,8 @@ python = ">=3.9,<4.0"

[tool.poetry.group.dev.dependencies]
ruff = "^0.9.2"
pytest = "^8.3.4"
pytest-asyncio = "^0.25.2"

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand All @@ -22,3 +24,7 @@ build-backend = "poetry.core.masonry.api"
[tool.ruff]
line-length = 100
target-version = "py39"

[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
28 changes: 28 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import pytest

from aiomcrcon.client import Client
from rcon_server import Server


@pytest.fixture
async def dummy_server(unused_tcp_port):
server = Server(
host="localhost",
port=unused_tcp_port,
password="hunter2",
)

await server.start()

yield server

await server.close()


@pytest.fixture
def client(dummy_server):
return Client(
host=dummy_server.host,
port=dummy_server.port,
password=dummy_server.password,
)
Loading
Loading