Skip to content
This repository was archived by the owner on May 3, 2026. It is now read-only.
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
8 changes: 7 additions & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import asyncio
from dataclasses import dataclass
from types import SimpleNamespace
from unittest.mock import Mock
import pytest

from reticulum_openapi import client as client_module


@dataclass
class Sample:
text: str


@pytest.mark.asyncio
async def test_send_command_receives_response(monkeypatch):
loop = asyncio.get_running_loop()
Expand All @@ -28,11 +29,13 @@ async def test_send_command_receives_response(monkeypatch):
class FakeDestination:
OUT = object()
SINGLE = object()

def __init__(self, *a, **k):
pass
monkeypatch.setattr(client_module.RNS, "Destination", FakeDestination)

class FakeLXMessage:

def __init__(self, dest, src, content, title):
self.dest = dest
self.src = src
Expand All @@ -49,6 +52,7 @@ async def run_cmd():
result = await task
assert result == b"ok"


@pytest.mark.asyncio
async def test_send_command_timeout(monkeypatch):
loop = asyncio.get_running_loop()
Expand All @@ -62,9 +66,11 @@ async def test_send_command_timeout(monkeypatch):

monkeypatch.setattr(client_module.RNS.Transport, "has_path", lambda dest: True)
monkeypatch.setattr(client_module.RNS.Identity, "recall", lambda h, create=False: object())

class FakeDestination:
OUT = object()
SINGLE = object()

def __init__(self, *a, **k):
pass
monkeypatch.setattr(client_module.RNS, "Destination", FakeDestination)
Expand Down
8 changes: 7 additions & 1 deletion tests/test_controller.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import pytest
import asyncio

from reticulum_openapi import controller as c


@pytest.mark.asyncio
async def test_handle_exceptions_success():
@c.handle_exceptions
Expand All @@ -12,6 +12,7 @@ async def handler(self, x):
result = await handler(object(), 3)
assert result == 6


@pytest.mark.asyncio
async def test_handle_exceptions_apierror():
@c.handle_exceptions
Expand All @@ -21,6 +22,7 @@ async def handler(self):
result = await handler(object())
assert result == {"error": "bad", "code": 400}


@pytest.mark.asyncio
async def test_handle_exceptions_generic():
@c.handle_exceptions
Expand All @@ -30,17 +32,21 @@ async def handler(self):
result = await handler(object())
assert result == {"error": "InternalServerError", "code": 500}


@pytest.mark.asyncio
async def test_run_business_logic(monkeypatch):
ctrl = c.Controller()

async def logic(a, b):
return a + b
result = await ctrl.run_business_logic(logic, 2, 3)
assert result == 5


@pytest.mark.asyncio
async def test_run_business_logic_error():
ctrl = c.Controller()

async def logic():
raise c.APIException("fail", 401)
result = await ctrl.run_business_logic(logic)
Expand Down
24 changes: 23 additions & 1 deletion tests/test_service_extra.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
from reticulum_openapi import service as service_module
from dataclasses import dataclass


@dataclass
class Item:
name: str


@pytest.mark.asyncio
async def test_send_message_calls_send(monkeypatch):
svc = service_module.LXMFService.__new__(service_module.LXMFService)
Expand Down Expand Up @@ -38,18 +40,23 @@ def recall(dest, create=False):
await svc.send_message("aa", "CMD", Item(name="x"))
svc._send_lxmf.assert_called_once()


@pytest.mark.asyncio
async def test_send_lxmf_uses_router(monkeypatch):
svc = service_module.LXMFService.__new__(service_module.LXMFService)
send_mock = Mock()
svc.router = SimpleNamespace(handle_outbound=send_mock)
svc.source_identity = object()

class FakeDestination:
OUT = object()
SINGLE = object()

def __init__(self, *a, **k):
pass

class FakeLXMessage:

def __init__(self, dest, src, content, title):
self.dest = dest
self.src = src
Expand All @@ -60,6 +67,7 @@ def __init__(self, dest, src, content, title):
svc._send_lxmf(object(), "CMD", b"data")
send_mock.assert_called_once()


@pytest.mark.asyncio
async def test_announce_logs(monkeypatch):
svc = service_module.LXMFService.__new__(service_module.LXMFService)
Expand All @@ -71,42 +79,56 @@ async def test_announce_logs(monkeypatch):
svc.announce()
ann_mock.assert_called_once_with(svc.source_identity.hash)


@pytest.mark.asyncio
async def test_start_and_stop(monkeypatch):
svc = service_module.LXMFService.__new__(service_module.LXMFService)
svc.router = SimpleNamespace(exit_handler=Mock())
svc._loop = asyncio.get_running_loop()
monkeypatch.setattr(service_module.RNS, "log", lambda *a, **k: None)
task = asyncio.create_task(svc.start())
asyncio.create_task(svc.start())
await asyncio.sleep(0.05)
await svc.stop()
assert svc._start_task is None


@pytest.mark.asyncio
async def test_init_and_add_route(monkeypatch):
class FakeReticulum:
storagepath = '/tmp'

def __init__(self, config_path=None):
pass

class FakeIdentity:

def __init__(self):

self.hash = b'h'

class FakeRNS:
Reticulum = FakeReticulum
Identity = FakeIdentity

@staticmethod
def log(*a, **k):
pass

@staticmethod
def prettyhexrep(x):
return 'h'

class FakeLXMRouter:

def __init__(self, storagepath=None):
self.storagepath = storagepath

def register_delivery_callback(self, cb):
self.cb = cb

def register_delivery_identity(self, ident, display_name=None, stamp_cost=0):
return ident

class FakeLXMF:
LXMRouter = FakeLXMRouter
LXMessage = object
Expand Down