-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto-reconnect to Stomp when connection lost (#101)
* Working reconnect * Working tests for stomp template * Skip stomp tests with flag * Include activemq server in stomp tests (CI)
- Loading branch information
1 parent
71e54a2
commit 8e95744
Showing
5 changed files
with
227 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Based on https://docs.pytest.org/en/latest/example/simple.html#control-skipping-of-tests-according-to-command-line-option # noqa: E501 | ||
|
||
import pytest | ||
|
||
|
||
def pytest_addoption(parser): | ||
parser.addoption( | ||
"--skip-stomp", | ||
action="store_true", | ||
default=False, | ||
help="skip stomp tests (e.g. because a server is unavailable)", | ||
) | ||
|
||
|
||
def pytest_configure(config): | ||
config.addinivalue_line("markers", "stomp: mark test as requiring stomp broker") | ||
|
||
|
||
def pytest_collection_modifyitems(config, items): | ||
if config.getoption("--skip-stomp"): | ||
skip_stomp = pytest.mark.skip(reason="skipping stomp tests at user request") | ||
for item in items: | ||
if "stomp" in item.keywords: | ||
item.add_marker(skip_stomp) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,121 @@ | ||
import json | ||
from typing import Tuple | ||
from unittest.mock import MagicMock | ||
import itertools | ||
from concurrent.futures import Future | ||
from dataclasses import dataclass | ||
from typing import Any, Iterable, Type | ||
|
||
from blueapi.messaging import MessagingTemplate, StompMessagingTemplate | ||
import pytest | ||
|
||
from blueapi.config import StompConfig | ||
from blueapi.messaging import MessageContext, MessagingTemplate, StompMessagingTemplate | ||
|
||
def test_send() -> None: | ||
template, conn = _mock_template() | ||
template.send("test", "test_message") | ||
conn.send.assert_called_once_with( | ||
body=json.dumps("test_message"), | ||
destination="test", | ||
headers={}, | ||
_TIMEOUT: float = 10.0 | ||
_COUNT = itertools.count() | ||
|
||
|
||
@pytest.fixture | ||
def disconnected_template() -> MessagingTemplate: | ||
return StompMessagingTemplate.autoconfigured(StompConfig()) | ||
|
||
|
||
@pytest.fixture | ||
def template(disconnected_template: MessagingTemplate) -> Iterable[MessagingTemplate]: | ||
disconnected_template.connect() | ||
yield disconnected_template | ||
disconnected_template.disconnect() | ||
|
||
|
||
@pytest.fixture | ||
def test_queue(template: MessagingTemplate) -> str: | ||
return template.destinations.queue(f"test-{next(_COUNT)}") | ||
|
||
|
||
@pytest.mark.stomp | ||
def test_send(template: MessagingTemplate, test_queue: str) -> None: | ||
f: Future = Future() | ||
|
||
def callback(ctx: MessageContext, message: str) -> None: | ||
f.set_result(message) | ||
|
||
template.subscribe(test_queue, callback) | ||
template.send(test_queue, "test_message") | ||
assert f.result(timeout=_TIMEOUT) | ||
|
||
|
||
@pytest.mark.stomp | ||
def test_send_on_reply(template: MessagingTemplate, test_queue: str) -> None: | ||
acknowledge(template, test_queue) | ||
|
||
f: Future = Future() | ||
|
||
def callback(ctx: MessageContext, message: str) -> None: | ||
f.set_result(message) | ||
|
||
template.send(test_queue, "test_message", callback) | ||
assert f.result(timeout=_TIMEOUT) | ||
|
||
|
||
@pytest.mark.stomp | ||
def test_send_and_recieve(template: MessagingTemplate, test_queue: str) -> None: | ||
acknowledge(template, test_queue) | ||
reply = template.send_and_recieve(test_queue, "test", str).result(timeout=_TIMEOUT) | ||
assert reply == "ack" | ||
|
||
|
||
@dataclass | ||
class Foo: | ||
a: int | ||
b: str | ||
|
||
|
||
@pytest.mark.stomp | ||
@pytest.mark.parametrize( | ||
"message,message_type", | ||
[("test", str), (1, int), (Foo(1, "test"), Foo)], | ||
) | ||
def test_deserialization( | ||
template: MessagingTemplate, test_queue: str, message: Any, message_type: Type | ||
) -> None: | ||
def server(ctx: MessageContext, message: message_type) -> None: # type: ignore | ||
reply_queue = ctx.reply_destination | ||
if reply_queue is None: | ||
raise RuntimeError("reply queue is None") | ||
template.send(reply_queue, message) | ||
|
||
template.subscribe(test_queue, server) | ||
reply = template.send_and_recieve(test_queue, message, message_type).result( | ||
timeout=_TIMEOUT | ||
) | ||
assert reply == message | ||
|
||
|
||
@pytest.mark.stomp | ||
def test_subscribe_before_connect( | ||
disconnected_template: MessagingTemplate, test_queue: str | ||
) -> None: | ||
acknowledge(disconnected_template, test_queue) | ||
disconnected_template.connect() | ||
reply = disconnected_template.send_and_recieve(test_queue, "test", str).result( | ||
timeout=_TIMEOUT | ||
) | ||
assert reply == "ack" | ||
|
||
|
||
@pytest.mark.stomp | ||
def test_reconnect(template: MessagingTemplate, test_queue: str) -> None: | ||
acknowledge(template, test_queue) | ||
reply = template.send_and_recieve(test_queue, "test", str).result(timeout=_TIMEOUT) | ||
assert reply == "ack" | ||
template.disconnect() | ||
template.connect() | ||
reply = template.send_and_recieve(test_queue, "test", str).result(timeout=_TIMEOUT) | ||
assert reply == "ack" | ||
|
||
|
||
def acknowledge(template: MessagingTemplate, test_queue: str) -> None: | ||
def server(ctx: MessageContext, message: str) -> None: | ||
reply_queue = ctx.reply_destination | ||
if reply_queue is None: | ||
raise RuntimeError("reply queue is None") | ||
template.send(reply_queue, "ack") | ||
|
||
def _mock_template() -> Tuple[MessagingTemplate, MagicMock]: | ||
conn = MagicMock() | ||
return StompMessagingTemplate(conn), conn | ||
template.subscribe(test_queue, server) |