adding (nonfunctional) tests
This commit is contained in:
commit
2e0bf0cb67
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.egg-info/
|
||||||
|
*.eggs/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
Pipfile*
|
39
setup.cfg
Normal file
39
setup.cfg
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
[metadata]
|
||||||
|
name = announce_server
|
||||||
|
author = Mathematical Michael
|
||||||
|
author_email = mm@clfx.cc
|
||||||
|
url = https://git.mlden.com/mm/announce-server.git
|
||||||
|
license = MIT
|
||||||
|
description = Announces a server to a host
|
||||||
|
long_description = file: README.md
|
||||||
|
long_description_content_type = text/markdown
|
||||||
|
classifiers =
|
||||||
|
Development Status :: 3 - Alpha
|
||||||
|
Intended Audience :: Developers
|
||||||
|
License :: OSI Approved :: MIT License
|
||||||
|
Programming Language :: Python :: 3
|
||||||
|
Programming Language :: Python :: 3.6
|
||||||
|
Programming Language :: Python :: 3.7
|
||||||
|
Programming Language :: Python :: 3.8
|
||||||
|
Programming Language :: Python :: 3.9
|
||||||
|
Programming Language :: Python :: 3.10
|
||||||
|
|
||||||
|
|
||||||
|
[options]
|
||||||
|
package_dir =
|
||||||
|
= src
|
||||||
|
packages = find:
|
||||||
|
install_requires =
|
||||||
|
python-socketio[asyncio_client]
|
||||||
|
|
||||||
|
[options.packages.find]
|
||||||
|
where = src
|
||||||
|
|
||||||
|
[options.extras_require]
|
||||||
|
dev =
|
||||||
|
build
|
||||||
|
setuptools_scm
|
||||||
|
pytest
|
||||||
|
pytest-mock
|
||||||
|
pytest-asyncio
|
||||||
|
asynctest; python_version<'3.8'
|
11
setup.py
Normal file
11
setup.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="announce-server",
|
||||||
|
packages=find_packages(),
|
||||||
|
setup_requires=[
|
||||||
|
"setuptools_scm",
|
||||||
|
],
|
||||||
|
use_scm_version=True,
|
||||||
|
python_requires=">=3.6",
|
||||||
|
)
|
2
src/announce_server/__init__.py
Normal file
2
src/announce_server/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from .decorator import _announce_server, announce_server
|
||||||
|
from .get_ip import get_ip_address
|
69
src/announce_server/decorator.py
Normal file
69
src/announce_server/decorator.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import asyncio
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
import socketio
|
||||||
|
|
||||||
|
sio = socketio.AsyncClient()
|
||||||
|
|
||||||
|
|
||||||
|
async def _announce_server(**kwargs):
|
||||||
|
SERVER_NAME = kwargs.get("name", "server_1")
|
||||||
|
SERVER_IP = kwargs.get("ip", "localhost")
|
||||||
|
SERVER_PORT = kwargs.get("port", 8000)
|
||||||
|
HOST_SERVER_IP = kwargs.get("host_ip", "0.0.0.0")
|
||||||
|
HOST_SERVER_PORT = kwargs.get("host_port", 5000)
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
async def connect():
|
||||||
|
await sio.emit(
|
||||||
|
"register", {"name": SERVER_NAME, "ip": SERVER_IP, "port": SERVER_PORT}
|
||||||
|
)
|
||||||
|
print("Announced server to host")
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
# retry until we connect to the host
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
await sio.connect(f"http://{HOST_SERVER_IP}:{HOST_SERVER_PORT}")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
print("Failed to connect to host, retrying in 5 seconds")
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
# await sio.connect(f'http://{HOST_SERVER_IP}:{HOST_SERVER_PORT}')
|
||||||
|
print("Connected to host")
|
||||||
|
|
||||||
|
@sio.on("heartbeat")
|
||||||
|
async def on_heartbeat():
|
||||||
|
print("Received heartbeat from host")
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
async def disconnect():
|
||||||
|
print("Disconnected from host")
|
||||||
|
|
||||||
|
await main()
|
||||||
|
|
||||||
|
|
||||||
|
def announce_server(task=None, loop=None, **outer_kwargs):
|
||||||
|
if task is None:
|
||||||
|
return lambda f: announce_server(f, loop=loop, **outer_kwargs)
|
||||||
|
|
||||||
|
@wraps(task)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
async def main(*args, **kwargs):
|
||||||
|
if loop is not None:
|
||||||
|
host_block_thread = loop.run_in_executor(None, task)
|
||||||
|
else:
|
||||||
|
host_block_thread = asyncio.to_thread(task)
|
||||||
|
|
||||||
|
# Announce the server to the host
|
||||||
|
await _announce_server(**outer_kwargs)
|
||||||
|
|
||||||
|
# Wait for host_block to finish
|
||||||
|
await host_block_thread
|
||||||
|
|
||||||
|
if loop is not None:
|
||||||
|
return loop.create_task(main(*args, **kwargs))
|
||||||
|
else:
|
||||||
|
return asyncio.run(main(*args, **kwargs))
|
||||||
|
return wrapper
|
14
src/announce_server/get_ip.py
Normal file
14
src/announce_server/get_ip.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import socket
|
||||||
|
|
||||||
|
|
||||||
|
def get_ip_address():
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
try:
|
||||||
|
# This IP address doesn't need to be reachable, as we're only using it to find the local IP address
|
||||||
|
s.connect(("10.255.255.255", 1))
|
||||||
|
ip = s.getsockname()[0]
|
||||||
|
except Exception:
|
||||||
|
ip = "127.0.0.1"
|
||||||
|
finally:
|
||||||
|
s.close()
|
||||||
|
return ip
|
51
tests/test_announce.py
Normal file
51
tests/test_announce.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import asyncio
|
||||||
|
import sys
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 8):
|
||||||
|
from unittest.mock import AsyncMock
|
||||||
|
else:
|
||||||
|
from asynctest import CoroutineMock as AsyncMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from announce_server import _announce_server, announce_server
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_announce_server_decorator(event_loop, mocker):
|
||||||
|
# Mock the _announce_server function to prevent actual connections
|
||||||
|
mocker.patch("announce_server._announce_server")
|
||||||
|
|
||||||
|
# Sample function to be decorated
|
||||||
|
async def sample_async_function():
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
return "Hello, world!"
|
||||||
|
|
||||||
|
# Decorate the sample function with announce_server
|
||||||
|
decorated_function = announce_server(
|
||||||
|
name="test_server",
|
||||||
|
ip="127.0.0.1",
|
||||||
|
port=8000,
|
||||||
|
host_ip="127.0.0.1",
|
||||||
|
host_port=5000,
|
||||||
|
loop=event_loop, # Pass the current event loop
|
||||||
|
)(sample_async_function)
|
||||||
|
|
||||||
|
# Run the decorated function
|
||||||
|
task = await decorated_function()
|
||||||
|
await asyncio.sleep(1.1) # Sleep slightly longer than sample_async_function
|
||||||
|
task.cancel() # Cancel the task
|
||||||
|
|
||||||
|
# Check if the _announce_server function was called with the correct arguments
|
||||||
|
announce_server._announce_server.assert_called_once_with(
|
||||||
|
name="test_server",
|
||||||
|
ip="127.0.0.1",
|
||||||
|
port=8000,
|
||||||
|
host_ip="127.0.0.1",
|
||||||
|
host_port=5000,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if the decorated function returns the expected result
|
||||||
|
result = await sample_async_function()
|
||||||
|
assert result == "Hello, world!"
|
34
tests/test_get_ip.py
Normal file
34
tests/test_get_ip.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import socket
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from announce_server import get_ip_address
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_ip_address():
|
||||||
|
with patch("socket.socket") as mock_socket:
|
||||||
|
# Create a MagicMock object for the socket object
|
||||||
|
mock_socket_instance = MagicMock()
|
||||||
|
mock_socket.return_value = mock_socket_instance
|
||||||
|
|
||||||
|
# Define the expected IP address
|
||||||
|
expected_ip = "192.168.1.100"
|
||||||
|
|
||||||
|
# Configure the mock socket instance to return the expected IP address
|
||||||
|
mock_socket_instance.getsockname.return_value = (expected_ip, 0)
|
||||||
|
|
||||||
|
# Test the get_ip_address function
|
||||||
|
result_ip = get_ip_address()
|
||||||
|
|
||||||
|
# Check if the result matches the expected IP address
|
||||||
|
assert result_ip == expected_ip
|
||||||
|
|
||||||
|
# Check if the socket object was created with the correct arguments
|
||||||
|
mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
|
||||||
|
# Check if the socket.connect method was called with the correct arguments
|
||||||
|
mock_socket_instance.connect.assert_called_once_with(("10.255.255.255", 1))
|
||||||
|
|
||||||
|
# Check if the socket.close method was called
|
||||||
|
mock_socket_instance.close.assert_called_once()
|
Loading…
Reference in New Issue
Block a user