From 4d5479b014679f53b0f665982584bfdc2c142220 Mon Sep 17 00:00:00 2001 From: sklump Date: Tue, 28 Apr 2020 14:26:14 +0000 Subject: [PATCH] holder and routing unit test coverage Signed-off-by: sklump --- aries_cloudagent/holder/tests/test_indy.py | 136 +++++++++++++++++- aries_cloudagent/holder/tests/test_routes.py | 135 +++++++++++++++++ aries_cloudagent/indy/tests/test_utils.py | 8 +- aries_cloudagent/messaging/valid.py | 2 +- .../v1_0/messages/tests/test_forward.py | 15 +- 5 files changed, 292 insertions(+), 4 deletions(-) create mode 100644 aries_cloudagent/holder/tests/test_routes.py diff --git a/aries_cloudagent/holder/tests/test_indy.py b/aries_cloudagent/holder/tests/test_indy.py index 9b3bda6d7b..e3526ac568 100644 --- a/aries_cloudagent/holder/tests/test_indy.py +++ b/aries_cloudagent/holder/tests/test_indy.py @@ -12,7 +12,6 @@ from aries_cloudagent.holder.indy import IndyHolder from aries_cloudagent.storage.error import StorageError from aries_cloudagent.storage.record import StorageRecord -from aries_cloudagent.wallet.indy import IndyWallet from ...protocols.issue_credential.v1_0.messages.inner.credential_preview import ( CredentialPreview, @@ -24,6 +23,7 @@ class TestIndyHolder(AsyncTestCase): def test_init(self): holder = IndyHolder("wallet") assert holder.wallet == "wallet" + assert "IndyHolder" in str(holder) @async_mock.patch("indy.anoncreds.prover_create_credential_req") async def test_create_credential_request(self, mock_create_credential_req): @@ -67,6 +67,40 @@ async def test_store_credential(self, mock_store_cred): assert cred_id == "cred_id" + @async_mock.patch("indy.anoncreds.prover_store_credential") + async def test_store_credential_with_mime_types(self, mock_store_cred): + with async_mock.patch.object( + test_module, "IndyStorage", async_mock.MagicMock() + ) as mock_storage: + mock_storage.return_value = async_mock.MagicMock( + add_record=async_mock.CoroutineMock() + ) + + mock_store_cred.return_value = "cred_id" + mock_wallet = async_mock.MagicMock() + + holder = IndyHolder(mock_wallet) + + CRED_DATA = {"values": {"cameo": "d29yZCB1cA=="}} + cred_id = await holder.store_credential( + "credential_definition", + CRED_DATA, + "credential_request_metadata", + {"cameo": "image/png"}, + ) + + mock_store_cred.assert_called_once_with( + wallet_handle=mock_wallet.handle, + cred_id=None, + cred_req_metadata_json=json.dumps("credential_request_metadata"), + cred_json=json.dumps(CRED_DATA), + cred_def_json=json.dumps("credential_definition"), + rev_reg_def_json=None, + ) + mock_storage.return_value.add_record.assert_called_once() + + assert cred_id == "cred_id" + @async_mock.patch("indy.non_secrets.get_wallet_record") async def test_get_credential_attrs_mime_types(self, mock_nonsec_get_wallet_record): cred_id = "credential_id" @@ -125,6 +159,23 @@ async def test_get_credential_attr_mime_type(self, mock_nonsec_get_wallet_record assert a_mime_type == dummy_tags["a"] + @async_mock.patch("indy.non_secrets.get_wallet_record") + async def test_get_credential_attr_mime_type_x(self, mock_nonsec_get_wallet_record): + cred_id = "credential_id" + dummy_tags = {"a": "1", "b": "2"} + dummy_rec = { + "type": IndyHolder.RECORD_TYPE_MIME_TYPES, + "id": cred_id, + "value": "value", + "tags": dummy_tags, + } + mock_nonsec_get_wallet_record.side_effect = test_module.StorageError() + mock_wallet = async_mock.MagicMock() + + holder = IndyHolder(mock_wallet) + + assert await holder.get_mime_type(cred_id, "a") is None + @async_mock.patch("indy.anoncreds.prover_search_credentials") @async_mock.patch("indy.anoncreds.prover_fetch_credentials") @async_mock.patch("indy.anoncreds.prover_close_credentials_search") @@ -207,6 +258,39 @@ async def test_get_credentials_for_presentation_request_by_referent( {"cred_info": {"referent": "asdb"}, "presentation_referents": ["asdb"]}, ) + @async_mock.patch("indy.anoncreds.prover_search_credentials_for_proof_req") + @async_mock.patch("indy.anoncreds.prover_fetch_credentials_for_proof_req") + @async_mock.patch("indy.anoncreds.prover_close_credentials_search_for_proof_req") + async def test_get_credentials_for_presentation_request_by_referent_default_refts( + self, + mock_prover_close_credentials_search_for_proof_req, + mock_prover_fetch_credentials_for_proof_req, + mock_prover_search_credentials_for_proof_req, + ): + mock_prover_search_credentials_for_proof_req.return_value = "search_handle" + mock_prover_fetch_credentials_for_proof_req.return_value = ( + '[{"cred_info": {"referent": "asdb"}}]' + ) + + mock_wallet = async_mock.MagicMock() + holder = IndyHolder(mock_wallet) + + PRES_REQ = { + "requested_attributes": { + "0_a_uuid": {"...": "..."}, + "1_b_uuid": {"...": "..."}, + }, + "requested_predicates": {"2_c_ge_80": {"...": "..."}}, + } + + credentials = await holder.get_credentials_for_presentation_request_by_referent( + PRES_REQ, None, 2, 3, + ) + + mock_prover_search_credentials_for_proof_req.assert_called_once_with( + mock_wallet.handle, json.dumps(PRES_REQ), json.dumps({}) + ) + @async_mock.patch("indy.anoncreds.prover_get_credential") async def test_get_credential(self, mock_get_cred): mock_get_cred.return_value = "{}" @@ -220,6 +304,26 @@ async def test_get_credential(self, mock_get_cred): assert json.loads(credential_json) == {} + @async_mock.patch("indy.anoncreds.prover_get_credential") + async def test_get_credential_not_found(self, mock_get_cred): + mock_get_cred.side_effect = IndyError(error_code=ErrorCode.WalletItemNotFound) + + mock_wallet = async_mock.MagicMock() + holder = IndyHolder(mock_wallet) + + with self.assertRaises(test_module.WalletNotFoundError): + await holder.get_credential("credential_id") + + @async_mock.patch("indy.anoncreds.prover_get_credential") + async def test_get_credential_x(self, mock_get_cred): + mock_get_cred.side_effect = IndyError("unexpected failure") + + mock_wallet = async_mock.MagicMock() + holder = IndyHolder(mock_wallet) + + with self.assertRaises(test_module.HolderError): + await holder.get_credential("credential_id") + @async_mock.patch("indy.anoncreds.prover_delete_credential") @async_mock.patch("indy.non_secrets.get_wallet_record") @async_mock.patch("indy.non_secrets.delete_wallet_record") @@ -246,6 +350,36 @@ async def test_delete_credential( mock_wallet.handle, "credential_id" ) + @async_mock.patch("indy.anoncreds.prover_delete_credential") + @async_mock.patch("indy.non_secrets.get_wallet_record") + @async_mock.patch("indy.non_secrets.delete_wallet_record") + async def test_delete_credential_x( + self, + mock_nonsec_del_wallet_record, + mock_nonsec_get_wallet_record, + mock_prover_del_cred, + ): + mock_wallet = async_mock.MagicMock() + holder = IndyHolder(mock_wallet) + + mock_nonsec_get_wallet_record.side_effect = test_module.StorageNotFoundError() + mock_prover_del_cred.side_effect = IndyError( + error_code=ErrorCode.WalletItemNotFound + ) + + with self.assertRaises(test_module.WalletNotFoundError): + await holder.delete_credential("credential_id") + mock_prover_del_cred.assert_called_once_with( + mock_wallet.handle, "credential_id" + ) + + mock_prover_del_cred.side_effect = IndyError( + error_code=ErrorCode.CommonInvalidParam1 + ) + with self.assertRaises(test_module.HolderError): + await holder.delete_credential("credential_id") + assert mock_prover_del_cred.call_count == 2 + @async_mock.patch("indy.anoncreds.prover_create_proof") async def test_create_presentation(self, mock_create_proof): mock_create_proof.return_value = "{}" diff --git a/aries_cloudagent/holder/tests/test_routes.py b/aries_cloudagent/holder/tests/test_routes.py new file mode 100644 index 0000000000..4d04a7afb5 --- /dev/null +++ b/aries_cloudagent/holder/tests/test_routes.py @@ -0,0 +1,135 @@ +import json +import pytest + +from asynctest import TestCase as AsyncTestCase +from asynctest import mock as async_mock + +from aiohttp.web import HTTPForbidden + +from ...config.injection_context import InjectionContext +from ...wallet.base import BaseWallet + +from .. import routes as test_module +from ..base import BaseHolder + + +class TestHolderRoutes(AsyncTestCase): + def setUp(self): + self.context = InjectionContext(enforce_typing=False) + + self.wallet = async_mock.create_autospec(BaseWallet) + self.context.injector.bind_instance(BaseWallet, self.wallet) + + self.holder = async_mock.create_autospec(BaseHolder) + self.app = { + "request_context": self.context, + } + + async def test_credentials_get(self): + request = async_mock.MagicMock( + app=self.app, match_info={"credential_id": "dummy"} + ) + + request.app["request_context"].inject = async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + get_credential=async_mock.CoroutineMock( + return_value=json.dumps({"hello": "world"}) + ) + ) + ) + + with async_mock.patch.object( + test_module.web, "json_response", async_mock.Mock() + ) as json_response: + result = await test_module.credentials_get(request) + json_response.assert_called_once_with({"hello": "world"}) + assert result is json_response.return_value + + async def test_credentials_get_not_found(self): + request = async_mock.MagicMock( + app=self.app, match_info={"credential_id": "dummy"} + ) + request.app["request_context"].inject = async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + get_credential=async_mock.CoroutineMock( + side_effect=test_module.WalletNotFoundError() + ) + ) + ) + with self.assertRaises(test_module.web.HTTPNotFound): + await test_module.credentials_get(request) + + async def test_credentials_remove(self): + request = async_mock.MagicMock( + app=self.app, match_info={"credential_id": "dummy"} + ) + + request.app["request_context"].inject = async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + delete_credential=async_mock.CoroutineMock(return_value=None) + ) + ) + + with async_mock.patch.object( + test_module.web, "json_response", async_mock.Mock() + ) as json_response: + result = await test_module.credentials_remove(request) + json_response.assert_called_once_with({}) + assert result is json_response.return_value + + async def test_credentials_remove_not_found(self): + request = async_mock.MagicMock( + app=self.app, match_info={"credential_id": "dummy"} + ) + request.app["request_context"].inject = async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + delete_credential=async_mock.CoroutineMock( + side_effect=test_module.WalletNotFoundError() + ) + ) + ) + with self.assertRaises(test_module.web.HTTPNotFound): + await test_module.credentials_remove(request) + + async def test_credentials_list(self): + request = async_mock.MagicMock( + app=self.app, query={"start": "0", "count": "10"} + ) + + request.app["request_context"].inject = async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + get_credentials=async_mock.CoroutineMock( + return_value={"hello": "world"} + ) + ) + ) + + with async_mock.patch.object( + test_module.web, "json_response", async_mock.Mock() + ) as json_response: + result = await test_module.credentials_list(request) + json_response.assert_called_once_with({"results": {"hello": "world"}}) + assert result is json_response.return_value + + async def test_credentials_list_x_holder(self): + request = async_mock.MagicMock( + app=self.app, query={"start": "0", "count": "10"} + ) + + request.app["request_context"].inject = async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + get_credentials=async_mock.CoroutineMock( + side_effect=test_module.HolderError() + ) + ) + ) + + with self.assertRaises(test_module.web.HTTPBadRequest): + await test_module.credentials_list(request) + + async def test_register(self): + mock_app = async_mock.MagicMock() + mock_app.add_routes = async_mock.MagicMock() + + await test_module.register(mock_app) + mock_app.add_routes.assert_called_once() diff --git a/aries_cloudagent/indy/tests/test_utils.py b/aries_cloudagent/indy/tests/test_utils.py index 742f0e35ec..4e36300340 100644 --- a/aries_cloudagent/indy/tests/test_utils.py +++ b/aries_cloudagent/indy/tests/test_utils.py @@ -6,7 +6,7 @@ import indy.blob_storage -from .. import create_tails_reader +from .. import create_tails_reader, create_tails_writer TAILS_DIR = "/tmp/indy/revocation/tails_files" TAILS_HASH = "8UW1Sz5cqoUnK9hqQk7nvtKK65t7Chu3ui866J23sFyJ" @@ -31,3 +31,9 @@ async def test_tails_reader(self): rmtree(TAILS_DIR, ignore_errors=True) with self.assertRaises(FileNotFoundError): await create_tails_reader(TAILS_LOCAL) + + async def test_tails_writer(self): + makedirs(TAILS_DIR, exist_ok=True) + assert await create_tails_writer(TAILS_DIR) + + rmtree(TAILS_DIR, ignore_errors=True) diff --git a/aries_cloudagent/messaging/valid.py b/aries_cloudagent/messaging/valid.py index d6da19d1a6..d785d23da2 100644 --- a/aries_cloudagent/messaging/valid.py +++ b/aries_cloudagent/messaging/valid.py @@ -195,7 +195,7 @@ def __init__(self): class IndyRevRegId(Regexp): """Validate value against indy revocation registry identifier specification.""" - EXAMPLE = f"WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0" + EXAMPLE = "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0" PATTERN = ( rf"^([{B58}]{{21,22}}):4:" rf"([{B58}]{{21,22}}):3:" diff --git a/aries_cloudagent/protocols/routing/v1_0/messages/tests/test_forward.py b/aries_cloudagent/protocols/routing/v1_0/messages/tests/test_forward.py index d100b9573c..3c6e9fdda4 100644 --- a/aries_cloudagent/protocols/routing/v1_0/messages/tests/test_forward.py +++ b/aries_cloudagent/protocols/routing/v1_0/messages/tests/test_forward.py @@ -1,4 +1,6 @@ -from ..forward import Forward +import json + +from ..forward import Forward, ForwardSchema from ...message_types import FORWARD, PROTOCOL_PACKAGE from unittest import mock, TestCase @@ -41,3 +43,14 @@ def test_make_model(self): data = message.serialize() model_instance = Forward.deserialize(data) assert isinstance(model_instance, Forward) + + def test_make_model_str(self): + MSG = {"some": "msg"} + message = Forward(to="to", msg=json.dumps(MSG)) + data = message.serialize() + model_instance = Forward.deserialize(data) + assert isinstance(model_instance, Forward) + + assert {"msg": MSG} == ForwardSchema().handle_str_message( + data={"msg": json.dumps(MSG)} + )