Skip to content

Commit

Permalink
Merge pull request #633 from sklump/unit-tests-messaging
Browse files Browse the repository at this point in the history
Unit tests messaging
  • Loading branch information
andrewwhitehead committed Jul 29, 2020
2 parents 864bacc + c8976b1 commit 7b7aa1c
Show file tree
Hide file tree
Showing 12 changed files with 342 additions and 27 deletions.
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

0 comments on commit 7b7aa1c

Please sign in to comment.