From 1960d4bb1d5ab533bece3eb3331826bf0ff4f533 Mon Sep 17 00:00:00 2001 From: Nicholas Rempel Date: Thu, 11 Jul 2019 11:03:51 -0700 Subject: [PATCH 1/6] Update basicmessage admin to check is_ready instead of is_active Signed-off-by: Nicholas Rempel --- aries_cloudagent/messaging/basicmessage/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/messaging/basicmessage/routes.py b/aries_cloudagent/messaging/basicmessage/routes.py index 3c15786279..d9c75d10dd 100644 --- a/aries_cloudagent/messaging/basicmessage/routes.py +++ b/aries_cloudagent/messaging/basicmessage/routes.py @@ -39,7 +39,7 @@ async def connections_send_message(request: web.BaseRequest): except StorageNotFoundError: raise web.HTTPNotFound() - if connection.is_active: + if connection.is_ready: msg = BasicMessage(content=params["content"]) await outbound_handler(msg, connection_id=connection_id) From 107dcf2d1e21485cb0cbe5a778b28fbb1c6a4a13 Mon Sep 17 00:00:00 2001 From: Nicholas Rempel Date: Thu, 11 Jul 2019 11:56:38 -0700 Subject: [PATCH 2/6] Add basic message admin send tests Signed-off-by: Nicholas Rempel --- .../messaging/basicmessage/tests/__init__.py | 0 .../basicmessage/tests/test_routes.py | 98 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 aries_cloudagent/messaging/basicmessage/tests/__init__.py create mode 100644 aries_cloudagent/messaging/basicmessage/tests/test_routes.py diff --git a/aries_cloudagent/messaging/basicmessage/tests/__init__.py b/aries_cloudagent/messaging/basicmessage/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/messaging/basicmessage/tests/test_routes.py b/aries_cloudagent/messaging/basicmessage/tests/test_routes.py new file mode 100644 index 0000000000..809999c9fa --- /dev/null +++ b/aries_cloudagent/messaging/basicmessage/tests/test_routes.py @@ -0,0 +1,98 @@ +from asynctest import TestCase as AsyncTestCase +from asynctest import mock as async_mock + +from .. import routes as test_module + +from ....storage.error import StorageNotFoundError + + +class TestBasicMessageRoutes(AsyncTestCase): + async def test_connections_send_message(self): + mock_request = async_mock.MagicMock() + mock_request.json = async_mock.CoroutineMock() + + mock_request.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "BasicMessage", autospec=True + ) as mock_basic_message, async_mock.patch.object( + test_module, "ConnectionManager", autospec=True + ) as mock_conn_manager: + + mock_conn_manager.return_value.log_activity = async_mock.CoroutineMock() + + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() + + test_module.web.json_response = async_mock.CoroutineMock() + + res = await test_module.connections_send_message(mock_request) + test_module.web.json_response.assert_called_once_with({}) + mock_conn_manager.return_value.log_activity.assert_called_once_with( + mock_connection_record.retrieve_by_id.return_value, + "message", + mock_connection_record.retrieve_by_id.return_value.DIRECTION_SENT, + {"content": mock_request.json.return_value["content"]}, + ) + mock_basic_message.assert_called_once() + + async def test_connections_send_message_no_conn_record(self): + mock_request = async_mock.MagicMock() + mock_request.json = async_mock.CoroutineMock() + + mock_request.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "BasicMessage", autospec=True + ) as mock_basic_message, async_mock.patch.object( + test_module, "ConnectionManager", autospec=True + ) as mock_conn_manager: + + # Emulate storage not found (bad connection id) + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock( + side_effect=StorageNotFoundError + ) + + mock_conn_manager.return_value.log_activity = async_mock.CoroutineMock() + + test_module.web.json_response = async_mock.CoroutineMock() + + with self.assertRaises(test_module.web.HTTPNotFound): + await test_module.connections_send_message(mock_request) + + async def test_connections_send_message_not_ready(self): + mock_request = async_mock.MagicMock() + mock_request.json = async_mock.CoroutineMock() + + mock_request.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "BasicMessage", autospec=True + ) as mock_basic_message, async_mock.patch.object( + test_module, "ConnectionManager", autospec=True + ) as mock_conn_manager: + + # Emulate connection not ready + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() + mock_connection_record.retrieve_by_id.return_value.is_ready = False + + mock_conn_manager.return_value.log_activity = async_mock.CoroutineMock() + + test_module.web.json_response = async_mock.CoroutineMock() + + await test_module.connections_send_message(mock_request) + mock_basic_message.assert_not_called() From 53ae0d6a597cc4301e2bbb0a2138d40feaefee58 Mon Sep 17 00:00:00 2001 From: Nicholas Rempel Date: Thu, 11 Jul 2019 14:34:58 -0700 Subject: [PATCH 3/6] Update actionmenu routes to check is_ready, add tests Signed-off-by: Nicholas Rempel --- .../messaging/actionmenu/routes.py | 6 +- .../messaging/actionmenu/tests/__init__.py | 0 .../messaging/actionmenu/tests/test_routes.py | 235 ++++++++++++++++++ 3 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 aries_cloudagent/messaging/actionmenu/tests/__init__.py create mode 100644 aries_cloudagent/messaging/actionmenu/tests/test_routes.py diff --git a/aries_cloudagent/messaging/actionmenu/routes.py b/aries_cloudagent/messaging/actionmenu/routes.py index f335ee8d83..018d591890 100644 --- a/aries_cloudagent/messaging/actionmenu/routes.py +++ b/aries_cloudagent/messaging/actionmenu/routes.py @@ -99,7 +99,7 @@ async def actionmenu_perform(request: web.BaseRequest): except StorageNotFoundError: raise web.HTTPNotFound() - if connection.is_active: + if connection.is_ready: msg = Perform(name=params["name"], params=params.get("params")) await outbound_handler(msg, connection_id=connection_id) return web.json_response({}) @@ -126,7 +126,7 @@ async def actionmenu_request(request: web.BaseRequest): LOGGER.debug("Connection not found for action menu request: %s", connection_id) raise web.HTTPNotFound() - if connection.is_active: + if connection.is_ready: msg = MenuRequest() await outbound_handler(msg, connection_id=connection_id) return web.json_response({}) @@ -163,7 +163,7 @@ async def actionmenu_send(request: web.BaseRequest): ) raise web.HTTPNotFound() - if connection.is_active: + if connection.is_ready: await outbound_handler(msg, connection_id=connection_id) return web.json_response({}) diff --git a/aries_cloudagent/messaging/actionmenu/tests/__init__.py b/aries_cloudagent/messaging/actionmenu/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/messaging/actionmenu/tests/test_routes.py b/aries_cloudagent/messaging/actionmenu/tests/test_routes.py new file mode 100644 index 0000000000..3ac9b899cc --- /dev/null +++ b/aries_cloudagent/messaging/actionmenu/tests/test_routes.py @@ -0,0 +1,235 @@ +from asynctest import TestCase as AsyncTestCase +from asynctest import mock as async_mock + +from .. import routes as test_module + +from ....storage.error import StorageNotFoundError + + +class TestActionMenuRoutes(AsyncTestCase): + async def test_actionmenu_perform(self): + mock_request = async_mock.MagicMock() + mock_request.json = async_mock.CoroutineMock() + + mock_request.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "Perform", autospec=True + ) as mock_perform: + + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() + + test_module.web.json_response = async_mock.CoroutineMock() + + res = await test_module.actionmenu_perform(mock_request) + test_module.web.json_response.assert_called_once_with({}) + mock_request.app["outbound_message_router"].assert_called_once_with( + mock_perform.return_value, connection_id=mock_request.match_info["id"] + ) + + async def test_actionmenu_perform_no_conn_record(self): + mock_request = async_mock.MagicMock() + mock_request.json = async_mock.CoroutineMock() + + mock_request.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "Perform", autospec=True + ) as mock_perform: + + # Emulate storage not found (bad connection id) + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock( + side_effect=StorageNotFoundError + ) + + test_module.web.json_response = async_mock.CoroutineMock() + + with self.assertRaises(test_module.web.HTTPNotFound): + await test_module.actionmenu_perform(mock_request) + + async def test_actionmenu_perform_conn_not_ready(self): + mock_request = async_mock.MagicMock() + mock_request.json = async_mock.CoroutineMock() + + mock_request.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "Perform", autospec=True + ) as mock_perform: + + # Emulate connection not ready + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() + mock_connection_record.retrieve_by_id.return_value.is_ready = False + + test_module.web.json_response = async_mock.CoroutineMock() + + with self.assertRaises(test_module.web.HTTPForbidden): + await test_module.actionmenu_perform(mock_request) + + async def test_actionmenu_request(self): + mock_request = async_mock.MagicMock() + mock_request.json = async_mock.CoroutineMock() + + mock_request.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "MenuRequest", autospec=True + ) as menu_request: + + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() + + test_module.web.json_response = async_mock.CoroutineMock() + + res = await test_module.actionmenu_request(mock_request) + test_module.web.json_response.assert_called_once_with({}) + mock_request.app["outbound_message_router"].assert_called_once_with( + menu_request.return_value, connection_id=mock_request.match_info["id"] + ) + + async def test_actionmenu_request_no_conn_record(self): + mock_request = async_mock.MagicMock() + mock_request.json = async_mock.CoroutineMock() + + mock_request.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "Perform", autospec=True + ) as mock_perform: + + # Emulate storage not found (bad connection id) + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock( + side_effect=StorageNotFoundError + ) + + test_module.web.json_response = async_mock.CoroutineMock() + + with self.assertRaises(test_module.web.HTTPNotFound): + await test_module.actionmenu_request(mock_request) + + async def test_actionmenu_request_conn_not_ready(self): + mock_request = async_mock.MagicMock() + mock_request.json = async_mock.CoroutineMock() + + mock_request.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "Perform", autospec=True + ) as mock_perform: + + # Emulate connection not ready + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() + mock_connection_record.retrieve_by_id.return_value.is_ready = False + + test_module.web.json_response = async_mock.CoroutineMock() + + with self.assertRaises(test_module.web.HTTPForbidden): + await test_module.actionmenu_request(mock_request) + + async def test_actionmenu_send(self): + mock_request = async_mock.MagicMock() + mock_request.json = async_mock.CoroutineMock() + + mock_request.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "Menu", autospec=True + ) as mock_menu: + + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() + test_module.web.json_response = async_mock.CoroutineMock() + mock_menu.deserialize = async_mock.MagicMock() + + res = await test_module.actionmenu_send(mock_request) + test_module.web.json_response.assert_called_once_with({}) + mock_request.app["outbound_message_router"].assert_called_once_with( + mock_menu.deserialize.return_value, connection_id=mock_request.match_info["id"] + ) + + async def test_actionmenu_send_no_conn_record(self): + mock_request = async_mock.MagicMock() + mock_request.json = async_mock.CoroutineMock() + + mock_request.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "Menu", autospec=True + ) as mock_menu: + + mock_menu.deserialize = async_mock.MagicMock() + + # Emulate storage not found (bad connection id) + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock( + side_effect=StorageNotFoundError + ) + + test_module.web.json_response = async_mock.CoroutineMock() + + with self.assertRaises(test_module.web.HTTPNotFound): + await test_module.actionmenu_send(mock_request) + + async def test_actionmenu_send_conn_not_ready(self): + mock_request = async_mock.MagicMock() + mock_request.json = async_mock.CoroutineMock() + + mock_request.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "Menu", autospec=True + ) as mock_menu: + + mock_menu.deserialize = async_mock.MagicMock() + + # Emulate connection not ready + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() + mock_connection_record.retrieve_by_id.return_value.is_ready = False + + test_module.web.json_response = async_mock.CoroutineMock() + + with self.assertRaises(test_module.web.HTTPForbidden): + await test_module.actionmenu_send(mock_request) + From 46f3e2fd173f6919d74f4bb60ac1fc5aebf590a5 Mon Sep 17 00:00:00 2001 From: Nicholas Rempel Date: Thu, 11 Jul 2019 14:35:59 -0700 Subject: [PATCH 4/6] Update router connection to check for readiness instead of active Signed-off-by: Nicholas Rempel --- aries_cloudagent/messaging/connections/manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/messaging/connections/manager.py b/aries_cloudagent/messaging/connections/manager.py index 57aba31c95..7f58bf5b78 100644 --- a/aries_cloudagent/messaging/connections/manager.py +++ b/aries_cloudagent/messaging/connections/manager.py @@ -885,9 +885,9 @@ async def establish_inbound( raise ConnectionManagerError( f"Routing connection not found: {inbound_connection_id}" ) - if not router.is_active: + if not router.is_ready: raise ConnectionManagerError( - f"Routing connection is not active: {inbound_connection_id}" + f"Routing connection is not ready: {inbound_connection_id}" ) connection.inbound_connection_id = inbound_connection_id From bae68fbea12597e22d4fd2b2e352916cacb155b5 Mon Sep 17 00:00:00 2001 From: Nicholas Rempel Date: Fri, 12 Jul 2019 11:19:07 -0700 Subject: [PATCH 5/6] Add tests Signed-off-by: Nicholas Rempel --- .../credentials/tests/test_routes.py | 366 ++++++++++++++++++ 1 file changed, 366 insertions(+) diff --git a/aries_cloudagent/messaging/credentials/tests/test_routes.py b/aries_cloudagent/messaging/credentials/tests/test_routes.py index 7060067ee3..81a172f695 100644 --- a/aries_cloudagent/messaging/credentials/tests/test_routes.py +++ b/aries_cloudagent/messaging/credentials/tests/test_routes.py @@ -7,6 +7,117 @@ class TestCredentialRoutes(AsyncTestCase): + async def test_credential_exchange_send(self): + mock = async_mock.MagicMock() + mock.json = async_mock.CoroutineMock() + + mock.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_credential_manager: + + test_module.web.json_response = async_mock.CoroutineMock() + + mock_credential_manager.return_value.create_offer = ( + async_mock.CoroutineMock() + ) + + mock_credential_manager.return_value.offer_credential = ( + async_mock.CoroutineMock() + ) + + mock_cred_ex_record = async_mock.MagicMock() + + mock_credential_manager.return_value.offer_credential.return_value = ( + mock_cred_ex_record, + async_mock.MagicMock(), + ) + + await test_module.credential_exchange_send(mock) + + test_module.web.json_response.assert_called_once_with( + mock_credential_manager.return_value.prepare_send.return_value.serialize.return_value + ) + + async def test_credential_exchange_send_no_conn_record(self): + mock = async_mock.MagicMock() + mock.json = async_mock.CoroutineMock() + + mock.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_connection_manager: + + test_module.web.json_response = async_mock.CoroutineMock() + + # Emulate storage not found (bad connection id) + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock( + side_effect=StorageNotFoundError + ) + + mock_connection_manager.return_value.create_offer = ( + async_mock.CoroutineMock() + ) + + mock_connection_manager.return_value.offer_credential = ( + async_mock.CoroutineMock() + ) + mock_connection_manager.return_value.offer_credential.return_value = ( + async_mock.MagicMock(), + async_mock.MagicMock(), + ) + + with self.assertRaises(test_module.web.HTTPBadRequest): + await test_module.credential_exchange_send(mock) + + async def test_credential_exchange_send_not_ready(self): + mock = async_mock.MagicMock() + mock.json = async_mock.CoroutineMock() + + mock.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_connection_manager: + + test_module.web.json_response = async_mock.CoroutineMock() + + # Emulate connection not ready + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() + mock_connection_record.retrieve_by_id.return_value.is_ready = False + + mock_connection_manager.return_value.create_offer = ( + async_mock.CoroutineMock() + ) + + mock_connection_manager.return_value.offer_credential = ( + async_mock.CoroutineMock() + ) + mock_connection_manager.return_value.offer_credential.return_value = ( + async_mock.MagicMock(), + async_mock.MagicMock(), + ) + + with self.assertRaises(test_module.web.HTTPForbidden): + await test_module.credential_exchange_send(mock) + async def test_credential_exchange_send_offer(self): mock = async_mock.MagicMock() mock.json = async_mock.CoroutineMock() @@ -117,3 +228,258 @@ async def test_credential_exchange_send_offer_not_ready(self): with self.assertRaises(test_module.web.HTTPForbidden): await test_module.credential_exchange_send_offer(mock) + + async def test_credential_exchange_send_request(self): + mock = async_mock.MagicMock() + mock.json = async_mock.CoroutineMock() + + mock.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_connection_manager, async_mock.patch.object( + test_module, "CredentialExchange", autospec=True + ) as mock_cred_ex: + + mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() + mock_cred_ex.retrieve_by_id.return_value.state = mock_cred_ex.STATE_OFFER_RECEIVED + + test_module.web.json_response = async_mock.CoroutineMock() + + mock_cred_ex_record = async_mock.MagicMock() + + mock_connection_manager.return_value.create_request.return_value = ( + mock_cred_ex_record, + async_mock.MagicMock(), + ) + + await test_module.credential_exchange_send_request(mock) + + test_module.web.json_response.assert_called_once_with( + mock_cred_ex_record.serialize.return_value + ) + + async def test_credential_exchange_send_request_no_conn_record(self): + mock = async_mock.MagicMock() + mock.json = async_mock.CoroutineMock() + + mock.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_connection_manager, async_mock.patch.object( + test_module, "CredentialExchange", autospec=True + ) as mock_cred_ex: + + test_module.web.json_response = async_mock.CoroutineMock() + + mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() + mock_cred_ex.retrieve_by_id.return_value.state = mock_cred_ex.STATE_OFFER_RECEIVED + + + # Emulate storage not found (bad connection id) + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock( + side_effect=StorageNotFoundError + ) + + mock_connection_manager.return_value.create_offer = ( + async_mock.CoroutineMock() + ) + + mock_connection_manager.return_value.offer_credential = ( + async_mock.CoroutineMock() + ) + mock_connection_manager.return_value.offer_credential.return_value = ( + async_mock.MagicMock(), + async_mock.MagicMock(), + ) + + with self.assertRaises(test_module.web.HTTPBadRequest): + await test_module.credential_exchange_send_request(mock) + + async def test_credential_exchange_send_request_not_ready(self): + mock = async_mock.MagicMock() + mock.json = async_mock.CoroutineMock() + + mock.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_connection_manager, async_mock.patch.object( + test_module, "CredentialExchange", autospec=True + ) as mock_cred_ex: + + mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() + mock_cred_ex.retrieve_by_id.return_value.state = mock_cred_ex.STATE_OFFER_RECEIVED + + test_module.web.json_response = async_mock.CoroutineMock() + + # Emulate connection not ready + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() + mock_connection_record.retrieve_by_id.return_value.is_ready = False + + mock_connection_manager.return_value.create_offer = ( + async_mock.CoroutineMock() + ) + + mock_connection_manager.return_value.offer_credential = ( + async_mock.CoroutineMock() + ) + mock_connection_manager.return_value.offer_credential.return_value = ( + async_mock.MagicMock(), + async_mock.MagicMock(), + ) + + with self.assertRaises(test_module.web.HTTPForbidden): + await test_module.credential_exchange_send_request(mock) + + + + + + + + + + + + + async def test_credential_exchange_issue(self): + mock = async_mock.MagicMock() + mock.json = async_mock.CoroutineMock() + + mock.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_connection_manager, async_mock.patch.object( + test_module, "CredentialExchange", autospec=True + ) as mock_cred_ex: + + mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() + mock_cred_ex.retrieve_by_id.return_value.state = mock_cred_ex.STATE_REQUEST_RECEIVED + + test_module.web.json_response = async_mock.CoroutineMock() + + mock_cred_ex_record = async_mock.MagicMock() + + mock_connection_manager.return_value.issue_credential.return_value = ( + mock_cred_ex_record, + async_mock.MagicMock(), + ) + + await test_module.credential_exchange_issue(mock) + + test_module.web.json_response.assert_called_once_with( + mock_cred_ex_record.serialize.return_value + ) + + async def test_credential_exchange_issue_no_conn_record(self): + mock = async_mock.MagicMock() + mock.json = async_mock.CoroutineMock() + + mock.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_connection_manager, async_mock.patch.object( + test_module, "CredentialExchange", autospec=True + ) as mock_cred_ex: + + test_module.web.json_response = async_mock.CoroutineMock() + + mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() + mock_cred_ex.retrieve_by_id.return_value.state = mock_cred_ex.STATE_REQUEST_RECEIVED + + + # Emulate storage not found (bad connection id) + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock( + side_effect=StorageNotFoundError + ) + + mock_connection_manager.return_value.create_offer = ( + async_mock.CoroutineMock() + ) + + mock_connection_manager.return_value.offer_credential = ( + async_mock.CoroutineMock() + ) + mock_connection_manager.return_value.issue_credential.return_value = ( + async_mock.MagicMock(), + async_mock.MagicMock(), + ) + + with self.assertRaises(test_module.web.HTTPBadRequest): + await test_module.credential_exchange_issue(mock) + + async def test_credential_exchange_issue_not_ready(self): + mock = async_mock.MagicMock() + mock.json = async_mock.CoroutineMock() + + mock.app = { + "outbound_message_router": async_mock.CoroutineMock(), + "request_context": "context", + } + + with async_mock.patch.object( + test_module, "ConnectionRecord", autospec=True + ) as mock_connection_record, async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_connection_manager, async_mock.patch.object( + test_module, "CredentialExchange", autospec=True + ) as mock_cred_ex: + + mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() + mock_cred_ex.retrieve_by_id.return_value.state = mock_cred_ex.STATE_REQUEST_RECEIVED + + test_module.web.json_response = async_mock.CoroutineMock() + + # Emulate connection not ready + mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() + mock_connection_record.retrieve_by_id.return_value.is_ready = False + + mock_connection_manager.return_value.create_offer = ( + async_mock.CoroutineMock() + ) + + mock_connection_manager.return_value.offer_credential = ( + async_mock.CoroutineMock() + ) + mock_connection_manager.return_value.issue_credential.return_value = ( + async_mock.MagicMock(), + async_mock.MagicMock(), + ) + + with self.assertRaises(test_module.web.HTTPForbidden): + await test_module.credential_exchange_issue(mock) + + + + + + From a82c1b0fbb8eecdb9cdf23f3b9196f32091d0b78 Mon Sep 17 00:00:00 2001 From: Nicholas Rempel Date: Fri, 12 Jul 2019 11:28:36 -0700 Subject: [PATCH 6/6] Refactor active to ready state checking Signed-off-by: Nicholas Rempel --- aries_cloudagent/dispatcher.py | 2 +- .../connections/models/connection_record.py | 5 ----- .../credentials/handlers/credential_issue_handler.py | 2 +- .../credentials/handlers/credential_offer_handler.py | 2 +- .../handlers/credential_request_handler.py | 2 +- .../handlers/forward_invitation_handler.py | 2 +- .../introduction/handlers/invitation_handler.py | 2 +- .../handlers/invitation_request_handler.py | 2 +- aries_cloudagent/messaging/request_context.py | 12 ++++++------ .../routing/handlers/route_query_request_handler.py | 2 +- .../routing/handlers/route_query_response_handler.py | 2 +- .../routing/handlers/route_update_request_handler.py | 2 +- .../handlers/route_update_response_handler.py | 2 +- .../handlers/tests/test_query_update_handlers.py | 4 ++-- .../messaging/trustping/handlers/ping_handler.py | 7 +------ aries_cloudagent/messaging/trustping/routes.py | 2 +- demo/acme.py | 12 ++++++------ demo/alice.py | 12 ++++++------ demo/faber.py | 12 ++++++------ demo/performance.py | 8 ++++---- 20 files changed, 43 insertions(+), 53 deletions(-) diff --git a/aries_cloudagent/dispatcher.py b/aries_cloudagent/dispatcher.py index 6c575272d4..bc4abaadd7 100644 --- a/aries_cloudagent/dispatcher.py +++ b/aries_cloudagent/dispatcher.py @@ -75,7 +75,7 @@ async def dispatch( context = RequestContext(base_context=self.context) context.message = message context.message_delivery = delivery - context.connection_active = connection and connection.is_active + context.connection_ready = connection and connection.is_ready context.connection_record = connection responder = DispatcherResponder( diff --git a/aries_cloudagent/messaging/connections/models/connection_record.py b/aries_cloudagent/messaging/connections/models/connection_record.py index 99a0035e43..0bce3fd8d8 100644 --- a/aries_cloudagent/messaging/connections/models/connection_record.py +++ b/aries_cloudagent/messaging/connections/models/connection_record.py @@ -439,11 +439,6 @@ async def update_activity_meta( value["meta"] = meta await storage.update_record_value(record, json.dumps(value)) - @property - def is_active(self) -> bool: - """Accessor to check if the connection is active.""" - return self.state == self.STATE_ACTIVE - @property def is_ready(self) -> str: """Accessor for connection readiness.""" diff --git a/aries_cloudagent/messaging/credentials/handlers/credential_issue_handler.py b/aries_cloudagent/messaging/credentials/handlers/credential_issue_handler.py index 7e497b222d..9a3a7942c3 100644 --- a/aries_cloudagent/messaging/credentials/handlers/credential_issue_handler.py +++ b/aries_cloudagent/messaging/credentials/handlers/credential_issue_handler.py @@ -21,7 +21,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): assert isinstance(context.message, CredentialIssue) self._logger.info(f"Received credential: {context.message.issue}") - if not context.connection_active: + if not context.connection_ready: raise HandlerException("No connection established for credential request") credential_manager = CredentialManager(context) diff --git a/aries_cloudagent/messaging/credentials/handlers/credential_offer_handler.py b/aries_cloudagent/messaging/credentials/handlers/credential_offer_handler.py index 4c26432c78..5bc60bbb8f 100644 --- a/aries_cloudagent/messaging/credentials/handlers/credential_offer_handler.py +++ b/aries_cloudagent/messaging/credentials/handlers/credential_offer_handler.py @@ -23,7 +23,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): self._logger.info("Received credential offer: %s", context.message.offer_json) - if not context.connection_active: + if not context.connection_ready: raise HandlerException("No connection established for credential offer") credential_manager = CredentialManager(context) diff --git a/aries_cloudagent/messaging/credentials/handlers/credential_request_handler.py b/aries_cloudagent/messaging/credentials/handlers/credential_request_handler.py index be94f85767..b6603dacf0 100644 --- a/aries_cloudagent/messaging/credentials/handlers/credential_request_handler.py +++ b/aries_cloudagent/messaging/credentials/handlers/credential_request_handler.py @@ -25,7 +25,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): "Received credential request: %s", context.message.serialize(as_string=True) ) - if not context.connection_active: + if not context.connection_ready: raise HandlerException("No connection established for credential request") credential_manager = CredentialManager(context) diff --git a/aries_cloudagent/messaging/introduction/handlers/forward_invitation_handler.py b/aries_cloudagent/messaging/introduction/handlers/forward_invitation_handler.py index 81b61dc08c..8d642ec468 100644 --- a/aries_cloudagent/messaging/introduction/handlers/forward_invitation_handler.py +++ b/aries_cloudagent/messaging/introduction/handlers/forward_invitation_handler.py @@ -13,7 +13,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): self._logger.debug("ForwardInvitationHandler called with context %s", context) assert isinstance(context.message, ForwardInvitation) - if not context.connection_active: + if not context.connection_ready: raise HandlerException( "No connection established for forward invitation message" ) diff --git a/aries_cloudagent/messaging/introduction/handlers/invitation_handler.py b/aries_cloudagent/messaging/introduction/handlers/invitation_handler.py index f3307d09da..873de9a67f 100644 --- a/aries_cloudagent/messaging/introduction/handlers/invitation_handler.py +++ b/aries_cloudagent/messaging/introduction/handlers/invitation_handler.py @@ -13,7 +13,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): self._logger.debug("InvitationHandler called with context %s", context) assert isinstance(context.message, Invitation) - if not context.connection_active: + if not context.connection_ready: raise HandlerException("No connection established for invitation message") service: BaseIntroductionService = await context.inject( diff --git a/aries_cloudagent/messaging/introduction/handlers/invitation_request_handler.py b/aries_cloudagent/messaging/introduction/handlers/invitation_request_handler.py index bf83feedb6..a6697f19bd 100644 --- a/aries_cloudagent/messaging/introduction/handlers/invitation_request_handler.py +++ b/aries_cloudagent/messaging/introduction/handlers/invitation_request_handler.py @@ -14,7 +14,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): self._logger.debug("InvitationRequestHandler called with context %s", context) assert isinstance(context.message, InvitationRequest) - if not context.connection_active: + if not context.connection_ready: raise HandlerException( "No connection established for invitation request message" ) diff --git a/aries_cloudagent/messaging/request_context.py b/aries_cloudagent/messaging/request_context.py index 17bba1702a..a1394113c4 100644 --- a/aries_cloudagent/messaging/request_context.py +++ b/aries_cloudagent/messaging/request_context.py @@ -30,13 +30,13 @@ def __init__( self._scope_name = base_context.scope_name self._scopes = base_context._scopes self.start_scope("request") - self._connection_active = False + self._connection_ready = False self._connection_record = None self._message = None self._message_delivery = None @property - def connection_active(self) -> bool: + def connection_ready(self) -> bool: """ Accessor for the flag indicating an active connection with the sender. @@ -44,10 +44,10 @@ def connection_active(self) -> bool: True if the connection is active, else False """ - return self._connection_active + return self._connection_ready - @connection_active.setter - def connection_active(self, active: bool): + @connection_ready.setter + def connection_ready(self, active: bool): """ Setter for the flag indicating an active connection with the sender. @@ -55,7 +55,7 @@ def connection_active(self, active: bool): active: The new active value """ - self._connection_active = active + self._connection_ready = active @property def connection_record(self) -> ConnectionRecord: diff --git a/aries_cloudagent/messaging/routing/handlers/route_query_request_handler.py b/aries_cloudagent/messaging/routing/handlers/route_query_request_handler.py index 0f85f9ddb7..9b45de4fcd 100644 --- a/aries_cloudagent/messaging/routing/handlers/route_query_request_handler.py +++ b/aries_cloudagent/messaging/routing/handlers/route_query_request_handler.py @@ -16,7 +16,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) assert isinstance(context.message, RouteQueryRequest) - if not context.connection_active: + if not context.connection_ready: raise HandlerException("Cannot query routes: no active connection") # TODO implement pagination diff --git a/aries_cloudagent/messaging/routing/handlers/route_query_response_handler.py b/aries_cloudagent/messaging/routing/handlers/route_query_response_handler.py index 3e8a97acfe..45d25885b5 100644 --- a/aries_cloudagent/messaging/routing/handlers/route_query_response_handler.py +++ b/aries_cloudagent/messaging/routing/handlers/route_query_response_handler.py @@ -14,7 +14,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) assert isinstance(context.message, RouteQueryResponse) - if not context.connection_active: + if not context.connection_ready: raise HandlerException( "Cannot handle route query response: no active connection" ) diff --git a/aries_cloudagent/messaging/routing/handlers/route_update_request_handler.py b/aries_cloudagent/messaging/routing/handlers/route_update_request_handler.py index e0432b76fa..b656517e19 100644 --- a/aries_cloudagent/messaging/routing/handlers/route_update_request_handler.py +++ b/aries_cloudagent/messaging/routing/handlers/route_update_request_handler.py @@ -16,7 +16,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) assert isinstance(context.message, RouteUpdateRequest) - if not context.connection_active: + if not context.connection_ready: raise HandlerException("Cannot update routes: no active connection") mgr = RoutingManager(context) diff --git a/aries_cloudagent/messaging/routing/handlers/route_update_response_handler.py b/aries_cloudagent/messaging/routing/handlers/route_update_response_handler.py index ecd44b0d92..223c0488ef 100644 --- a/aries_cloudagent/messaging/routing/handlers/route_update_response_handler.py +++ b/aries_cloudagent/messaging/routing/handlers/route_update_response_handler.py @@ -19,7 +19,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) assert isinstance(context.message, RouteUpdateResponse) - if not context.connection_active: + if not context.connection_ready: raise HandlerException("Cannot handle updated routes: no active connection") conn_mgr = ConnectionManager(context) diff --git a/aries_cloudagent/messaging/routing/handlers/tests/test_query_update_handlers.py b/aries_cloudagent/messaging/routing/handlers/tests/test_query_update_handlers.py index 8a46d83d21..0266c1cd83 100644 --- a/aries_cloudagent/messaging/routing/handlers/tests/test_query_update_handlers.py +++ b/aries_cloudagent/messaging/routing/handlers/tests/test_query_update_handlers.py @@ -26,7 +26,7 @@ @pytest.fixture() def request_context() -> RequestContext: ctx = RequestContext() - ctx.connection_active = True + ctx.connection_ready = True ctx.connection_record = ConnectionRecord(connection_id="conn-id") ctx.message_delivery = MessageDelivery(sender_verkey=TEST_VERKEY) ctx.injector.bind_instance(BaseStorage, BasicStorage()) @@ -48,7 +48,7 @@ async def test_query_none(self, request_context): @pytest.mark.asyncio async def test_no_connection(self, request_context): - request_context.connection_active = False + request_context.connection_ready = False request_context.message = RouteQueryRequest() handler = RouteQueryRequestHandler() responder = MockResponder() diff --git a/aries_cloudagent/messaging/trustping/handlers/ping_handler.py b/aries_cloudagent/messaging/trustping/handlers/ping_handler.py index 78a5fb7880..0583a933c6 100644 --- a/aries_cloudagent/messaging/trustping/handlers/ping_handler.py +++ b/aries_cloudagent/messaging/trustping/handlers/ping_handler.py @@ -25,12 +25,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): "Received trust ping from: %s", context.message_delivery.sender_did ) - response_state = ( - context.connection_record - and context.connection_record.state - == context.connection_record.STATE_RESPONSE - ) - if not context.connection_active and not response_state: + if not context.connection_ready: self._logger.info( "Connection not active, skipping ping response: %s", context.message_delivery.sender_did, diff --git a/aries_cloudagent/messaging/trustping/routes.py b/aries_cloudagent/messaging/trustping/routes.py index 6f757e1607..eaa0f22f00 100644 --- a/aries_cloudagent/messaging/trustping/routes.py +++ b/aries_cloudagent/messaging/trustping/routes.py @@ -26,7 +26,7 @@ async def connections_send_ping(request: web.BaseRequest): except StorageNotFoundError: raise web.HTTPNotFound() - if connection.is_active or connection.state == connection.STATE_RESPONSE: + if connection.is_ready: msg = Ping() await outbound_handler(msg, connection_id=connection_id) diff --git a/demo/acme.py b/demo/acme.py index 7b6dbee827..9d44d003c6 100644 --- a/demo/acme.py +++ b/demo/acme.py @@ -19,23 +19,23 @@ class AcmeAgent(DemoAgent): def __init__(self, http_port: int, admin_port: int, **kwargs): super().__init__("Acme Agent", http_port, admin_port, prefix="Acme", **kwargs) self.connection_id = None - self._connection_active = asyncio.Future() + self._connection_ready = asyncio.Future() self.cred_state = {} # TODO define a dict to hold credential attributes based on credential_definition_id self.cred_attrs = {} async def detect_connection(self): - await self._connection_active + await self._connection_ready @property - def connection_active(self): - return self._connection_active.done() and self._connection_active.result() + def connection_ready(self): + return self._connection_ready.done() and self._connection_ready.result() async def handle_connections(self, message): if message["connection_id"] == self.connection_id: - if message["state"] == "active" and not self._connection_active.done(): + if message["state"] == "active" and not self._connection_ready.done(): self.log("Connected") - self._connection_active.set_result(True) + self._connection_ready.set_result(True) async def handle_credentials(self, message): state = message["state"] diff --git a/demo/alice.py b/demo/alice.py index 2572126c75..691b251d72 100644 --- a/demo/alice.py +++ b/demo/alice.py @@ -18,21 +18,21 @@ class AliceAgent(DemoAgent): def __init__(self, http_port: int, admin_port: int, **kwargs): super().__init__("Alice Agent", http_port, admin_port, prefix="Alice", **kwargs) self.connection_id = None - self._connection_active = asyncio.Future() + self._connection_ready = asyncio.Future() self.cred_state = {} async def detect_connection(self): - await self._connection_active + await self._connection_ready @property - def connection_active(self): - return self._connection_active.done() and self._connection_active.result() + def connection_ready(self): + return self._connection_ready.done() and self._connection_ready.result() async def handle_connections(self, message): if message["connection_id"] == self.connection_id: - if message["state"] == "active" and not self._connection_active.done(): + if message["state"] == "active" and not self._connection_ready.done(): self.log("Connected") - self._connection_active.set_result(True) + self._connection_ready.set_result(True) async def handle_credentials(self, message): state = message["state"] diff --git a/demo/faber.py b/demo/faber.py index 4bde9ac29f..d89a25aaf8 100644 --- a/demo/faber.py +++ b/demo/faber.py @@ -19,23 +19,23 @@ class FaberAgent(DemoAgent): def __init__(self, http_port: int, admin_port: int, **kwargs): super().__init__("Faber Agent", http_port, admin_port, prefix="Faber", **kwargs) self.connection_id = None - self._connection_active = asyncio.Future() + self._connection_ready = asyncio.Future() self.cred_state = {} # TODO define a dict to hold credential attributes based on credential_definition_id self.cred_attrs = {} async def detect_connection(self): - await self._connection_active + await self._connection_ready @property - def connection_active(self): - return self._connection_active.done() and self._connection_active.result() + def connection_ready(self): + return self._connection_ready.done() and self._connection_ready.result() async def handle_connections(self, message): if message["connection_id"] == self.connection_id: - if message["state"] == "active" and not self._connection_active.done(): + if message["state"] == "active" and not self._connection_ready.done(): self.log("Connected") - self._connection_active.set_result(True) + self._connection_ready.set_result(True) async def handle_credentials(self, message): state = message["state"] diff --git a/demo/performance.py b/demo/performance.py index 9fedbd873e..d7d8d9d459 100644 --- a/demo/performance.py +++ b/demo/performance.py @@ -22,16 +22,16 @@ def __init__( prefix = ident super().__init__(ident, port, port + 1, timing=timing, prefix=prefix, **kwargs) self.connection_id = None - self.connection_active = asyncio.Future() + self.connection_ready = asyncio.Future() async def detect_connection(self): - await self.connection_active + await self.connection_ready async def handle_connections(self, payload): if payload["connection_id"] == self.connection_id: - if payload["state"] == "active" and not self.connection_active.done(): + if payload["state"] == "active" and not self.connection_ready.done(): self.log("Connected") - self.connection_active.set_result(True) + self.connection_ready.set_result(True) class AliceAgent(BaseAgent):