예제 #1
0
class TimestampSequencedItemTestCase(RecordManagerTestCase):
    EXAMPLE_EVENT_TOPIC1 = get_topic(TimestampedEventExample1)
    EXAMPLE_EVENT_TOPIC2 = get_topic(TimestampedEventExample2)

    def construct_positions(self, num=3):
        while num:
            yield decimaltimestamp()
            num -= 1
            sleep(0.00001)

    def construct_record_manager(self):
        return self.factory.construct_timestamp_sequenced_record_manager()
 def test_hash(self):
     entity_id1 = uuid4()
     event1 = Example.Created(originator_id=entity_id1,
                              originator_topic=get_topic(Example),
                              a=1,
                              b=2,
                              timestamp=3)
     event2 = Example.Created(originator_id=entity_id1,
                              originator_topic=get_topic(Example),
                              a=1,
                              b=2,
                              timestamp=3)
     self.assertEqual(hash(event1), hash(event2))
예제 #3
0
class IntegerSequencedRecordTestCase(RecordManagerTestCase):
    """
    Abstract test case for integer sequenced record managers.
    """

    EXAMPLE_EVENT_TOPIC1 = get_topic(VersionedEventExample1)
    EXAMPLE_EVENT_TOPIC2 = get_topic(VersionedEventExample2)

    def construct_positions(self):
        return 0, 1, 2

    def construct_record_manager(self):
        return self.factory.construct_integer_sequenced_record_manager()
예제 #4
0
    def test_example_aggregate_event_classes(self):
        self.assertIn("Event", ExampleAggregateRoot.__dict__)
        self.assertIn("Created", ExampleAggregateRoot.__dict__)
        self.assertIn("Discarded", ExampleAggregateRoot.__dict__)
        self.assertIn("AttributeChanged", ExampleAggregateRoot.__dict__)

        self.assertEqual(ExampleAggregateRoot.Event.__name__, "Event")
        self.assertEqual(ExampleAggregateRoot.Event.__qualname__,
                         "ExampleAggregateRoot.Event")
        topic = "eventsourcing.tests.core_tests.test_aggregate_root#ExampleAggregateRoot.Event"
        self.assertEqual(get_topic(ExampleAggregateRoot.Event), topic)
        self.assertEqual(resolve_topic(topic), ExampleAggregateRoot.Event)

        self.assertEqual(ExampleAggregateRoot.Created.__name__, "Created")
        self.assertEqual(ExampleAggregateRoot.Created.__qualname__,
                         "ExampleAggregateRoot.Created")
        topic = "eventsourcing.tests.core_tests.test_aggregate_root#ExampleAggregateRoot.Created"
        self.assertEqual(get_topic(ExampleAggregateRoot.Created), topic)
        self.assertEqual(resolve_topic(topic), ExampleAggregateRoot.Created)
        self.assertTrue(
            issubclass(ExampleAggregateRoot.Created,
                       ExampleAggregateRoot.Event))

        self.assertEqual(ExampleAggregateRoot.Discarded.__name__, "Discarded")
        self.assertEqual(
            ExampleAggregateRoot.Discarded.__qualname__,
            "ExampleAggregateRoot.Discarded",
        )
        topic = "eventsourcing.tests.core_tests.test_aggregate_root#ExampleAggregateRoot.Discarded"
        self.assertEqual(get_topic(ExampleAggregateRoot.Discarded), topic)
        self.assertEqual(resolve_topic(topic), ExampleAggregateRoot.Discarded)
        self.assertTrue(
            issubclass(ExampleAggregateRoot.Discarded,
                       ExampleAggregateRoot.Event))

        self.assertEqual(ExampleAggregateRoot.ExampleCreated.__name__,
                         "ExampleCreated")
        self.assertEqual(
            ExampleAggregateRoot.ExampleCreated.__qualname__,
            "ExampleAggregateRoot.ExampleCreated",
        )
        topic = "eventsourcing.tests.core_tests.test_aggregate_root#ExampleAggregateRoot.ExampleCreated"
        self.assertEqual(get_topic(ExampleAggregateRoot.ExampleCreated), topic)
        self.assertEqual(resolve_topic(topic),
                         ExampleAggregateRoot.ExampleCreated)
        self.assertTrue(
            issubclass(ExampleAggregateRoot.ExampleCreated,
                       ExampleAggregateRoot.Event))
예제 #5
0
    def construct_item_args(self, domain_event):
        """
        Constructs attributes of a sequenced item from the given domain event.
        """
        # Copy the state of the event.
        event_attrs = domain_event.__dict__.copy()

        # Get the sequence ID.
        sequence_id = event_attrs.get(self.sequence_id_attr_name)

        # Get the position in the sequence.
        position = event_attrs.get(self.position_attr_name)

        # Get the topic from the event attrs, otherwise from the class.
        topic = get_topic(domain_event.__class__)

        # Serialise the remaining event attribute values.
        data = json_dumps(event_attrs, cls=self.json_encoder_class)

        # Encrypt data.
        if self.cipher:
            data = self.cipher.encrypt(data)

        # Get the 'other' args.
        # - these are meant to be derivative of the other attributes,
        #   to populate database fields, and shouldn't affect the hash.
        other_args = tuple((getattr(domain_event, name) for name in self.other_attr_names))

        return (sequence_id, position, topic, data) + other_args
예제 #6
0
    def test_with_versioned_entity_event(self):
        # Setup the mapper, and create an event.
        mapper = SequencedItemMapper(sequenced_item_class=SequencedItem,
                                     sequence_id_attr_name='originator_id',
                                     position_attr_name='originator_version')
        entity_id1 = uuid4()
        event1 = Event1(originator_id=entity_id1, originator_version=101)

        # Check to_sequenced_item() method results in a sequenced item.
        sequenced_item = mapper.item_from_event(event1)
        self.assertIsInstance(sequenced_item, SequencedItem)
        self.assertEqual(sequenced_item.position, 101)
        self.assertEqual(sequenced_item.sequence_id, entity_id1)
        self.assertEqual(sequenced_item.topic, get_topic(Event1))
        self.assertTrue(sequenced_item.state)

        # Use the returned values to create a new sequenced item.
        sequenced_item_copy = SequencedItem(
            sequence_id=sequenced_item.sequence_id,
            position=sequenced_item.position,
            topic=sequenced_item.topic,
            state=sequenced_item.state,
        )

        # Check from_sequenced_item() returns an event.
        domain_event = mapper.event_from_item(sequenced_item_copy)
        self.assertIsInstance(domain_event, Event1)
        self.assertEqual(domain_event.originator_id, event1.originator_id)
        self.assertEqual(domain_event.originator_version,
                         event1.originator_version)
    def test_get_item(self):
        # Setup an event store.
        event_store = self.construct_event_store()

        # Put an event in the event store.
        entity_id = uuid4()
        event_store.store(
            Example.Created(
                a=1,
                b=2,
                originator_id=entity_id,
                originator_topic=get_topic(Example),
            ))

        # Construct a repository.
        event_sourced_repo = EventSourcedRepository(event_store=event_store)

        # Check the entity attributes.
        example = event_sourced_repo[entity_id]
        self.assertEqual(1, example.a)
        self.assertEqual(2, example.b)
        self.assertEqual(entity_id, example.id)

        # Setup an example repository, using the subclass ExampleRepository.
        example_repo = ExampleRepository(event_store=event_store)

        # Check the repo has the example.
        self.assertIn(entity_id, example_repo)
        self.assertNotIn(uuid4(), example_repo)

        # Check the entity attributes.
        example = example_repo[entity_id]
        self.assertEqual(1, example.a)
        self.assertEqual(2, example.b)
        self.assertEqual(entity_id, example.id)
예제 #8
0
    def default(self, obj):
        if isinstance(obj, UUID):
            return {'UUID': obj.hex}
        elif isinstance(obj, datetime.datetime):
            return {'ISO8601_datetime': obj.strftime('%Y-%m-%dT%H:%M:%S.%f%z')}
        elif isinstance(obj, datetime.date):
            return {'ISO8601_date': obj.isoformat()}
        elif isinstance(obj, datetime.time):
            return {'ISO8601_time': obj.strftime('%H:%M:%S.%f')}
        elif isinstance(obj, Decimal):
            return {
                '__decimal__': str(obj),
            }
        elif hasattr(obj, '__class__') and hasattr(obj, '__dict__'):
            topic = get_topic(obj.__class__)
            state = obj.__dict__.copy()
            return {
                '__class__': {
                    'topic': topic,
                    'state': state,
                }
            }
        elif isinstance(obj, deque):
            assert list(obj) == []
            return {'__deque__': []}

        # Let the base class default method raise the TypeError.
        return JSONEncoder.default(self, obj)
    def test_with_timestamped_entity_event(self):
        # Setup the mapper, and create an event.
        mapper = SequencedItemMapper(
            sequenced_item_class=SequencedItem,
            sequence_id_attr_name="originator_id",
            position_attr_name="timestamp",
        )
        before = decimaltimestamp()
        sleep(0.000001)  # Avoid test failing due to timestamp having limited precision.
        event2 = Event2(originator_id="entity2")
        sleep(0.000001)  # Avoid test failing due to timestamp having limited precision.
        after = decimaltimestamp()

        # Check to_sequenced_item() method results in a sequenced item.
        sequenced_item = mapper.item_from_event(event2)
        self.assertIsInstance(sequenced_item, SequencedItem)
        self.assertGreater(sequenced_item.position, before)
        self.assertLess(sequenced_item.position, after)
        self.assertEqual(sequenced_item.sequence_id, "entity2")
        self.assertEqual(sequenced_item.topic, get_topic(Event2))
        self.assertTrue(sequenced_item.state)

        # Use the returned values to create a new sequenced item.
        sequenced_item_copy = SequencedItem(
            sequence_id=sequenced_item.sequence_id,
            position=sequenced_item.position,
            topic=sequenced_item.topic,
            state=sequenced_item.state,
        )

        # Check from_sequenced_item() returns an event.
        domain_event = mapper.event_from_item(sequenced_item_copy)
        self.assertIsInstance(domain_event, Event2)
        self.assertEqual(domain_event.originator_id, event2.originator_id)
        self.assertEqual(domain_event.timestamp, event2.timestamp)
예제 #10
0
 def append_notification(self, item):
     sequenced_item = self.entity_record_manager.sequenced_item_class(
         uuid4(),
         0,
         get_topic(DomainEvent),
         item
     )
     self.entity_record_manager.append(sequenced_item)
예제 #11
0
 def __create__(cls, originator_id=None, event_class=None, **kwargs):
     if originator_id is None:
         originator_id = uuid4()
     event = (event_class or cls.Created)(originator_id=originator_id,
                                          originator_topic=get_topic(cls),
                                          **kwargs)
     obj = event.__mutate__()
     obj.__publish__(event)
     return obj
예제 #12
0
    def __init__(self, **kwargs):
        super(EventWithHash, self).__init__(**kwargs)

        # Set __event_topic__ to differentiate events of
        # different types with otherwise equal attributes.
        self.__dict__['__event_topic__'] = get_topic(type(self))

        # Set __event_hash__ with a SHA-256 hash of the event.
        self.__dict__['__event_hash__'] = self.__hash_object__(self.__dict__)
예제 #13
0
    def __hash__(self):
        """
        Computes a Python integer hash for an event,
        using its event hash string.

        Supports equality and inequality comparisons.
        """
        state = self.__dict__.copy()
        state['__event_topic__'] = get_topic(type(self))
        return hash(self.__hash_object__(state))
예제 #14
0
 def iterencode(self, o, _one_shot=False):
     if isinstance(o, tuple):
         o = {
             "__tuple__": {
                 "topic": (get_topic(o.__class__)),
                 "state": (list(o))
             }
         }
     return super(ObjectJSONEncoder, self).iterencode(o,
                                                      _one_shot=_one_shot)
예제 #15
0
 def encode_deque(self, o):
     if type(o) == deque:
         return {"__deque__": self.encode_object(list(o))}
     else:
         return {
             "__deque__": {
                 "state": self.encode_object(list(o)),
                 "topic": get_topic(o.__class__),
             }
         }
예제 #16
0
 def encode_list(self, o):
     if type(o) == list:
         return [self.encode_object(i) for i in o]
     else:
         return {
             "__list__": {
                 "state": [self.encode_object(i) for i in o],
                 "topic": get_topic(o.__class__),
             }
         }
예제 #17
0
 def encode_frozenset(self, o):
     if type(o) == frozenset:
         return {"__frozenset__": self.encode_iterable(o)}
     else:
         return {
             "__frozenset__": {
                 "state": self.encode_iterable(o),
                 "topic": get_topic(o.__class__),
             }
         }
예제 #18
0
 def encode_dict(self, o):
     if type(o) == dict:
         return self.encode_dict_state(o)
     else:
         return {
             "__dict__": {
                 "topic": get_topic(o.__class__),
                 "state": self.encode_dict_state(o),
             }
         }
예제 #19
0
    def __init__(self, **kwargs: Any):
        super(EventWithHash, self).__init__(**kwargs)

        # Set __event_topic__ to differentiate events of
        # different types with otherwise equal attributes.
        self.__dict__["__event_topic__"] = get_topic(type(self))

        # Set __event_hash__ with a SHA-256 hash of the event.
        hash_method = self.__hash_object_v2__
        self.__dict__["__event_hash_method_name__"] = hash_method.__name__
        self.__dict__["__event_hash__"] = hash_method(self.__dict__)
예제 #20
0
    def test_event_hash_error(self):
        event1 = AggregateRoot.Created(
            originator_version=0,
            originator_id='1',
            originator_topic=get_topic(AggregateRoot))
        event1.__check_hash__()

        # Break the hash.
        event1.__dict__['event_hash'] = 'damage'
        with self.assertRaises(EventHashError):
            event1.__check_hash__()
예제 #21
0
    def test_aggregate1_event_classes(self):
        self.assertIn("Event", Aggregate1.__dict__)
        self.assertIn("Created", Aggregate1.__dict__)
        self.assertIn("Discarded", Aggregate1.__dict__)
        self.assertIn("AttributeChanged", Aggregate1.__dict__)

        self.assertEqual(Aggregate1.Event.__name__, "Event")
        self.assertEqual(Aggregate1.Event.__qualname__, "Aggregate1.Event")
        topic = "eventsourcing.tests.core_tests.test_aggregate_root#Aggregate1.Event"
        self.assertEqual(get_topic(Aggregate1.Event), topic)
        self.assertEqual(resolve_topic(topic), Aggregate1.Event)

        self.assertEqual(Aggregate1.Created.__name__, "Created")
        self.assertEqual(Aggregate1.Created.__qualname__, "Aggregate1.Created")
        topic = "eventsourcing.tests.core_tests.test_aggregate_root#Aggregate1.Created"
        self.assertEqual(get_topic(Aggregate1.Created), topic)
        self.assertEqual(resolve_topic(topic), Aggregate1.Created)
        self.assertTrue(issubclass(Aggregate1.Created, Aggregate1.Event))

        self.assertEqual(Aggregate1.Discarded.__name__, "Discarded")
        self.assertEqual(Aggregate1.Discarded.__qualname__,
                         "Aggregate1.Discarded")
        topic = (
            "eventsourcing.tests.core_tests.test_aggregate_root#Aggregate1"
            ".Discarded")
        self.assertEqual(get_topic(Aggregate1.Discarded), topic)
        self.assertEqual(resolve_topic(topic), Aggregate1.Discarded)
        self.assertTrue(issubclass(Aggregate1.Discarded, Aggregate1.Event))

        self.assertEqual(Aggregate1.ExampleCreated.__name__, "ExampleCreated")
        self.assertEqual(Aggregate1.ExampleCreated.__qualname__,
                         "Aggregate1.ExampleCreated")
        topic = (
            "eventsourcing.tests.core_tests.test_aggregate_root#Aggregate1"
            ".ExampleCreated")
        self.assertEqual(get_topic(Aggregate1.ExampleCreated), topic)
        self.assertEqual(resolve_topic(topic), Aggregate1.ExampleCreated)
        self.assertTrue(issubclass(Aggregate1.ExampleCreated,
                                   Aggregate1.Event))
        self.assertTrue(
            issubclass(Aggregate1.SomethingElseOccurred, Aggregate1.Event))
예제 #22
0
 def __create__(cls, originator_id=None, event_class=None, **kwargs):
     if originator_id is None:
         originator_id = uuid4()
     if getattr(cls, '__with_data_integrity__', True):
         genesis_hash = getattr(cls, '__genesis_hash__', GENESIS_HASH)
         kwargs['__previous_hash__'] = genesis_hash
     event = (event_class or cls.Created)(originator_id=originator_id,
                                          originator_topic=get_topic(cls),
                                          **kwargs)
     obj = event.__mutate__()
     obj.__publish__(event)
     return obj
예제 #23
0
    def __init__(self, **kwargs):
        """
        Initialises event attribute values directly from constructor kwargs.
        """
        if self.__with_data_integrity__:
            kwargs['__event_topic__'] = get_topic(type(self))
            # Seal the event with a hash of the other values.
            assert '__event_hash__' not in kwargs
            event_hash = self.__hash_for_data_integrity__(kwargs)
            kwargs['__event_hash__'] = event_hash

        self.__dict__.update(kwargs)
 def test_equality_comparison(self):
     entity_id1 = uuid4()
     event1 = Example.Created(originator_id=entity_id1,
                              originator_topic=get_topic(Example),
                              a=1,
                              b=2,
                              timestamp=3)
     event2 = Example.Created(originator_id=entity_id1,
                              originator_topic=get_topic(Example),
                              a=1,
                              b=2,
                              timestamp=3)
     event3 = Example.Created(originator_id=entity_id1,
                              originator_topic=get_topic(Example),
                              a=3,
                              b=2,
                              timestamp=3)
     self.assertEqual(event1, event2)
     self.assertNotEqual(event1, event3)
     self.assertNotEqual(event2, event3)
     self.assertNotEqual(event2, None)
예제 #25
0
    def __init__(self, **kwargs):
        """
        Initialises event attribute values directly from constructor kwargs.
        """
        super(EventWithHash, self).__init__(**kwargs)

        # Seal the event with a hash of the other values.
        # __event_topic__ is needed to obtain a different hash
        # for different types of events with otherwise equal
        # attributes.
        self.__dict__['__event_topic__'] = get_topic(type(self))
        self.__dict__['__event_hash__'] = self.__hash_object__(self.__dict__)
예제 #26
0
    def get_item_topic_and_state(self, domain_event_class, event_attrs):
        # Get the topic from the event attrs, otherwise from the class.
        topic = get_topic(domain_event_class)

        # Serialise the event attributes.
        state = json_dumps(event_attrs, cls=self.json_encoder_class)

        # Encrypt serialised state.
        if self.cipher:
            state = self.cipher.encrypt(state)

        return topic, state
예제 #27
0
    def test_all_domain_events(self):
        event_store = self.construct_event_store()

        # Check there are zero domain events in total.
        domain_events = event_store.all_domain_events()
        domain_events = list(domain_events)
        self.assertEqual(len(domain_events), 0)

        # Store a domain event.
        entity_id1 = uuid4()
        event1 = Example.Created(a=1,
                                 b=2,
                                 originator_id=entity_id1,
                                 originator_topic=get_topic(Example))
        event_store.store(event1)

        # Store another domain event for the same entity.
        event1 = Example.AttributeChanged(
            a=1,
            b=2,
            originator_id=entity_id1,
            originator_version=1,
            __previous_hash__=event1.__event_hash__,
        )
        event_store.store(event1)

        # Store a domain event for a different entity.
        entity_id2 = uuid4()
        event1 = Example.Created(
            originator_topic=get_topic(Example),
            originator_id=entity_id2,
            a=1,
            b=2,
        )
        event_store.store(event1)

        # Check there are three domain events in total.
        domain_events = event_store.all_domain_events()
        domain_events = list(domain_events)
        self.assertEqual(len(domain_events), 3)
예제 #28
0
    def test_get_domain_events(self):
        event_store = self.construct_event_store()

        # Check there are zero stored events in the repo.
        entity_id1 = uuid4()
        entity_events = event_store.get_domain_events(originator_id=entity_id1)
        entity_events = list(entity_events)
        self.assertEqual(0, len(entity_events))

        # Check there are zero events in the event store, using iterator.
        entity_events = event_store.get_domain_events(originator_id=entity_id1,
                                                      page_size=1)
        entity_events = list(entity_events)
        self.assertEqual(0, len(entity_events))

        # Store a domain event.
        event1 = Example.Created(a=1,
                                 b=2,
                                 originator_id=entity_id1,
                                 originator_topic=get_topic(Example))
        event_store.store(event1)

        # Check there is one event in the event store.
        entity_events = event_store.get_domain_events(originator_id=entity_id1)
        entity_events = list(entity_events)
        self.assertEqual(1, len(entity_events))

        # Check there are two events in the event store, using iterator.
        entity_events = event_store.get_domain_events(originator_id=entity_id1,
                                                      page_size=1)
        entity_events = list(entity_events)
        self.assertEqual(1, len(entity_events))

        # Store another domain event.
        event1 = Example.AttributeChanged(
            a=1,
            b=2,
            originator_id=entity_id1,
            originator_version=1,
        )
        event_store.store(event1)

        # Check there are two events in the event store.
        entity_events = event_store.get_domain_events(originator_id=entity_id1)
        entity_events = list(entity_events)
        self.assertEqual(2, len(entity_events))

        # Check there are two events in the event store, using iterator.
        entity_events = event_store.get_domain_events(originator_id=entity_id1,
                                                      page_size=1)
        entity_events = list(entity_events)
        self.assertEqual(2, len(entity_events))
    def test_get_item(self) -> None:
        # Setup an event store.
        event_store = self.construct_event_store()

        # Put an event in the event store.
        entity_id = uuid4()
        event_store.store_events([
            Example.Created(
                a=1,
                b=2,
                originator_id=entity_id,
                originator_topic=get_topic(Example),
            )
        ])

        # Construct a repository.
        event_sourced_repo: EventSourcedRepository[
            Example,
            Example.Event] = EventSourcedRepository(event_store=event_store)

        # Check the entity attributes.
        example = event_sourced_repo[entity_id]
        self.assertEqual(1, example.a)
        self.assertEqual(2, example.b)
        self.assertEqual(entity_id, example.id)

        # Check class-specific request.
        example = event_sourced_repo.get_instance_of(Example, entity_id)
        self.assertEqual(1, example.a)
        self.assertEqual(2, example.b)
        self.assertEqual(entity_id, example.id)

        # Check class-specific request fails on type.
        class SubExample(Example):
            pass

        self.assertIsNone(
            event_sourced_repo.get_instance_of(SubExample, entity_id))

        # Setup an example repository, using the subclass ExampleRepository.
        example_repo = ExampleRepository(event_store=event_store)

        # Check the repo has the example.
        self.assertIn(entity_id, example_repo)
        self.assertNotIn(uuid4(), example_repo)

        # Check the entity attributes.
        example = example_repo[entity_id]
        self.assertEqual(1, example.a)
        self.assertEqual(2, example.b)
        self.assertEqual(entity_id, example.id)
예제 #30
0
    def test_subscribe_to_decorator(self):
        entity_id1 = uuid4()
        event1 = Example.Created(
            originator_id=entity_id1,
            originator_topic=get_topic(Example),
            a=1, b=2
        )
        event2 = Example.Discarded(
            originator_id=entity_id1,
            originator_version=1,
        )
        handler = mock.Mock()

        # Check we can assert there are no event handlers subscribed.
        assert_event_handlers_empty()

        @subscribe_to(Example.Created)
        def test_handler(e):
            """Doc string"""
            handler(e)

        # Check the decorator doesn't mess with the function doc string.
        self.assertEqual('Doc string', test_handler.__doc__)

        # Check can fail to assert event handlers empty.
        self.assertRaises(EventHandlersNotEmptyError, assert_event_handlers_empty)

        # Check event is received when published individually.
        publish(event1)
        handler.assert_called_once_with(event1)

        # Check event of wrong type is not received.
        handler.reset_mock()
        publish(event2)
        self.assertFalse(handler.call_count)

        # Check a list of events can be filtered.
        handler.reset_mock()
        publish([event1, event2])
        handler.assert_called_once_with(event1)

        handler.reset_mock()
        publish([event1, event1])
        self.assertEqual(2, handler.call_count)

        handler.reset_mock()
        publish([event2, event2])
        self.assertEqual(0, handler.call_count)

        _event_handlers.clear()