def setUp(self): assert_event_handlers_empty() # Setup the repo, and a persistence subscriber. stored_event_repo = PythonObjectsStoredEventRepository() event_store = EventStore(stored_event_repo=stored_event_repo) self.repo = CollectionRepo(event_store=event_store) self.ps = PersistenceSubscriber(event_store=event_store)
def setUp(self): self.es = EventStore( stored_event_repo=PythonObjectsStoredEventRepository()) self.ps = PersistenceSubscriber(self.es) self.call_dependencies_repo = CallDependenciesRepo(self.es) self.call_dependents_repo = CallDependentsRepo(self.es) self.call_leafs_repo = CallLeafsRepo(self.es) self.call_requirement_repo = CallRequirementRepo(self.es)
def setUp(self): assert_event_handlers_empty() self.es = EventStore(PythonObjectsStoredEventRepository()) self.ps = PersistenceSubscriber(self.es) # self.call_result_repo = CallResultRepo(self.es) self.call_result_repo = {} self.call_dependencies_repo = CallDependenciesRepo(self.es) self.call_dependents_repo = CallDependentsRepo(self.es) self.call_requirement_repo = CallRequirementRepo(self.es) self.policy = CallResultPolicy(call_result_repo=self.call_result_repo)
class TestCollectionRepo(TestCase): def setUp(self): assert_event_handlers_empty() # Setup the repo, and a persistence subscriber. stored_event_repo = PythonObjectsStoredEventRepository() event_store = EventStore(stored_event_repo=stored_event_repo) self.repo = CollectionRepo(event_store=event_store) self.ps = PersistenceSubscriber(event_store=event_store) def tearDown(self): self.ps.close() assert_event_handlers_empty() def test(self): # Check the collection is not in the repo. with self.assertRaises(RepositoryKeyError): _ = self.repo['none'] # Register a new collection. collection_id = register_new_collection().id # Check the collection is in the repo. collection = self.repo[collection_id] self.assertIsInstance(collection, Collection) self.assertEqual(collection.id, collection_id) # Check the collection has zero items. self.assertEqual(len(collection.items), 0) # Add item. item1 = 'item1' collection.add_item(item1) # Check the collection is in the repo. collection = self.repo[collection_id] self.assertIsInstance(collection, Collection) self.assertEqual(collection.id, collection_id) # Check the collection has one item. self.assertEqual(len(collection.items), 1) # Remove item. collection.remove_item(item1) # Check the collection is in the repo. collection = self.repo[collection_id] self.assertIsInstance(collection, Collection) self.assertEqual(collection.id, collection_id) # Check the collection has zero items. self.assertEqual(len(collection.items), 0) # Discard the collection. collection.discard() # Check the collection is not in the repo. with self.assertRaises(RepositoryKeyError): _ = self.repo['none']
class TestExampleEntity(unittest.TestCase): def setUp(self): # Setup the persistence subscriber. self.event_store = EventStore(PythonObjectsStoredEventRepository()) self.persistence_subscriber = PersistenceSubscriber(event_store=self.event_store) def tearDown(self): self.persistence_subscriber.close() def test_entity_lifecycle(self): # Check the factory creates an instance. example1 = register_new_example(a=1, b=2) self.assertIsInstance(example1, Example) self.assertEqual(1, example1.a) self.assertEqual(2, example1.b) # Check a second instance with the same values is not "equal" to the first. example2 = register_new_example(a=1, b=2) self.assertNotEqual(example1, example2) # Setup the repo. repo = ExampleRepository(self.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(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 be discarded. entity1.discard() self.assertRaises(KeyError, repo.__getitem__, entity1.id) def test_mutator_not_implemented_error(self): class UnsupportedEvent(DomainEvent): pass self.assertRaises(NotImplementedError, Example.mutator, Example, UnsupportedEvent('1', '0'))
def setUp(self): super(LogTestCase, self).setUp() # Check we're starting clean, event handler-wise. assert_event_handlers_empty() # Setup the persistence subscriber. self.event_store = EventStore(self.stored_event_repo) self.persistence_subscriber = PersistenceSubscriber( event_store=self.event_store) self.log_repo = LogRepo(self.event_store)
class TestPersistenceSubscriber(unittest.TestCase): def setUp(self): self.mock_event_store = mock.Mock(spec=EventStore) self.persistence_subscriber = PersistenceSubscriber(event_store=self.mock_event_store) def tearDown(self): self.persistence_subscriber.close() def test(self): # Check the publishing a domain event causes 'append' to be called on the event store. self.assertEqual(0, self.mock_event_store.append.call_count) publish(mock.Mock(spec=DomainEvent)) self.assertEqual(1, self.mock_event_store.append.call_count)
def is_domain_event(event): return PersistenceSubscriber.is_domain_event(event) and \ not isinstance(event, ( CallResult.Created, CallResult.Discarded, SimulatedPrice.Created, ))
def setUp(self): self.es = EventStore(stored_event_repo=PythonObjectsStoredEventRepository()) self.ps = PersistenceSubscriber(self.es) self.call_dependencies_repo = CallDependenciesRepo(self.es) self.call_dependents_repo = CallDependentsRepo(self.es) self.call_leafs_repo = CallLeafsRepo(self.es) self.call_requirement_repo = CallRequirementRepo(self.es)
class TestPersistenceSubscriber(unittest.TestCase): def setUp(self): # Set up a persistence subscriber with a (mock) event store. self.event_store = mock.Mock(spec=EventStore) self.persistence_subscriber = PersistenceSubscriber( event_store=self.event_store) def tearDown(self): # Close the persistence subscriber. self.persistence_subscriber.close() def test_published_events_are_appended_to_event_store(self): # Check the event store's append method has NOT been called. self.assertEqual(0, self.event_store.append.call_count) # Publish a (mock) domain event. domain_event = mock.Mock(spec=DomainEvent) publish(domain_event) # Check the append method HAS been called once with the domain event. self.event_store.append.assert_called_once_with(domain_event)
def test_with_snapshots(self): # Check the EventPlayer's take_snapshot() method. stored_event_repo = PythonObjectsStoredEventRepository() event_store = EventStore(stored_event_repo) self.ps = PersistenceSubscriber(event_store) event_player = EventPlayer( event_store=event_store, id_prefix='Example', mutate_func=Example.mutate, snapshot_strategy=EventSourcedSnapshotStrategy(event_store=event_store) ) # Check the method returns None when there are no events. snapshot = event_player.take_snapshot('wrong') self.assertIsNone(snapshot) # Create a new entity. example = register_new_example(a=123, b=234) # Take a snapshot with the entity. snapshot1 = event_player.take_snapshot(example.id) self.assertIsInstance(snapshot1, Snapshot) # Take another snapshot with the entity. snapshot2 = event_player.take_snapshot(example.id) # - should return the previous snapshot self.assertIsInstance(snapshot2, Snapshot) self.assertEqual(snapshot2.at_event_id, snapshot1.at_event_id) # Generate a domain event. example.beat_heart() # Take another snapshot with the entity. # - should use the previous snapshot and the heartbeat event snapshot3 = event_player.take_snapshot(example.id) self.assertNotEqual(snapshot3.at_event_id, snapshot1.at_event_id)
def setUp(self): # Setup the persistence subscriber. self.event_store = EventStore(PythonObjectsStoredEventRepository()) self.persistence_subscriber = PersistenceSubscriber( event_store=self.event_store)
class TestExampleEntity(unittest.TestCase): def setUp(self): # Setup the persistence subscriber. self.event_store = EventStore(PythonObjectsStoredEventRepository()) self.persistence_subscriber = PersistenceSubscriber( event_store=self.event_store) def tearDown(self): self.persistence_subscriber.close() assert_event_handlers_empty() def test_entity_lifecycle(self): # Check the factory creates an instance. example1 = register_new_example(a=1, b=2) self.assertIsInstance(example1, Example) # Check the properties of the Example class. self.assertEqual(1, example1.a) self.assertEqual(2, example1.b) # Check the properties of the EventSourcedEntity class. self.assertTrue(example1.id) self.assertEqual(1, example1.version) self.assertTrue(example1.created_on) # Check a second instance with the same values is not "equal" to the first. example2 = register_new_example(a=1, b=2) self.assertNotEqual(example1, example2) # Setup the repo. repo = ExampleRepo(self.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(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 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. self.assertRaises( EntityIDConsistencyError, entity2._validate_originator, DomainEvent(entity_id=entity2.id + 'wrong', entity_version=0)) # Should fail to validate event with wrong entity version. self.assertRaises(EntityVersionConsistencyError, entity2._validate_originator, DomainEvent(entity_id=entity2.id, entity_version=0)) # Should validate event with correct entity ID and version. entity2._validate_originator( DomainEvent(entity_id=entity2.id, entity_version=entity2.version)) # Check an entity can be reregistered with the same ID. replacement_event = Example.Created(entity_id=entity1.id, a=11, b=12) # replacement = Example.mutate(event=replacement_event) publish(event=replacement_event) # Check the replacement entity can be retrieved from the example repository. replacement = repo[entity1.id] assert isinstance(replacement, Example) self.assertEqual(replacement.a, 11) self.assertEqual(replacement.b, 12) def test_not_implemented_error(self): # Define an event class. class UnsupportedEvent(DomainEvent): pass # Check we get an error when attempting to mutate on the event. self.assertRaises(NotImplementedError, Example.mutate, Example, UnsupportedEvent('1', '0')) def test_mutableproperty(self): # Check we get an error when called with something other than a function. self.assertRaises(ProgrammingError, mutableproperty, 'not a getter') self.assertRaises(ProgrammingError, mutableproperty, 123) self.assertRaises(ProgrammingError, mutableproperty, None) # Call the decorator with a function. getter = lambda: None p = mutableproperty(getter) # Check we got a property object. self.assertIsInstance(p, property) # Check the property object has both setter and getter functions. self.assertTrue(p.fset) self.assertTrue(p.fget) # Pretend we decorated an object. o = EventSourcedEntity(entity_id='1', entity_version=0, domain_event_id=1) o.__dict__['_<lambda>'] = 'value1' # Call the property's getter function. value = p.fget(o) self.assertEqual(value, 'value1') # Call the property's setter function. p.fset(o, 'value2') # Check the attribute has changed. value = p.fget(o) self.assertEqual(value, 'value2') # Check the property's getter function isn't the getter function we passed in. self.assertNotEqual(p.fget, getter) # Define a class that uses the decorator. class Aaa(EventSourcedEntity): "An event sourced entity." def __init__(self, a, *args, **kwargs): super(Aaa, self).__init__(*args, **kwargs) self._a = a @mutableproperty def a(self): "A mutable event sourced property." # Instantiate the class and check assigning to the property publishes an event and updates the object state. published_events = [] subscription = (lambda x: True, lambda x: published_events.append(x)) subscribe(*subscription) entity_id = '1' try: aaa = Aaa(entity_id=entity_id, entity_version=None, domain_event_id='0', a=1) self.assertEqual(aaa.a, 1) aaa.a = 'value1' self.assertEqual(aaa.a, 'value1') finally: unsubscribe(*subscription) # Check an event was published. self.assertEqual(len(published_events), 1) # Check the published event was an AttributeChanged event, with the expected attribute values. published_event = published_events[0] self.assertIsInstance(published_event, Aaa.AttributeChanged) self.assertEqual(published_event.name, '_a') self.assertEqual(published_event.value, 'value1') self.assertTrue(published_event.domain_event_id) self.assertEqual(published_event.entity_id, entity_id) def test_static_mutator_method(self): self.assertRaises(NotImplementedError, EventSourcedEntity._mutator, 1, 2) def test_created_mutator_error(self): self.assertRaises(CreatedMutatorRequiresTypeNotInstance, created_mutator, mock.Mock(spec=DomainEvent), 'not a class')
def setUp(self): # Setup the persistence subscriber. self.event_store = EventStore(PythonObjectsStoredEventRepository()) self.persistence_subscriber = PersistenceSubscriber(event_store=self.event_store)
class TestInstanceEntity(unittest.TestCase): def setUp(self): # Setup the persistence subscriber. self.event_store = EventStore(PythonObjectsStoredEventRepository()) self.persistence_subscriber = PersistenceSubscriber(event_store=self.event_store) def tearDown(self): self.persistence_subscriber.close() assert_event_handlers_empty() def test_entity_lifecycle(self): # Check the factory creates an instance. instance1 = register_new_instance(atmo_id=27216, name='Ubuntu 14.04.2 XFCE Base', username='******') self.assertIsInstance(instance1, Instance) # Check the properties of the Instance class. self.assertEqual(27216, instance1.atmo_id) self.assertEqual('Ubuntu 14.04.2 XFCE Base', instance1.name) self.assertEqual('amitj', instance1.username) self.assertEqual('unknown', instance1.status) self.assertEqual('', instance1.activity) self.assertEqual({-1, -1, -1}, instance1.size) # Check the properties of the Instance class. self.assertTrue(instance1.id) self.assertEqual(1, instance1.version) self.assertTrue(instance1.created_on) # Check a second instance with the same values is not "equal" to the first. # TODO: Actually, maybe it will be, and more importantly, _should_ be. instance2 = register_new_instance(atmo_id=27216, name='Ubuntu 14.04.2 XFCE Base', username='******') self.assertNotEqual(instance1, instance2) # Setup the repo. repo = InstanceRepo(self.event_store) # Check the allocation source entities can be retrieved from the allocation source repository. entity1 = repo[instance1.id] self.assertIsInstance(entity1, Instance) self.assertEqual(27216, entity1.atmo_id) self.assertEqual('Ubuntu 14.04.2 XFCE Base', entity1.name) entity2 = repo[instance2.id] self.assertIsInstance(entity2, Instance) self.assertEqual(27216, entity2.atmo_id) self.assertEqual('Ubuntu 14.04.2 XFCE Base', entity2.name) # Check the mutable properties can be updated, but not the immutable ones: # Immutable: # - atmo_id # - username # # Mutable: # - name # - status # - activity # - size with self.assertRaises(AttributeError): # noinspection PyPropertyAccess entity1.atmo_id = 27217 self.assertEqual(27216, repo[entity1.id].atmo_id) with self.assertRaises(AttributeError): # noinspection PyPropertyAccess entity1.username = '******' self.assertEqual('amitj', repo[entity1.id].username) entity1.name = 'Ubuntu 16.04.1 XFCE Base' self.assertEqual('Ubuntu 16.04.1 XFCE Base', repo[entity1.id].name) entity1.status = 'pending' self.assertEqual('pending', repo[entity1.id].status) entity1.activity = 'networking' self.assertEqual('networking', repo[entity1.id].activity) entity1.size = {'mem': '65536', 'disk': '0', 'cpu': '16'} self.assertEqual({'mem': '65536', 'disk': '0', 'cpu': '16'}, repo[entity1.id].size) 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 be discarded. entity1.discard() # Check the repo now raises a KeyError. self.assertRaises(KeyError, 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. self.assertRaises(EntityIDConsistencyError, entity2._validate_originator, DomainEvent(entity_id=entity2.id + 'wrong', entity_version=0) ) # Should fail to validate event with wrong entity version. self.assertRaises(EntityVersionConsistencyError, entity2._validate_originator, DomainEvent(entity_id=entity2.id, entity_version=0) ) # Should validate event with correct entity ID and version. entity2._validate_originator( DomainEvent(entity_id=entity2.id, entity_version=entity2.version) ) def test_entity_created_date(self): instance_created_timestamp = datetime_to_timestamp(datetime.datetime(2016, 1, 1, tzinfo=utc_timezone)) domain_event_id = uuid_from_timestamp(instance_created_timestamp) event = Instance.Created(entity_id='instance1', atmo_id=27216, name='Ubuntu 14.04.2 XFCE Base', username='******', domain_event_id=domain_event_id) instance1 = Instance.mutate(event=event) publish(event=event) self.assertIsInstance(instance1, Instance) # Check the properties of the Instance class. self.assertEqual(27216, instance1.atmo_id) self.assertEqual('Ubuntu 14.04.2 XFCE Base', instance1.name) # Check the properties of the Instance class. self.assertTrue(instance1.id) self.assertEqual(1, instance1.version) self.assertTrue(instance1.created_on) self.assertAlmostEqual(instance_created_timestamp, instance1.created_on) def test_not_implemented_error(self): # Define an event class. class UnsupportedEvent(DomainEvent): pass # Check we get an error when attempting to mutate on the event. self.assertRaises(NotImplementedError, Instance.mutate, Instance, UnsupportedEvent('1', '0'))
class TestDependencyGraph(unittest.TestCase): def setUp(self): self.es = EventStore( stored_event_repo=PythonObjectsStoredEventRepository()) self.ps = PersistenceSubscriber(self.es) self.call_dependencies_repo = CallDependenciesRepo(self.es) self.call_dependents_repo = CallDependentsRepo(self.es) self.call_leafs_repo = CallLeafsRepo(self.es) self.call_requirement_repo = CallRequirementRepo(self.es) def tearDown(self): self.ps.close() def test_generate_dependency_graph_with_function_call(self): contract_specification = register_contract_specification( specification=""" def double(x): return x * 2 double(1 + 1) """) generate_dependency_graph( contract_specification=contract_specification, call_dependencies_repo=self.call_dependencies_repo, call_dependents_repo=self.call_dependents_repo, call_leafs_repo=self.call_leafs_repo, call_requirement_repo=self.call_requirement_repo, ) root_dependencies = self.call_dependencies_repo[ contract_specification.id] assert isinstance(root_dependencies, CallDependencies) self.assertEqual(len(root_dependencies.dependencies), 1) root_dependency = root_dependencies.dependencies[0] call_dependencies = self.call_dependencies_repo[root_dependency] self.assertEqual(len(call_dependencies.dependencies), 0) dependency = self.call_dependents_repo[root_dependency] assert isinstance(dependency, CallDependents) self.assertEqual(len(dependency.dependents), 1) self.assertEqual(dependency.dependents[0], contract_specification.id) # def test_generate_dependency_graph_recursive_functional_call(self): # contract_specification = self.app.register_contract_specification(specification=""" # def inc(x): # if x < 10: # return inc(x+1) + inc(x+2) # else: # return 100 # # inc(1 + 2) # """) # # dependency_graph = self.app.generate_dependency_graph(contract_specification=contract_specification) # # call_dependencies = self.app.call_dependencies_repo[dependency_graph.id] # self.assertEqual(len(call_dependencies.requirements), 1) # dependency_id = call_dependencies.requirements[0] # dependents = self.app.call_dependents_repo[dependency_id].dependents # self.assertEqual(len(dependents), 1) # self.assertIn(dependency_graph.id, dependents) # # A circular dependency... # self.assertIn(dependency_id, dependents) def test_generate_execution_order(self): # Dependencies: # 1 -> 2 # 1 -> 3 # 3 -> 2 # Therefore dependents: # 1 = [] # 2 = [1, 3] # 3 = [1] # 2 depends on nothing, so 2 is a leaf. # 1 depends on 3 and 2, so 1 is not next. # 3 depends only on 2, so is next. # Therefore 1 is last. # Hence evaluation order: 2, 3, 1 call_dependencies_repo = MagicMock(spec=CallDependenciesRepository, __getitem__=lambda self, x: { 1: [2, 3], 2: [], 3: [2] }[x]) call_dependents_repo = MagicMock(spec=CallDependentsRepository, __getitem__=lambda self, x: { 1: [], 2: [1, 3], 3: [1] }[x]) leaf_call_ids = [2] execution_order_gen = generate_execution_order(leaf_call_ids, call_dependents_repo, call_dependencies_repo) execution_order = list(execution_order_gen) self.assertEqual(execution_order, [2, 3, 1]) def test_get_dependency_values(self): call_dependencies_repo = MagicMock( spec=CallDependenciesRepository, __getitem__=lambda self, x: { '1': CallDependencies(dependencies=['2', '3'], entity_id=123, entity_version=0, timestamp=1), }[x]) call_result_repo = MagicMock( spec=CallResultRepository, __getitem__=lambda self, x: { 'valuation2': Mock(spec=CallResult, result_value=12, perturbed_values={}), 'valuation3': Mock(spec=CallResult, result_value=13, perturbed_values={}), }[x]) values = get_dependency_results('valuation', '1', call_dependencies_repo, call_result_repo) self.assertEqual(values, {'2': (12, {}), '3': (13, {})})
class TestCallResultPolicy(TestCase): def setUp(self): assert_event_handlers_empty() self.es = EventStore(PythonObjectsStoredEventRepository()) self.ps = PersistenceSubscriber(self.es) # self.call_result_repo = CallResultRepo(self.es) self.call_result_repo = {} self.call_dependencies_repo = CallDependenciesRepo(self.es) self.call_dependents_repo = CallDependentsRepo(self.es) self.call_requirement_repo = CallRequirementRepo(self.es) self.policy = CallResultPolicy(call_result_repo=self.call_result_repo) def tearDown(self): self.ps.close() self.policy.close() assert_event_handlers_empty() def test_delete_result(self): # In this test, there are two "calls": call1 and call2. # It is supposed that call1 happens first, and call2 uses the result of call1. # Therefore call2 depends upon call1, call1 is a dependency of call2, and call2 is a dependent of call1. call1_id = 'call1' call2_id = 'call2' contract_valuation_id = 'val1' contract_specification_id = 'spec1' # call1_id = uuid4().hex # call2_id = uuid4().hex # contract_valuation_id = uuid4().hex # contract_specification_id = uuid4().hex register_call_dependencies(call2_id, [call1_id]) # Check the policy has the dependencies for call2. self.assertEqual(self.policy.dependencies[call2_id], [call1_id]) # Register dependents of call1, as call2. register_call_dependents(call1_id, [call2_id]) # Check the policy has the dependencies for call2. self.assertEqual(self.policy.dependents[call1_id], [call2_id]) # Register call result for call1. # - this should trigger deletion of call2 result call1_result = register_call_result(call1_id, 1.0, {}, contract_valuation_id, contract_specification_id, []) # Check the policy has the result for call1. self.assertTrue(call1_result.id in self.policy.result) # Check the result for call1 exists. self.assertTrue(call1_result.id in self.call_result_repo) # Register call result for call2. call2_result = register_call_result(call2_id, 1.0, {}, contract_valuation_id, contract_specification_id, []) # Check the policy has the result for call2. self.assertTrue(call2_result.id in self.policy.result) # Check the result for call2 exists. self.assertTrue(call2_result.id in self.call_result_repo) # Check the policy does not have the result for call1. self.assertFalse(call1_result.id in self.policy.result) # Check the result for call1 doesn't exist (because it's dependents have results). self.assertFalse(call1_result.id in self.call_result_repo)
def setUp(self): self.es = EventStore(stored_event_repo=InMemoryStoredEventRepository()) self.ps = PersistenceSubscriber(self.es) self.call_dependencies_repo = CallDependenciesRepo(self.es) self.call_dependents_repo = CallDependentsRepo(self.es)
class TestAllocationSourceEntity(unittest.TestCase): def setUp(self): # Setup the persistence subscriber. self.event_store = EventStore(PythonObjectsStoredEventRepository()) self.persistence_subscriber = PersistenceSubscriber(event_store=self.event_store) def tearDown(self): self.persistence_subscriber.close() assert_event_handlers_empty() def test_entity_lifecycle(self): # Check the factory creates an instance. allocation_source1 = register_new_allocation_source(a=1, b=2) self.assertIsInstance(allocation_source1, AllocationSource) # Check the properties of the AllocationSource class. self.assertEqual(1, allocation_source1.a) self.assertEqual(2, allocation_source1.b) # Check the properties of the EventSourcedEntity class. self.assertTrue(allocation_source1.id) self.assertEqual(1, allocation_source1.version) self.assertTrue(allocation_source1.created_on) # Check a second instance with the same values is not "equal" to the first. allocation_source2 = register_new_allocation_source(a=1, b=2) self.assertNotEqual(allocation_source1, allocation_source2) # Setup the repo. repo = AllocationSourceRepo(self.event_store) # Check the allocation source entities can be retrieved from the allocation source repository. entity1 = repo[allocation_source1.id] self.assertIsInstance(entity1, AllocationSource) self.assertEqual(1, entity1.a) self.assertEqual(2, entity1.b) entity2 = repo[allocation_source2.id] self.assertIsInstance(entity2, AllocationSource) 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(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 be discarded. entity1.discard() # Check the repo now raises a KeyError. self.assertRaises(KeyError, 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. self.assertRaises(EntityIDConsistencyError, entity2._validate_originator, DomainEvent(entity_id=entity2.id + 'wrong', entity_version=0) ) # Should fail to validate event with wrong entity version. self.assertRaises(EntityVersionConsistencyError, entity2._validate_originator, DomainEvent(entity_id=entity2.id, entity_version=0) ) # Should validate event with correct entity ID and version. entity2._validate_originator( DomainEvent(entity_id=entity2.id, entity_version=entity2.version) ) def test_not_implemented_error(self): # Define an event class. class UnsupportedEvent(DomainEvent): pass # Check we get an error when attempting to mutate on the event. self.assertRaises(NotImplementedError, AllocationSource.mutate, AllocationSource, UnsupportedEvent('1', '0'))
def setUp(self): self.mock_event_store = mock.Mock(spec=EventStore) self.persistence_subscriber = PersistenceSubscriber(event_store=self.mock_event_store)
class LogTestCase(AbstractTestCase): @property def stored_event_repo(self): """ Returns a stored event repository. Concrete log test cases will provide this method. """ raise NotImplementedError def setUp(self): super(LogTestCase, self).setUp() # Check we're starting clean, event handler-wise. assert_event_handlers_empty() # Setup the persistence subscriber. self.event_store = EventStore(self.stored_event_repo) self.persistence_subscriber = PersistenceSubscriber( event_store=self.event_store) self.log_repo = LogRepo(self.event_store) def tearDown(self): # Close the persistence subscriber. self.persistence_subscriber.close() super(LogTestCase, self).tearDown() # Check we finished clean, event handler-wise. assert_event_handlers_empty() def test_entity_lifecycle(self): log = self.log_repo.get_or_create(log_name='log1', bucket_size='year') self.assertIsInstance(log, Log) self.assertEqual(log.name, 'log1') self.assertEqual(log.bucket_size, 'year') # Test get_logger and get_log_reader(). logger = get_logger(log) self.assertIsInstance(logger, Logger) message1 = 'This is message 1' message2 = 'This is message 2' message3 = 'This is message 3' message4 = 'This is message 4' message5 = 'This is message 5' message6 = 'This is message 6' event1 = logger.info(message1) event2 = logger.info(message2) event3 = logger.info(message3) halfway = uuid1().hex event4 = logger.info(message4) event5 = logger.info(message5) event6 = logger.info(message6) # Check we can get all the messages (query running in descending order). log_reader = get_log_reader(log, event_store=self.event_store) messages = list(log_reader.get_messages(is_ascending=False)) self.assertEqual(len(messages), 6) self.assertEqual(messages[0], message6) self.assertEqual(messages[1], message5) self.assertEqual(messages[2], message4) self.assertEqual(messages[3], message3) self.assertEqual(messages[4], message2) self.assertEqual(messages[5], message1) # Check we can get all the messages (query running in ascending order). messages = list(log_reader.get_messages(is_ascending=True)) self.assertEqual(len(messages), 6) self.assertEqual(messages[0], message1) self.assertEqual(messages[1], message2) self.assertEqual(messages[2], message3) self.assertEqual(messages[3], message4) self.assertEqual(messages[4], message5) self.assertEqual(messages[5], message6) # Check we can get messages after halfway (query running in descending order). messages = list( log_reader.get_messages(after=halfway, is_ascending=False)) self.assertEqual(len(messages), 3) self.assertEqual(messages[0], message6) self.assertEqual(messages[1], message5) self.assertEqual(messages[2], message4) # Check we can get messages until halfway (query running in descending order). messages = list( log_reader.get_messages(until=halfway, is_ascending=False)) self.assertEqual(len(messages), 3) self.assertEqual(messages[0], message3) self.assertEqual(messages[1], message2) self.assertEqual(messages[2], message1) # Check we can get messages until halfway (query running in ascending order). messages = list( log_reader.get_messages(until=halfway, is_ascending=True)) self.assertEqual(len(messages), 3) self.assertEqual(messages[0], message1) self.assertEqual(messages[1], message2) self.assertEqual(messages[2], message3) # Check we can get messages after halfway (query running in ascending order). messages = list( log_reader.get_messages(after=halfway, is_ascending=True)) self.assertEqual(len(messages), 3) self.assertEqual(messages[0], message4) self.assertEqual(messages[1], message5) self.assertEqual(messages[2], message6) # Check we can get last three messages (query running in descending order). messages = list(log_reader.get_messages(limit=3, is_ascending=False)) self.assertEqual(len(messages), 3) self.assertEqual(messages[0], message6) self.assertEqual(messages[1], message5) self.assertEqual(messages[2], message4) # Check we can get first three messages (query running in ascending order). messages = list(log_reader.get_messages(limit=3, is_ascending=True)) self.assertEqual(len(messages), 3) self.assertEqual(messages[0], message1) self.assertEqual(messages[1], message2) self.assertEqual(messages[2], message3) # Check we can get last line (query running in descending order). messages = list( log_reader.get_messages(limit=1, after=halfway, is_ascending=False)) self.assertEqual(len(messages), 1) self.assertEqual(messages[0], message6) # Check we can get the first line after halfway (query running in ascending order). messages = list( log_reader.get_messages(limit=1, after=halfway, is_ascending=True)) self.assertEqual(len(messages), 1) self.assertEqual(messages[0], message4) # Check we can get the first line before halfway (query running in descending order). messages = list( log_reader.get_messages(limit=1, until=halfway, is_ascending=False)) self.assertEqual(len(messages), 1) self.assertEqual(messages[0], message3) # Check we can get the first line (query running in ascending order). messages = list( log_reader.get_messages(limit=1, until=halfway, is_ascending=True)) self.assertEqual(len(messages), 1) self.assertEqual(messages[0], message1) # Check there isn't a line after the last line (query running in ascending order). messages = list( log_reader.get_messages(limit=1, after=event6.domain_event_id, is_ascending=True)) self.assertEqual(len(messages), 0) # Check there is nothing somehow both after and until halfway. messages = list(log_reader.get_messages(after=halfway, until=halfway)) self.assertEqual(len(messages), 0) def test_buckets(self): # Start new log. log = start_new_log(name='log1', bucket_size='second') # Write messages across the time interval start = datetime.datetime.now() logger = get_logger(log) number_of_messages = 300 events = [] for i in range(number_of_messages): message_logged = logger.info(str(i)) events.append(message_logged) sleep(0.01) self.assertGreater(datetime.datetime.now() - start, datetime.timedelta(seconds=1)) # Get the messages in descending order. reader = get_log_reader(log, self.event_store) messages = list(reader.get_messages(is_ascending=False, page_size=10)) self.assertEqual(len(messages), number_of_messages) # Expect the order of the messages is the reverse of the created order. self.assertEqual( messages, list(reversed([str(i) for i in range(number_of_messages)]))) # Get the messages in ascending order. messages = list(reader.get_messages(is_ascending=True, page_size=10)) self.assertEqual(len(messages), number_of_messages) # Expect the order of the messages is the same as the created order. self.assertEqual(messages, [str(i) for i in range(number_of_messages)]) # Get a limited number of messages in descending order. limit = 150 messages = list( reader.get_messages(is_ascending=False, page_size=10, limit=limit)) self.assertLess(limit, number_of_messages) self.assertEqual(len(messages), limit) # Expect the order of the messages is the reverse of the created order. self.assertEqual( messages, list(reversed([str(i) for i in range(number_of_messages)]))[:limit]) # Get a limited number of messages in ascending order. limit = 150 messages = list( reader.get_messages(is_ascending=True, page_size=10, limit=limit)) self.assertLess(limit, number_of_messages) self.assertEqual(len(messages), limit) # Expect the order of the messages is the same as the created order. self.assertEqual(messages, [str(i) for i in range(limit)]) # Get a limited number of messages in descending order from the midpoint down. limit = 110 midpoint = events[150].domain_event_id messages = list( reader.get_messages(is_ascending=False, page_size=10, limit=limit, until=midpoint)) self.assertLess(limit, number_of_messages) self.assertEqual(len(messages), limit) # Expect the order of the messages is the reverse of the created order. self.assertEqual( messages, list(reversed([str(i) for i in range(150 - limit, 150)]))) # Get a limited number of messages in ascending order from the midpoint up. limit = 110 midpoint = events[149].domain_event_id messages = list( reader.get_messages(is_ascending=True, page_size=10, limit=limit, after=midpoint)) self.assertLess(limit, number_of_messages) self.assertEqual(len(messages), limit) # Expect the order of the messages is the same as the created order. self.assertEqual(messages, [str(i) for i in range(150, 150 + limit)]) # Get a limited number of messages in descending order above the midpoint down. limit = 200 midpoint = events[150].domain_event_id messages = list( reader.get_messages(is_ascending=False, page_size=10, limit=limit, after=midpoint)) self.assertLess(limit, number_of_messages) self.assertEqual(len(messages), 150) # Expect the order of the messages is the reverse of the created order. self.assertEqual(messages, list(reversed([str(i) for i in range(150, 300)]))) # Get a limited number of messages in ascending order below the midpoint up. limit = 200 midpoint = events[149].domain_event_id messages = list( reader.get_messages(is_ascending=True, page_size=10, limit=limit, until=midpoint)) self.assertLess(limit, number_of_messages) self.assertEqual(len(messages), 150) # Expect the order of the messages is the same as the created order. self.assertEqual(messages, [str(i) for i in range(150)]) # # Use the last position to start part way through. limit = 20 last_position = reader.position messages = reader.get_messages(is_ascending=True, page_size=10, limit=limit, after=last_position) messages = list(messages) self.assertEqual(len(messages), limit) # Expect the order of the messages is the same as the created order. self.assertEqual(messages, [str(i) for i in range(150, 150 + limit)]) # Do it again. last_position = reader.position messages = reader.get_messages(is_ascending=True, page_size=10, limit=limit, after=last_position) messages = list(messages) self.assertEqual(len(messages), limit) # Expect the order of the messages is the same as the created order. self.assertEqual(messages, [str(i) for i in range(150 + limit, 150 + limit * 2)]) # Go back. last_position = reader.position messages = reader.get_messages(is_ascending=False, page_size=10, limit=limit, until=last_position) messages = list(messages) self.assertEqual(len(messages), limit) # Expect the order of the messages is the reverse of the created order. self.assertEqual( messages, [str(i) for i in range(148 + limit * 2, 148 + limit, -1)]) # Go back. last_position = reader.position messages = reader.get_messages(is_ascending=False, page_size=10, limit=limit, until=last_position) messages = list(messages) self.assertEqual(len(messages), limit) # Expect the order of the messages is the reverse of the created order. self.assertEqual( messages, [str(i) for i in range(128 + limit * 2, 128 + limit, -1)]) # Go back. last_position = reader.position messages = reader.get_messages(is_ascending=False, page_size=10, limit=limit, until=last_position) messages = list(messages) self.assertEqual(len(messages), limit) # Expect the order of the messages is the reverse of the created order. self.assertEqual( messages, [str(i) for i in range(108 + limit * 2, 108 + limit, -1)]) # Repeat. messages = reader.get_messages(is_ascending=False, page_size=10, limit=limit, until=last_position) messages = list(messages) self.assertEqual(len(messages), limit) # Expect the order of the messages is the reverse of the created order. self.assertEqual( messages, [str(i) for i in range(108 + limit * 2, 108 + limit, -1)])
class TestDependencyGraph(unittest.TestCase): def setUp(self): self.es = EventStore(stored_event_repo=PythonObjectsStoredEventRepository()) self.ps = PersistenceSubscriber(self.es) self.call_dependencies_repo = CallDependenciesRepo(self.es) self.call_dependents_repo = CallDependentsRepo(self.es) self.call_leafs_repo = CallLeafsRepo(self.es) self.call_requirement_repo = CallRequirementRepo(self.es) def tearDown(self): self.ps.close() def test_generate_dependency_graph_with_function_call(self): contract_specification = register_contract_specification(source_code=""" def double(x): return x * 2 double(1 + 1) """) generate_dependency_graph( contract_specification=contract_specification, call_dependencies_repo=self.call_dependencies_repo, call_dependents_repo=self.call_dependents_repo, call_requirement_repo=self.call_requirement_repo, ) root_dependencies = self.call_dependencies_repo[contract_specification.id] assert isinstance(root_dependencies, CallDependencies) self.assertEqual(len(root_dependencies.dependencies), 1) root_dependency = root_dependencies.dependencies[0] call_dependencies = self.call_dependencies_repo[root_dependency] self.assertEqual(len(call_dependencies.dependencies), 0) dependency = self.call_dependents_repo[root_dependency] assert isinstance(dependency, CallDependents) self.assertEqual(len(dependency.dependents), 1) self.assertEqual(dependency.dependents[0], contract_specification.id) # def test_generate_dependency_graph_recursive_functional_call(self): # contract_specification = self.app.register_contract_specification(specification=""" # def inc(x): # if x < 10: # return inc(x+1) + inc(x+2) # else: # return 100 # # inc(1 + 2) # """) # # dependency_graph = self.app.generate_dependency_graph(contract_specification=contract_specification) # # call_dependencies = self.app.call_dependencies_repo[dependency_graph.id] # self.assertEqual(len(call_dependencies.requirements), 1) # dependency_id = call_dependencies.requirements[0] # dependents = self.app.call_dependents_repo[dependency_id].dependents # self.assertEqual(len(dependents), 1) # self.assertIn(dependency_graph.id, dependents) # # A circular dependency... # self.assertIn(dependency_id, dependents) def test_generate_execution_order(self): # Dependencies: # 1 -> 2 # 1 -> 3 # 3 -> 2 # Therefore dependents: # 1 = [] # 2 = [1, 3] # 3 = [1] # 2 depends on nothing, so 2 is a leaf. # 1 depends on 3 and 2, so 1 is not next. # 3 depends only on 2, so is next. # Therefore 1 is last. # Hence evaluation order: 2, 3, 1 call_dependencies_repo = MagicMock(spec=CallDependenciesRepository, __getitem__=lambda self, x: { 1: [2, 3], 2: [], 3: [2] }[x]) call_dependents_repo = MagicMock(spec=CallDependentsRepository, __getitem__=lambda self, x: { 1: [], 2: [1, 3], 3: [1] }[x]) leaf_call_ids = [2] execution_order_gen = generate_execution_order(leaf_call_ids, call_dependents_repo, call_dependencies_repo) execution_order = list(execution_order_gen) self.assertEqual(execution_order, [2, 3, 1]) def test_get_dependency_values(self): call_dependencies_repo = MagicMock(spec=CallDependenciesRepository, __getitem__=lambda self, x: { '1': CallDependencies(dependencies=['2', '3'], entity_id=123, entity_version=0, timestamp=1), }[x]) call_result1 = Mock(spec=CallResult, result_value=12, perturbed_values={}) call_result2 = Mock(spec=CallResult, result_value=13, perturbed_values={}) call_result_repo = MagicMock(spec=CallResultRepository, __getitem__=lambda self, x: { 'valuation2': call_result1, 'valuation3': call_result2, }[x]) values = get_dependency_results('valuation', '1', call_dependencies_repo, call_result_repo) self.assertEqual(len(values), 2) self.assertEqual(values['2'].result_value, 12) self.assertEqual(values['3'].result_value, 13)
def test_snapshots(self): stored_event_repo = PythonObjectsStoredEventRepository() event_store = EventStore(stored_event_repo) self.ps = PersistenceSubscriber(event_store) event_player = EventPlayer(event_store=event_store, id_prefix='Example', mutate_func=Example.mutate) # Create a new entity. registered_example = register_new_example(a=123, b=234) # Take a snapshot. snapshot = take_snapshot(registered_example, uuid1().hex) # Replay from this snapshot. after = snapshot.at_event_id initial_state = entity_from_snapshot(snapshot) retrieved_example = event_player.replay_events(registered_example.id, initial_state=initial_state, after=after) # Check the attributes are correct. self.assertEqual(retrieved_example.a, 123) # Remember the time now. timecheck1 = uuid1().hex # Change attribute value. retrieved_example.a = 999 # Check the initial state doesn't move. self.assertEqual(initial_state.a, 123) # Remember the time now. timecheck2 = uuid1().hex # Change attribute value. retrieved_example.a = 9999 # Remember the time now. timecheck3 = uuid1().hex # Check the event sourced entities are correct. assert initial_state.a == 123 retrieved_example = event_player.replay_events(registered_example.id) self.assertEqual(retrieved_example.a, 9999) # Take another snapshot. snapshot2 = take_snapshot(retrieved_example, uuid1().hex) # Check we can replay from this snapshot. initial_state2 = entity_from_snapshot(snapshot2) after2 = snapshot2.domain_event_id retrieved_example = event_player.replay_events(registered_example.id, initial_state=initial_state2, after=after2) # Check the attributes are correct. self.assertEqual(retrieved_example.a, 9999) # Check we can get historical state at timecheck1. retrieved_example = event_player.replay_events(registered_example.id, until=timecheck1) self.assertEqual(retrieved_example.a, 123) # Check we can get historical state at timecheck2. retrieved_example = event_player.replay_events(registered_example.id, until=timecheck2) self.assertEqual(retrieved_example.a, 999) # Check we can get historical state at timecheck3. retrieved_example = event_player.replay_events(registered_example.id, until=timecheck3) self.assertEqual(retrieved_example.a, 9999) # Similarly, check we can get historical state using a snapshot retrieved_example = event_player.replay_events(registered_example.id, initial_state=initial_state, after=after, until=timecheck2) self.assertEqual(retrieved_example.a, 999)
def setUp(self): # Set up a persistence subscriber with a (mock) event store. self.event_store = mock.Mock(spec=EventStore) self.persistence_subscriber = PersistenceSubscriber( event_store=self.event_store)
def create_persistence_subscriber(self): if self.persist_events and self.event_store: return PersistenceSubscriber(self.event_store)