diff --git a/aries_cloudagent/transport/outbound/tests/test_http_transport.py b/aries_cloudagent/transport/outbound/tests/test_http_transport.py index 532f8f1b8d..713817b281 100644 --- a/aries_cloudagent/transport/outbound/tests/test_http_transport.py +++ b/aries_cloudagent/transport/outbound/tests/test_http_transport.py @@ -1,13 +1,17 @@ import asyncio +import pytest from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop from aiohttp import web +from asynctest import mock as async_mock from ....config.injection_context import InjectionContext from ....utils.stats import Collector from ...outbound.message import OutboundMessage +from ...wire_format import JsonWireFormat +from ..base import OutboundTransportError from ..http import HttpTransport @@ -38,6 +42,7 @@ async def send_message(transport, payload, endpoint): await transport.handle_message(self.context, payload, endpoint) transport = HttpTransport() + await asyncio.wait_for(send_message(transport, "{}", endpoint=server_addr), 5.0) assert self.message_results == [{}] @@ -61,3 +66,29 @@ async def send_message(transport, payload, endpoint): "outbound-http:connect": 1, "outbound-http:POST": 1, } + + @unittest_run_loop + async def test_transport_coverage(self): + transport = HttpTransport() + assert transport.wire_format is None + transport.wire_format = JsonWireFormat() + assert transport.wire_format is not None + + await transport.start() + + with pytest.raises(OutboundTransportError): + await transport.handle_message(None, None, None) + + with async_mock.patch.object( + transport, "client_session", async_mock.MagicMock() + ) as mock_session: + mock_response = async_mock.MagicMock(status=404) + mock_session.post = async_mock.MagicMock( + return_value=async_mock.MagicMock( + __aenter__=async_mock.CoroutineMock(return_value=mock_response) + ) + ) + with pytest.raises(OutboundTransportError): + await transport.handle_message(None, "dummy", "http://localhost") + + await transport.__aexit__(KeyError, KeyError("just a drill"), None) diff --git a/aries_cloudagent/transport/outbound/tests/test_manager.py b/aries_cloudagent/transport/outbound/tests/test_manager.py index 6c8ec19efa..805b041641 100644 --- a/aries_cloudagent/transport/outbound/tests/test_manager.py +++ b/aries_cloudagent/transport/outbound/tests/test_manager.py @@ -22,9 +22,14 @@ def test_register_path(self): assert mgr.get_registered_transport_for_scheme("http") assert mgr.MAX_RETRY_COUNT == 4 + assert mgr.get_registered_transport_for_scheme("xmpp") is None + with self.assertRaises(OutboundTransportRegistrationError): mgr.register("http") + with self.assertRaises(OutboundTransportRegistrationError): + mgr.register("no.such.module.path") + def test_maximum_retry_count(self): context = InjectionContext() context.update_settings({"transport.max_outbound_retry": 5}) @@ -67,6 +72,7 @@ async def test_send_message(self): assert mgr.get_running_transport_for_scheme("http") == "transport_cls" message = OutboundMessage(payload="{}") + assert "payload" in str(message) message.target = ConnectionTarget( endpoint="http://localhost", recipient_keys=[1, 2], @@ -89,11 +95,33 @@ async def test_send_message(self): transport.wire_format.encode_message.return_value, message.target.endpoint, ) + + with self.assertRaises(OutboundDeliveryError): + mgr.get_running_transport_for_endpoint("localhost") + + message.target = ConnectionTarget( + endpoint="localhost", recipient_keys=[1, 2], routing_keys=[3], sender_key=4, + ) + with self.assertRaises(OutboundDeliveryError) as context: + mgr.enqueue_message(send_context, message) + assert "No supported transport" in str(context.exception) + await mgr.stop() assert mgr.get_running_transport_for_scheme("http") is None transport.stop.assert_awaited_once_with() + async def test_stop_cancel(self): + context = InjectionContext() + context.update_settings({"transport.outbound_configs": ["http"]}) + mgr = OutboundTransportManager(context) + mgr._process_task = async_mock.MagicMock( + done=async_mock.MagicMock(return_value=False), cancel=async_mock.MagicMock() + ) + mgr.running_transports = {} + await mgr.stop() + mgr._process_task.cancel.assert_called_once() + async def test_enqueue_webhook(self): context = InjectionContext() mgr = OutboundTransportManager(context) @@ -126,3 +154,42 @@ async def test_enqueue_webhook(self): assert json.loads(queued.payload) == test_payload assert queued.retries == test_attempts - 1 assert queued.state == QueuedOutboundMessage.STATE_PENDING + + async def test_process_done_x(self): + mock_task = async_mock.MagicMock( + done=async_mock.MagicMock(return_value=True), + exception=async_mock.MagicMock(return_value=KeyError("No such key")), + ) + context = InjectionContext() + mgr = OutboundTransportManager(context) + + with async_mock.patch.object( + mgr, "_process_task", async_mock.MagicMock() + ) as mock_mgr_process: + mock_mgr_process.done = async_mock.MagicMock(return_value=True) + mgr._process_done(mock_task) + + async def test_process_finished_x(self): + mock_queued = async_mock.MagicMock(retries=1) + mock_task = async_mock.MagicMock(exc_info=(KeyError, KeyError("nope"), None),) + context = InjectionContext() + mgr = OutboundTransportManager(context) + + with async_mock.patch.object( + mgr, "process_queued", async_mock.MagicMock() + ) as mock_mgr_process: + mgr.finished_encode(mock_queued, mock_task) + mgr.finished_deliver(mock_queued, mock_task) + mgr.finished_deliver(mock_queued, mock_task) + + async def test_process_loop_x(self): + mock_queued = async_mock.MagicMock( + state=QueuedOutboundMessage.STATE_DONE, error=KeyError() + ) + + context = InjectionContext() + mock_handle_not_delivered = async_mock.MagicMock() + mgr = OutboundTransportManager(context, mock_handle_not_delivered) + mgr.outbound_buffer.append(mock_queued) + + await mgr._process_loop() diff --git a/aries_cloudagent/transport/queue/tests/test_basic_queue.py b/aries_cloudagent/transport/queue/tests/test_basic_queue.py index 91115e7074..fd078bf71c 100644 --- a/aries_cloudagent/transport/queue/tests/test_basic_queue.py +++ b/aries_cloudagent/transport/queue/tests/test_basic_queue.py @@ -27,6 +27,9 @@ async def test_enqueue_dequeue(self): with self.assertRaises(asyncio.TimeoutError): await queue.dequeue(timeout=0) + queue.task_done() + await queue.join() + async def test_async_iter(self): queue = BasicMessageQueue() diff --git a/aries_cloudagent/transport/tests/test_pack_format.py b/aries_cloudagent/transport/tests/test_pack_format.py index e7d474e30f..0f05891127 100644 --- a/aries_cloudagent/transport/tests/test_pack_format.py +++ b/aries_cloudagent/transport/tests/test_pack_format.py @@ -7,9 +7,11 @@ from ...protocols.routing.v1_0.message_types import FORWARD from ...wallet.base import BaseWallet from ...wallet.basic import BasicWallet +from ...wallet.error import WalletError -from ..error import MessageParseError +from ..error import MessageEncodeError, MessageParseError from ..pack_format import PackWireFormat +from .. import pack_format as test_module class TestPackWireFormat(AsyncTestCase): @@ -34,7 +36,6 @@ def setUp(self): async def test_errors(self): serializer = PackWireFormat() - bad_values = [None, "", "1", "[]", "{..."] for message_json in bad_values: @@ -43,6 +44,68 @@ async def test_errors(self): self.context, message_json ) + x_message = { + "@id": TestPackWireFormat.test_message_id, + "~thread": {"thid": TestPackWireFormat.test_thread_id}, + "~transport": {"return_route": "all"}, + "content": "{}", + } + + serializer.task_queue = None + with async_mock.patch.object( + serializer, "unpack", async_mock.CoroutineMock() + ) as mock_unpack: + mock_unpack.return_value = "{missing-brace" + with self.assertRaises(MessageParseError) as context: + await serializer.parse_message(self.context, json.dumps(x_message)) + assert "Message JSON parsing failed" in str(context.exception) + + serializer = PackWireFormat() + serializer.task_queue = None + with async_mock.patch.object( + serializer, "unpack", async_mock.CoroutineMock() + ) as mock_unpack: + mock_unpack.return_value = json.dumps([1, 2, 3]) + with self.assertRaises(MessageParseError) as context: + await serializer.parse_message(self.context, json.dumps(x_message)) + assert "Message JSON result is not an object" in str(context.exception) + + with self.assertRaises(MessageParseError): + await serializer.unpack(InjectionContext(), "...", None) + + async def test_pack_x(self): + serializer = PackWireFormat() + + with self.assertRaises(MessageEncodeError): + await serializer.pack(self.context, None, None, None, None) + + with self.assertRaises(MessageEncodeError): + await serializer.pack(InjectionContext(), None, ["key"], None, ["key"]) + + mock_wallet = async_mock.MagicMock( + pack_message=async_mock.CoroutineMock(side_effect=WalletError()) + ) + context = InjectionContext(enforce_typing=False) + context.injector.bind_instance(BaseWallet, mock_wallet) + with self.assertRaises(MessageEncodeError): + await serializer.pack(context, None, ["key"], None, ["key"]) + + context.injector.clear_binding(BaseWallet) + mock_wallet = async_mock.MagicMock( + pack_message=async_mock.CoroutineMock( + side_effect=[json.dumps("message").encode("utf-8"), WalletError()] + ) + ) + context.injector.bind_instance(BaseWallet, mock_wallet) + with async_mock.patch.object( + test_module, "Forward", async_mock.MagicMock() + ) as mock_forward: + mock_forward.return_value = async_mock.MagicMock( + to_json=async_mock.MagicMock() + ) + with self.assertRaises(MessageEncodeError): + await serializer.pack(context, None, ["key"], ["key"], ["key"]) + async def test_unpacked(self): serializer = PackWireFormat() message_json = json.dumps(self.test_message) @@ -90,6 +153,12 @@ async def test_encode_decode(self): assert delivery.thread_id == self.test_thread_id assert delivery.direct_response_mode == "all" + plain_json = json.dumps("plain") + assert ( + await serializer.encode_message(self.context, plain_json, None, None, None) + == plain_json + ) + async def test_forward(self): local_did = await self.wallet.create_local_did(self.test_seed) router_did = await self.wallet.create_local_did(self.test_routing_seed)