Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

holder and routing unit test coverage #482

Merged
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
136 changes: 135 additions & 1 deletion aries_cloudagent/holder/tests/test_indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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):
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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 = "{}"
Expand All @@ -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")
Expand All @@ -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 = "{}"
Expand Down
135 changes: 135 additions & 0 deletions aries_cloudagent/holder/tests/test_routes.py
Original file line number Diff line number Diff line change
@@ -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()
8 changes: 7 additions & 1 deletion aries_cloudagent/indy/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
2 changes: 1 addition & 1 deletion aries_cloudagent/messaging/valid.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:"
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)}
)