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(originator_id=entity_id1, a=1, b=2) event_store.append(event1) # Store another domain event for the same entity. event1 = Example.AttributeChanged(originator_id=entity_id1, a=1, b=2, originator_version=1) event_store.append(event1) # Store a domain event for a different entity. entity_id2 = uuid4() event1 = Example.Created(originator_id=entity_id2, a=1, b=2) event_store.append(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)
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)
def test_event_is_hashable(self): event1 = Example.Event(originator_id=1, originator_version=0, timestamp=1) event2 = Example.Event(originator_id=1, originator_version=0, timestamp=1) event3 = Example.Event(originator_id=1, originator_version=0, timestamp=2) # Same type with same values. self.assertEqual(event1, event2) self.assertEqual(hash(event1), hash(event2)) # Same thing # Same type with different values. self.assertNotEqual(event1, event3) self.assertNotEqual(hash(event1), hash(event3)) # Same thing # Different type with same values. class Subclass(Example.Event): pass event4 = Subclass(originator_id=1, originator_version=0, timestamp=1) self.assertNotEqual(hash(event1), hash(event4)) # Same thing self.assertNotEqual(event1, event4) self.assertNotEqual(hash(event1), hash(event4)) # Same thing
def test_event_attributes(self): entity_id1 = uuid4() event = Example.Created(originator_id=entity_id1, originator_topic=get_topic(Example), a=1, b=2) # Check constructor keyword args lead to read-only attributes. self.assertEqual(1, event.a) self.assertEqual(2, event.b) self.assertRaises(AttributeError, getattr, event, "c") self.assertRaises(AttributeError, setattr, event, "c", 3) # Check domain event has auto-generated timestamp. self.assertIsInstance(event.timestamp, Decimal) # Check timestamp value can be given to domain events. event1 = Example.Created( originator_id=entity_id1, originator_topic=get_topic(Example), a=1, b=2, timestamp=3, ) self.assertEqual(3, event1.timestamp)
def test_equality_comparison(self): entity_id1 = uuid4() event1 = Example.Created(entity_id=entity_id1, a=1, b=2, timestamp=3) event2 = Example.Created(entity_id=entity_id1, a=1, b=2, timestamp=3) event3 = Example.Created(entity_id=entity_id1, a=3, b=2, timestamp=3) self.assertEqual(event1, event2) self.assertNotEqual(event1, event3) self.assertNotEqual(event2, event3) self.assertNotEqual(event2, None)
def test_replay_entity(self): # Store example events. # Create entity1. entity_id1 = uuid4() event1 = Example.Created(originator_id=entity_id1, a=1, b=2) self.entity_event_store.append(event1) # Create entity2. entity_id2 = uuid4() event2 = Example.Created(originator_id=entity_id2, a=2, b=4) self.entity_event_store.append(event2) # Create entity3. entity_id3 = uuid4() event3 = Example.Created(originator_id=entity_id3, a=3, b=6) self.entity_event_store.append(event3) # Discard entity3. event4 = Example.Discarded(originator_id=entity_id3, originator_version=1) self.entity_event_store.append(event4) # Check the entities can be replayed. event_player = EventPlayer(event_store=self.entity_event_store, mutator=Example._mutate) # Check recovered entities have correct attribute values. recovered1 = event_player.replay_entity(entity_id1) self.assertEqual(entity_id1, recovered1.id) self.assertEqual(1, recovered1.a) recovered2 = event_player.replay_entity(entity_id2) self.assertEqual(2, recovered2.a) recovered3 = event_player.replay_entity(entity_id3) self.assertEqual(None, recovered3) # Check it works for "short" entities (should be faster, but the main thing is that it still works). # - just use a trivial mutate that always instantiates the 'Example'. event5 = Example.AttributeChanged(originator_id=entity_id1, originator_version=1, name='a', value=10) self.entity_event_store.append(event5) recovered1 = event_player.replay_entity(entity_id1) self.assertEqual(10, recovered1.a) event_player = EventPlayer( event_store=self.entity_event_store, mutator=Example._mutate, is_short=True, ) self.assertEqual(10, event_player.replay_entity(entity_id1).a)
def test_hash(self): entity_id1 = uuid4() event1 = Example.Created(originator_id=entity_id1, a=1, b=2, timestamp=3) event2 = Example.Created(originator_id=entity_id1, a=1, b=2, timestamp=3) self.assertEqual(hash(event1), hash(event2))
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_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()
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)
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.append(Example.Created(entity_id=entity_id, a=1, b=2)) # Check ValueError is raised if repo doesn't have a mutator function... with self.assertRaises(ValueError): EventSourcedRepository(event_store=event_store, mutator=None) # ...and isn't if we pass a mutator function as a constructor arg. event_sourced_repo = EventSourcedRepository(event_store=event_store, mutator=Example.mutate) # 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)
def test_event_attributes(self): entity_id1 = uuid4() event = Example.Created(entity_id=entity_id1, a=1, b=2) # Check constructor keyword args lead to read-only attributes. self.assertEqual(1, event.a) self.assertEqual(2, event.b) self.assertRaises(AttributeError, getattr, event, 'c') self.assertRaises(AttributeError, setattr, event, 'c', 3) # Check domain event has auto-generated timestamp. self.assertIsInstance(event.timestamp, float) # Check timestamp value can be given to domain events. event1 = Example.Created(entity_id=entity_id1, a=1, b=2, timestamp=3) self.assertEqual(3, event1.timestamp)
def test_repr(self): entity_id1 = uuid4() event1 = Example.Created(originator_id=entity_id1, a=1, b=2, timestamp=3) self.assertEqual( "Example.Created(a=1, b=2, originator_id={}, originator_version=0, timestamp=3)" .format(repr(entity_id1)), repr(event1))
def test_get_entity(self): # Store example events. entity_id1 = uuid4() event1 = Example.Created(entity_id=entity_id1, a=1, b=2) self.version_entity_event_store.append(event1) entity_id2 = uuid4() event2 = Example.Created(entity_id=entity_id2, a=2, b=4) self.version_entity_event_store.append(event2) entity_id3 = uuid4() event3 = Example.Created(entity_id=entity_id3, a=3, b=6) self.version_entity_event_store.append(event3) event4 = Example.Discarded(entity_id=entity_id3, entity_version=1) self.version_entity_event_store.append(event4) # Check the event sourced entities are correct. # - just use a trivial mutate that always instantiates the 'Example'. event_player = EventPlayer(event_store=self.version_entity_event_store, mutator=Example.mutate) # The the reconstituted entity has correct attribute values. self.assertEqual(entity_id1, event_player.replay_entity(entity_id1).id) self.assertEqual(1, event_player.replay_entity(entity_id1).a) self.assertEqual(2, event_player.replay_entity(entity_id2).a) self.assertEqual(None, event_player.replay_entity(entity_id3)) # Check entity3 raises KeyError. self.assertEqual(event_player.replay_entity(entity_id3), None) # Check it works for "short" entities (should be faster, but the main thing is that it still works). # - just use a trivial mutate that always instantiates the 'Example'. event5 = Example.AttributeChanged(entity_id=entity_id1, entity_version=1, name='a', value=10) self.version_entity_event_store.append(event5) event_player = EventPlayer(event_store=self.version_entity_event_store, mutator=Example.mutate) self.assertEqual(10, event_player.replay_entity(entity_id1).a) event_player = EventPlayer( event_store=self.version_entity_event_store, mutator=Example.mutate, is_short=True, ) self.assertEqual(10, event_player.replay_entity(entity_id1).a)
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)
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_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_events([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_events([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_events([event1]) # Check there are three domain events in total. domain_events = event_store.all_events() domain_events = list(domain_events) self.assertEqual(len(domain_events), 3)
def test_repr(self): entity_id1 = uuid4() event1 = Example.Created(originator_id=entity_id1, originator_topic=get_topic(Example), a=1, b=2, timestamp=3) self.maxDiff = None self.assertEqual(( "Example.Created(__event_hash__='{}', " "__event_topic__='eventsourcing.example.domainmodel#Example.Created', a=1, b=2, " "originator_id={}, " "originator_topic='eventsourcing.example.domainmodel#Example', originator_version=0, timestamp=3)" ).format(event1.__event_hash__, repr(entity_id1)), repr(event1))
def test_get_most_recent_event(self): event_store = self.construct_event_store() # Check there is no most recent event. entity_id1 = uuid4() entity_event = event_store.get_most_recent_event(entity_id=entity_id1) self.assertEqual(entity_event, None) # Store a domain event. event1 = Example.Created(entity_id=entity_id1, a=1, b=2) event_store.append(event1) # Check there is an event. entity_event = event_store.get_most_recent_event(entity_id=entity_id1) self.assertEqual(entity_event, event1)
def test_get_domain_events_deprecated(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) self.assertEqual(0, len(list(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_events([event1]) # Check there is one event in the event store. entity_events = event_store.get_domain_events(originator_id=entity_id1) self.assertEqual(1, len(list(entity_events)))
def test_get_most_recent_event(self): event_store = self.construct_event_store() # Check there is no most recent event. entity_id1 = uuid4() entity_event = event_store.get_most_recent_event(originator_id=entity_id1) self.assertEqual(entity_event, None) # Store a domain event. event1 = Example.Created( a=1, b=2, originator_id=entity_id1, originator_topic=get_topic(Example) ) event_store.store_events([event1]) # Check there is an event. entity_event = event_store.get_most_recent_event(originator_id=entity_id1) self.assertEqual(entity_event, event1)
def test_subscribe_to_decorator(self): entity_id1 = uuid4() event = Example.Created(originator_id=entity_id1, a=1, b=2) 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): handler(e) # Check we can assert there are event handlers subscribed. self.assertRaises(EventHandlersNotEmptyError, assert_event_handlers_empty) # Check what happens when an event is published. publish(event) handler.assert_called_once_with(event)
def test_repr(self): entity_id1 = UUID('c7383095-85a3-4c49-ae32-9c9957ac8579') event1 = Example.Created( originator_id=entity_id1, originator_topic=get_topic(Example), a=1, b=2, timestamp=3, ) self.maxDiff = None self.assertEqual( ("Example.Created(" "__event_hash__='dbd0d45ed797de4f9a5d574ec4e3da3df74e67641bd7632dd05ffa28a5876391', " "__event_hash_method_name__='__hash_object_v2__', " "__event_topic__='eventsourcing.example.domainmodel#Example.Created', a=1, b=2, " "originator_id={}, " "originator_topic='eventsourcing.example.domainmodel#Example', originator_version=0, timestamp=3)" ).format(repr(entity_id1)), repr(event1), )
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) handler1 = mock.Mock() handler2 = mock.Mock() handler3 = mock.Mock() # Check we can assert there are no event handlers subscribed. assert_event_handlers_empty() # Original style (one event class arg). @subscribe_to(Example.Created) def test_handler1(e): """Doc string""" handler1(e) # Naked style (not called). @subscribe_to def test_handler2(e): """Doc string""" handler2(e) # Multi-event style (many event class args). @subscribe_to((Example.Created, Example.Discarded)) def test_handler3(e): """Doc string""" handler3(e) # Check the decorator doesn't mess with the function doc string. self.assertEqual("Doc string", test_handler1.__doc__) self.assertEqual("Doc string", test_handler2.__doc__) self.assertEqual("Doc string", test_handler3.__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]) handler1.assert_called_once_with(event1) handler2.assert_called_once_with(event1) handler3.assert_called_once_with(event1) # Check event of wrong type is not received. handler1.reset_mock() handler2.reset_mock() handler3.reset_mock() publish([event2]) self.assertFalse(handler1.call_count) handler2.assert_called_once_with(event2) handler3.assert_called_once_with(event2) # Check a list of events can be filtered. handler1.reset_mock() handler2.reset_mock() handler3.reset_mock() publish([event1, event2]) handler1.assert_called_once_with(event1) self.assertEqual(handler2.call_count, 2) self.assertEqual(handler3.call_count, 2) handler1.reset_mock() handler2.reset_mock() handler3.reset_mock() publish([event1, event1]) self.assertEqual(2, handler1.call_count) self.assertEqual(2, handler2.call_count) self.assertEqual(2, handler3.call_count) handler1.reset_mock() handler2.reset_mock() handler3.reset_mock() publish([event2, event2]) self.assertEqual(0, handler1.call_count) self.assertEqual(2, handler2.call_count) self.assertEqual(2, handler3.call_count) clear_event_handlers()
def test_entity_lifecycle(self): # Check the factory creates an instance. example1 = Example.__create__(a=1, b=2) self.assertIsInstance(example1, Example) # Check the instance is equal to itself. self.assertEqual(example1, example1) # Check the instance is equal to a clone of itself. clone = object.__new__(type(example1)) clone.__dict__.update(example1.__dict__) self.assertEqual(example1, clone) # Check the properties of the Example class. self.assertEqual(1, example1.a) self.assertEqual(2, example1.b) # Check the properties of the TimestampedVersionedEntity class. self.assertTrue(example1.id) self.assertEqual(example1.__version__, 0) self.assertTrue(example1.__created_on__) self.assertLess(example1.__created_on__, time.time()) self.assertGreater(example1.__created_on__, time.time() - 1) self.assertTrue(example1.__last_modified__) self.assertEqual(example1.__created_on__, example1.__last_modified__) # Test the datetime_from_timestamp() converts ok. tt = time.time() dt = datetime.datetime.utcnow() self.assertEqual(datetime_from_timestamp(tt).hour, dt.hour) # Check can get datetime from timestamps, and it corresponds to UTC. dt = datetime_from_timestamp(example1.__created_on__) self.assertLess(dt, datetime.datetime.utcnow()) self.assertGreater(dt, datetime.datetime.utcnow() - datetime.timedelta(1)) # Check a different type with the same values is not "equal" to the first. class Subclass(Example): pass other = object.__new__(Subclass) other.__dict__.update(example1.__dict__) self.assertEqual(example1.__dict__, other.__dict__) self.assertNotEqual(example1, other) # Check a second instance with the same values is not "equal" to the first. example2 = create_new_example(a=1, b=2) self.assertEqual(type(example1), type(example2)) self.assertNotEqual(example1, example2) # Check entity not hashable. with self.assertRaises(TypeError): hash(example1) # Setup the repo. repo = ExampleRepository(self.entity_event_store) # Check the example entities can be retrieved from the example repository. entity1 = repo[example1.id] self.assertIsInstance(entity1, Example) self.assertEqual(1, entity1.a) self.assertEqual(2, entity1.b) entity2 = repo[example2.id] self.assertIsInstance(entity2, Example) self.assertEqual(1, entity2.a) self.assertEqual(2, entity2.b) # Check the entity can be updated. entity1.a = 100 self.assertEqual(100, repo[entity1.id].a) entity1.b = -200 self.assertEqual(-200, repo[entity1.id].b) self.assertEqual(repo[entity1.id].__created_on__, entity1.__created_on__) self.assertEqual(repo[entity1.id].__last_modified__, entity1.__last_modified__) self.assertNotEqual(entity1.__last_modified__, entity1.__created_on__) self.assertEqual(0, entity1.count_heartbeats()) entity1.beat_heart() entity1.beat_heart() entity1.beat_heart() self.assertEqual(3, entity1.count_heartbeats()) self.assertEqual(3, repo[entity1.id].count_heartbeats()) # Check the entity can publish multiple events simultaneously. entity1.beat_heart(number_of_beats=3) self.assertEqual(6, repo[entity1.id].count_heartbeats()) # Check the entity can be discarded. entity1.__discard__() # Check the repo now raises a KeyError. self.assertRaises(RepositoryKeyError, repo.__getitem__, entity1.id) # Check the entity can't be discarded twice. self.assertRaises(AssertionError, entity1.__discard__) # Should fail to validate event with wrong entity ID. with self.assertRaises(OriginatorIDError): VersionedEntity.Event( originator_id=uuid4(), originator_version=0, ).__check_obj__(entity2) # Should fail to validate event with wrong entity version. with self.assertRaises(OriginatorVersionError): VersionedEntity.Event( originator_id=entity2.id, originator_version=0, __previous_hash__=entity2.__head__, ).__check_obj__(entity2) # Should validate event with correct entity ID and version. VersionedEntity.Event( originator_id=entity2.id, originator_version=entity2.__version__ + 1, __previous_hash__=entity2.__head__, ).__check_obj__(entity2) # Check an entity cannot be reregistered with the ID of a discarded entity. replacement_event = Example.Created( originator_id=entity1.id, a=11, b=12, originator_topic=get_topic(Example), ) with self.assertRaises(ConcurrencyError): publish(event=replacement_event)
def test_entity_lifecycle(self): # Check the factory creates an instance. example1 = create_new_example(a=1, b=2) self.assertIsInstance(example1, Example) # Check the instance is equal to itself. self.assertEqual(example1, example1) # Check the instance is equal to a clone of itself. clone = object.__new__(type(example1)) clone.__dict__.update(example1.__dict__) self.assertEqual(example1, clone) # Check the properties of the Example class. self.assertEqual(1, example1.a) self.assertEqual(2, example1.b) # Check the properties of the TimestampedVersionedEntity class. self.assertTrue(example1.id) self.assertEqual(1, example1.version) self.assertTrue(example1.created_on) self.assertTrue(example1.last_modified) self.assertEqual(example1.created_on, example1.last_modified) # Check a different type with the same values is not "equal" to the first. class Subclass(Example): pass other = object.__new__(Subclass) other.__dict__.update(example1.__dict__) self.assertEqual(example1.__dict__, other.__dict__) self.assertNotEqual(example1, other) # Check a second instance with the same values is not "equal" to the first. example2 = create_new_example(a=1, b=2) self.assertEqual(type(example1), type(example2)) self.assertNotEqual(example1, example2) # Setup the repo. repo = ExampleRepository(self.entity_event_store) # Check the example entities can be retrieved from the example repository. entity1 = repo[example1.id] self.assertIsInstance(entity1, Example) self.assertEqual(1, entity1.a) self.assertEqual(2, entity1.b) entity2 = repo[example2.id] self.assertIsInstance(entity2, Example) self.assertEqual(1, entity2.a) self.assertEqual(2, entity2.b) # Check the entity can be updated. entity1.a = 100 self.assertEqual(100, repo[entity1.id].a) entity1.b = -200 self.assertEqual(-200, repo[entity1.id].b) self.assertEqual(repo[entity1.id].created_on, entity1.created_on) self.assertEqual(repo[entity1.id].last_modified, entity1.last_modified) self.assertNotEqual(entity1.last_modified, entity1.created_on) self.assertEqual(0, entity1.count_heartbeats()) entity1.beat_heart() entity1.beat_heart() entity1.beat_heart() self.assertEqual(3, entity1.count_heartbeats()) self.assertEqual(3, repo[entity1.id].count_heartbeats()) # Check the entity can publish multiple events simultaneously. entity1.beat_heart(number_of_beats=3) self.assertEqual(6, repo[entity1.id].count_heartbeats()) # Check the entity can be discarded. entity1.discard() # Check the repo now raises a KeyError. self.assertRaises(RepositoryKeyError, repo.__getitem__, entity1.id) # Check the entity can't be discarded twice. self.assertRaises(AssertionError, entity1.discard) # Should fail to validate event with wrong entity ID. with self.assertRaises(MismatchedOriginatorIDError): entity2._validate_originator( VersionedEntity.Event(originator_id=uuid4(), originator_version=0)) # Should fail to validate event with wrong entity version. with self.assertRaises(MismatchedOriginatorVersionError): entity2._validate_originator( VersionedEntity.Event( originator_id=entity2.id, originator_version=0, )) # Should validate event with correct entity ID and version. entity2._validate_originator( VersionedEntity.Event( originator_id=entity2.id, originator_version=entity2.version, )) # Check an entity cannot be reregistered with the ID of a discarded entity. replacement_event = Example.Created(originator_id=entity1.id, a=11, b=12) with self.assertRaises(ConcurrencyError): publish(event=replacement_event)