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

Unit tests messaging #633

Merged
merged 5 commits into from
Jul 29, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 20 additions & 0 deletions aries_cloudagent/core/tests/test_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ...connections.models.connection_record import ConnectionRecord
from ...core.protocol_registry import ProtocolRegistry
from ...messaging.agent_message import AgentMessage, AgentMessageSchema
from ...messaging.responder import MockResponder
from ...messaging.util import datetime_now

from ...protocols.problem_report.v1_0.message import ProblemReport
Expand Down Expand Up @@ -301,3 +302,22 @@ async def test_create_outbound_send_webhook(self):
result = await responder.create_outbound(message)
assert json.loads(result.payload)["@type"] == StubAgentMessage.Meta.message_type
await responder.send_webhook("topic", "payload")

async def test_create_send_outbound(self):
message = StubAgentMessage()
responder = MockResponder()
outbound_message = await responder.create_outbound(message)
await responder.send_outbound(outbound_message)
assert len(responder.messages) == 1

async def test_create_enc_outbound(self):
context = make_context()
message = b"abc123xyz7890000"
responder = test_module.DispatcherResponder(
context, message, None, async_mock.CoroutineMock()
)
with async_mock.patch.object(
responder, "send_outbound", async_mock.CoroutineMock()
) as mock_send_outbound:
await responder.send(message)
assert mock_send_outbound.called_once()
10 changes: 2 additions & 8 deletions aries_cloudagent/messaging/agent_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def __init__(self, _id: str = None, _decorators: BaseDecoratorSet = None):
TypeError: If message type is missing on subclass Meta class

"""
super(AgentMessage, self).__init__()
super().__init__()
if _id:
self._message_id = _id
self._message_new_id = False
Expand Down Expand Up @@ -414,13 +414,7 @@ def __init__(self, *args, **kwargs):
TypeError: If Meta.model_class has not been set

"""
super(AgentMessageSchema, self).__init__(*args, **kwargs)
if not self.Meta.model_class:
raise TypeError(
"Can't instantiate abstract class {} with no model_class".format(
self.__class__.__name__
)
)
super().__init__(*args, **kwargs)
self._decorators = DecoratorSet()
self._decorators_dict = None
self._signatures = {}
Expand Down
6 changes: 4 additions & 2 deletions aries_cloudagent/messaging/decorators/signature_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def decode(self) -> (object, int):
"""
msg_bin = b64_to_bytes(self.sig_data, urlsafe=True)
(timestamp,) = struct.unpack_from("!Q", msg_bin, 0)
return json.loads(msg_bin[8:]), timestamp
return (json.loads(msg_bin[8:]), timestamp)

async def verify(self, wallet: BaseWallet) -> bool:
"""
Expand Down Expand Up @@ -133,7 +133,9 @@ class Meta:
data_key="@type",
required=True,
description="Signature type",
example="did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/signature/1.0/ed25519Sha512_single",
example=(
"did:sov:BzCbsNYhMrjHiqZDTUASHg;" "spec/signature/1.0/ed25519Sha512_single"
),
)
signature = fields.Str(
required=True,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,9 @@ def test_indy_dict(self):

assert lynx_str == lynx_list
assert lynx_str != links
assert links != DATA_LINKS # has sha256

def test_indy_dict(self):
def test_from_aries_msg(self):
deco_aries = AttachDecorator.from_aries_msg(
message=INDY_CRED, ident=IDENT, description=DESCRIPTION,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import pytest

from asynctest import TestCase as AsyncTestCase, mock as async_mock

from ....protocols.trustping.v1_0.messages.ping import Ping
from ....wallet.basic import BasicWallet
from .. import signature_decorator as test_module
from ..signature_decorator import SignatureDecorator

TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx"


class TestSignatureDecorator(AsyncTestCase):
async def test_init(self):
decorator = SignatureDecorator()
assert decorator.signature_type is None
assert decorator.signature is None
assert decorator.sig_data is None
assert decorator.signer is None
assert "SignatureDecorator" in str(decorator)

async def test_serialize_load(self):
TEST_SIG = "IkJvYiI="
TEST_SIG_DATA = "MTIzNDU2Nzg5MCJCb2Ii"

decorator = SignatureDecorator(
signature_type=SignatureDecorator.TYPE_ED25519SHA512,
signature=TEST_SIG,
sig_data=TEST_SIG_DATA,
signer=TEST_VERKEY,
)

dumped = decorator.serialize()
loaded = SignatureDecorator.deserialize(dumped)

assert loaded.signature_type == SignatureDecorator.TYPE_ED25519SHA512
assert loaded.signature == TEST_SIG
assert loaded.sig_data == TEST_SIG_DATA
assert loaded.signer == TEST_VERKEY

async def test_create_decode_verify(self):
TEST_MESSAGE = "Hello world"
TEST_TIMESTAMP = 1234567890
wallet = BasicWallet()
key_info = await wallet.create_signing_key()

deco = await SignatureDecorator.create(
Ping(), key_info.verkey, wallet, timestamp=None
)
assert deco

deco = await SignatureDecorator.create(
TEST_MESSAGE, key_info.verkey, wallet, TEST_TIMESTAMP
)

(msg, timestamp) = deco.decode()
assert msg == TEST_MESSAGE
assert timestamp == TEST_TIMESTAMP

await deco.verify(wallet)
deco.signature_type = "unsupported-sig-type"
assert not await deco.verify(wallet)
2 changes: 1 addition & 1 deletion aries_cloudagent/messaging/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ def __init__(self, *args, **kwargs):
TypeError: If model_class is not set on Meta
"""
super(BaseModelSchema, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
if not self.Meta.model_class:
raise TypeError(
"Can't instantiate abstract class {} with no model_class".format(
Expand Down
22 changes: 20 additions & 2 deletions aries_cloudagent/messaging/models/tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from ...responder import BaseResponder, MockResponder
from ...util import time_now

from ..base import BaseModel, BaseModelSchema
from ..base import BaseModel, BaseModelError, BaseModelSchema


class ModelImpl(BaseModel):
Expand All @@ -26,7 +26,7 @@ class SchemaImpl(BaseModelSchema):
class Meta:
model_class = ModelImpl

attr = fields.String()
attr = fields.String(required=True)

@validates_schema
def validate_fields(self, data, **kwargs):
Expand All @@ -44,3 +44,21 @@ def test_model_validate_succeeds(self):
model = ModelImpl(attr="succeeds")
model = model.validate()
assert model.attr == "succeeds"

def test_ser_x(self):
model = ModelImpl(attr="hello world")
with async_mock.patch.object(
model, "_get_schema_class", async_mock.MagicMock()
) as mock_get_schema_class:
mock_get_schema_class.return_value = async_mock.MagicMock(
return_value=async_mock.MagicMock(
dump=async_mock.MagicMock(side_effect=ValidationError("error"))
)
)
with self.assertRaises(BaseModelError):
model.serialize()

def test_from_json_x(self):
data = "{}{}"
with self.assertRaises(BaseModelError):
ModelImpl.from_json(data)
85 changes: 82 additions & 3 deletions aries_cloudagent/messaging/models/tests/test_base_record.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import json

from asynctest import TestCase as AsyncTestCase, mock as async_mock
from marshmallow import fields

from ....cache.base import BaseCache
from ....config.injection_context import InjectionContext
from ....storage.base import BaseStorage, StorageRecord
from ....storage.base import BaseStorage, StorageDuplicateError, StorageRecord
from ....storage.basic import BasicStorage

from ...responder import BaseResponder, MockResponder
from ...util import time_now
Expand All @@ -25,6 +27,36 @@ class Meta:
model_class = BaseRecordImpl


class ARecordImpl(BaseRecord):
class Meta:
schema_class = "ARecordImplSchema"

RECORD_TYPE = "a-record"
CACHE_ENABLED = False
RECORD_ID_NAME = "ident"
TAG_NAMES = {"code"}

def __init__(self, *, ident=None, a, b, code, **kwargs):
super().__init__(ident, **kwargs)
self.a = a
self.b = b
self.code = code

@property
def record_value(self) -> dict:
return {"a": self.a, "b": self.b}


class ARecordImplSchema(BaseRecordSchema):
class Meta:
model_class = BaseRecordImpl

ident = fields.Str(attribute="_id")
a = fields.Str()
b = fields.Str()
code = fields.Str()


class UnencTestImpl(BaseRecord):
TAG_NAMES = {"~a", "~b", "c"}

Expand All @@ -42,6 +74,10 @@ def test_from_storage_values(self):
assert inst._id == record_id
assert inst.value == stored

stored[BaseRecordImpl.RECORD_ID_NAME] = inst._id
with self.assertRaises(ValueError):
BaseRecordImpl.from_storage(record_id, stored)

async def test_post_save_new(self):
context = InjectionContext(enforce_typing=False)
mock_storage = async_mock.MagicMock()
Expand Down Expand Up @@ -74,12 +110,15 @@ async def test_post_save_exist(self):
mock_storage.update_record_tags.assert_called_once()

async def test_cache(self):
assert not await BaseRecordImpl.get_cached_key(None, None)
await BaseRecordImpl.set_cached_key(None, None, None)
await BaseRecordImpl.clear_cached_key(None, None)
context = InjectionContext(enforce_typing=False)
mock_cache = async_mock.MagicMock(BaseCache, autospec=True)
context.injector.bind_instance(BaseCache, mock_cache)
record = BaseRecordImpl()
cache_key = "cache_key"
cache_result = await record.get_cached_key(context, cache_key)
cache_result = await BaseRecordImpl.get_cached_key(context, cache_key)
mock_cache.get.assert_awaited_once_with(cache_key)
assert cache_result is mock_cache.get.return_value

Expand Down Expand Up @@ -109,6 +148,39 @@ async def test_retrieve_cached_id(self):
assert result._id == record_id
assert result.value == stored

async def test_retrieve_by_tag_filter_multi_x_delete(self):
context = InjectionContext(enforce_typing=False)
basic_storage = BasicStorage()
context.injector.bind_instance(BaseStorage, basic_storage)
records = []
for i in range(3):
records.append(ARecordImpl(a="1", b=str(i), code="one"))
await records[i].save(context)
with self.assertRaises(StorageDuplicateError):
await ARecordImpl.retrieve_by_tag_filter(
context, {"code": "one"}, {"a": "1"}
)
await records[0].delete_record(context)

async def test_save_x(self):
context = InjectionContext(enforce_typing=False)
basic_storage = BasicStorage()
context.injector.bind_instance(BaseStorage, basic_storage)
rec = ARecordImpl(a="1", b="0", code="one")
with async_mock.patch.object(
context, "inject", async_mock.CoroutineMock()
) as mock_inject:
mock_inject.return_value = async_mock.MagicMock(
add_record=async_mock.CoroutineMock(side_effect=ZeroDivisionError())
)
with self.assertRaises(ZeroDivisionError):
await rec.save(context)

async def test_neq(self):
a_rec = ARecordImpl(a="1", b="0", code="one")
b_rec = BaseRecordImpl()
assert a_rec != b_rec

async def test_retrieve_uncached_id(self):
context = InjectionContext(enforce_typing=False)
mock_storage = async_mock.MagicMock(BaseStorage, autospec=True)
Expand Down Expand Up @@ -163,7 +235,7 @@ def test_log_state(self, mock_print):
BaseRecordImpl, "LOG_STATE_FLAG", test_param
) as cls:
record = BaseRecordImpl()
record.log_state(context, "state")
record.log_state(context, msg="state", params={"a": "1", "b": "2"})
mock_print.assert_called_once()

@async_mock.patch("builtins.print")
Expand All @@ -180,6 +252,8 @@ async def test_webhook(self):
record = BaseRecordImpl()
payload = {"test": "payload"}
topic = "topic"
await record.send_webhook(context, None, None) # cover short circuit
await record.send_webhook(context, "hello", None) # cover short circuit
await record.send_webhook(context, payload, topic=topic)
assert mock_responder.webhooks == [(topic, payload)]

Expand All @@ -190,6 +264,11 @@ async def test_tag_prefix(self):
tags = {"a": "x", "b": "y", "c": "z"}
assert UnencTestImpl.prefix_tag_filter(tags) == {"~a": "x", "~b": "y", "c": "z"}

tags = {"$not": {"a": "x", "b": "y", "c": "z"}}
expect = {"$not": {"~a": "x", "~b": "y", "c": "z"}}
actual = UnencTestImpl.prefix_tag_filter(tags)
assert {**expect} == {**actual}

tags = {"$or": [{"a": "x"}, {"c": "z"}]}
assert UnencTestImpl.prefix_tag_filter(tags) == {
"$or": [{"~a": "x"}, {"c": "z"}]
Expand Down
2 changes: 1 addition & 1 deletion aries_cloudagent/messaging/request_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def default_endpoint(self) -> str:
The default agent endpoint
"""
return self.settings["default_endpoint"]
return self.settings.get("default_endpoint")

@default_endpoint.setter
def default_endpoint(self, endpoint: str):
Expand Down