Browse Source

adding (nonfunctional) tests

hosting
Michael Pilosov 2 years ago
commit
2e0bf0cb67
  1. 8
      .gitignore
  2. 0
      README.md
  3. 39
      setup.cfg
  4. 11
      setup.py
  5. 2
      src/announce_server/__init__.py
  6. 69
      src/announce_server/decorator.py
  7. 14
      src/announce_server/get_ip.py
  8. 51
      tests/test_announce.py
  9. 34
      tests/test_get_ip.py

8
.gitignore

@ -0,0 +1,8 @@
__pycache__/
*.pyc
*.pyo
*.egg-info/
*.eggs/
dist/
build/
Pipfile*

0
README.md

39
setup.cfg

@ -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

@ -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

@ -0,0 +1,2 @@
from .decorator import _announce_server, announce_server
from .get_ip import get_ip_address

69
src/announce_server/decorator.py

@ -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

@ -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

@ -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

@ -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…
Cancel
Save