def db(app, account_id, client_id): event_store_db.create_all() operation_id = UniqueID() account_created_event = AccountCreated(operation_id=operation_id.value, client_id=client_id.value, account_id=account_id.value, account_name='test') account_credit_event = AccountCredited(operation_id=operation_id.value, dollars=23, cents=0, account_id=account_id.value) aggregate = AggregateModel(uuid=account_id.value, version=AGGREGATE_VERSION) event1 = EventModel(uuid=str(UniqueID()), aggregate_uuid=account_id.value, name=account_created_event.__class__.__name__, data=asdict(account_created_event)) event2 = EventModel(uuid=str(UniqueID()), aggregate_uuid=account_id.value, name=account_credit_event.__class__.__name__, data=asdict(account_credit_event)) # Add aggregate and event to the db. event_store_db.session.add(aggregate) event_store_db.session.flush() event_store_db.session.add(event1) event_store_db.session.commit() event_store_db.session.add(event2) event_store_db.session.commit() yield event_store_db # this is the object that the tests use event_store_db.drop_all()
def test_initialize_sets_uncommitted_changes(): aggregate = applyless_aggregate() aggregate._initialize([ BaseEvent(operation_id=str(UniqueID())), BaseEvent(operation_id=str(UniqueID())) ]) assert len(aggregate.uncommitted_changes) == 2
def test_add_account_produces_account_added_events_with_new_account_id( new_client): new_account_id = UniqueID() new_client.add_account(new_account_id, UniqueID(), 'test') assert isinstance(new_client.uncommitted_changes[-1], AccountAddedToClient) assert new_client.uncommitted_changes[ -1].account_id == new_account_id.value
def new_client(): client = Client.create(ssn=SocialSecurityNumber(345456567), last_name=LastName("test"), first_name=FirstName("more"), birthdate=Birthdate('23/04/1993'), operation_id=UniqueID()) client.add_account(UniqueID(), UniqueID()) return client
def test_apply_event_is_called_for_each_event_passed_when_loading_aggregate(): with patch.object(AggregateRoot, 'apply_event') as mock: AggregateRoot( EventStream([ BaseEvent(operation_id=str(UniqueID())), BaseEvent(operation_id=str(UniqueID())), BaseEvent(operation_id=str(UniqueID())) ])) assert mock.call_count == 3
def test_maximum_debt_cannot_be_lower_than_balance( new_random_account_with_high_maximum_debt): new_random_account_with_high_maximum_debt.debit( new_random_account_with_high_maximum_debt.maximum_debt / Amount(2, 0), UniqueID()) with pytest.raises(ValueError): new_random_account_with_high_maximum_debt.set_maximum_debt( new_random_account_with_high_maximum_debt.maximum_debt / Amount(3, 0), UniqueID())
def get_operation_id(args: Dict[Any, Any]) -> UniqueID: try: operation_id = args['operation_id'] if operation_id: return UniqueID(operation_id) else: return UniqueID() except KeyError: return UniqueID()
def test_marking_changes_as_committed_adds_committed_events_operation_ids_to_committed_operations( ): aggregate = applyless_aggregate() event1 = BaseEvent(operation_id=str(UniqueID())) event2 = BaseEvent(operation_id=str(UniqueID())) aggregate.apply_event(event1) aggregate.apply_event(event2) aggregate.mark_changes_as_committed() assert event1.operation_id in aggregate.committed_operations assert event2.operation_id in aggregate.committed_operations
def test_save_events_passing_expected_version_for_new_aggregate_throws_concurrency_error( session: Session, postgres_event_store): account_id = UniqueID() account_created_event = AccountCreated(operation_id=str(UniqueID()), client_id=str(UniqueID()), account_id=str(account_id), account_name='test') with pytest.raises(ConcurrencyException): postgres_event_store.save_events(account_id, [account_created_event], expected_version=6)
def test_save_events_tries_to_create_new_aggregate_if_no_expected_version_passed( session: Session, postgres_event_store): account_id = UniqueID() account_created_event = AccountCreated(operation_id=str(UniqueID()), client_id=str(UniqueID()), account_id=str(account_id), account_name='test') postgres_event_store.save_events(account_id, [account_created_event]) found = session.query(AggregateModel).filter( AggregateModel.uuid == account_id.value).all() assert len(found) == 1
def wrapped(target_id): if account_id == target_id: account_created = AccountCreated( operation_id=str(UniqueID()), account_id=str(UniqueID()), client_id=account_id, account_name='test' ) account = Account(EventStream([account_created])) account.set_maximum_debt(Amount(500, 0), UniqueID()) return account return None
def wrapped(target_id: UniqueID): if account_id == target_id: return EventStream([ AccountCreated(operation_id=str(UniqueID()), client_id=str(UniqueID()), account_id=str(account_id), account_name='test'), AccountCredited(dollars=20, cents=0, account_id=account_id.value, operation_id=str(UniqueID)) ], version=2) return None
def test_save_events_adds_related_events_to_new_aggregate( session: Session, postgres_event_store): account_id = UniqueID() account_created_event = AccountCreated(operation_id=str(UniqueID()), client_id=str(UniqueID()), account_id=str(account_id), account_name='test') credit_event = AccountCredited(dollars=22, cents=0, account_id=str(account_id), operation_id=str(UniqueID())) postgres_event_store.save_events( aggregate_id=account_id, events=[account_created_event, credit_event]) aggregate = session.query(AggregateModel).filter( AggregateModel.uuid == account_id.value).one() assert len(aggregate.events) == 2
def test_get_by_id_applies_event_stream(account_repo, fake_event_store): account_id = UniqueID() fake_event_store.load_stream = MagicMock( side_effect=return_based_on_id(account_id)) account = account_repo.get_by_id(account_id) assert account.balance == Amount(20) assert account.version == 2
def _(self, event: ClientCreated) -> None: self._client_id: UniqueID = UniqueID(event.client_id) self._ssn: SocialSecurityNumber = SocialSecurityNumber(event.ssn) self._first_name: FirstName = FirstName(event.first_name) self._last_name: LastName = LastName(event.last_name) self._birthdate: Birthdate = Birthdate(event.birthdate) self._accounts: List[UniqueID] = []
def wrapper(target_id): client = return_based_on_id(client_id)(target_id) if client: client.add_account(account_id, UniqueID(), 'test') client.mark_changes_as_committed() return client return None
def test_remove_account_for_client_adds_removed_account_event_to_uncommitted_changes( fake_client_repo_with_accounts, client_id, account_id): remove_account_from_client(UniqueID(), client_id, account_id, fake_client_repo_with_accounts) account_called_with: Client = fake_client_repo_with_accounts.save.call_args.args[ 0] assert isinstance(account_called_with.uncommitted_changes[-1], AccountRemovedFromClient)
def test_remove_account_produces_account_removed_event_with_right_account_id( new_client_with_account): account_id = new_client_with_account.accounts[0] new_client_with_account.remove_account(account_id, UniqueID()) assert isinstance(new_client_with_account.uncommitted_changes[-1], AccountRemovedFromClient) assert new_client_with_account.uncommitted_changes[ -1].account_id == account_id.value
def test_debit_adds_account_debited_event_to_uncommitted_changes( new_random_account_unlimited_debt, new_random_account_debit_event): amount = Amount(new_random_account_debit_event.dollars, new_random_account_debit_event.cents) new_random_account_unlimited_debt.debit(amount, UniqueID()) assert isinstance( new_random_account_unlimited_debt.uncommitted_changes[-1], AccountDebited)
def test_credit_adds_account_credited_event_to_uncommitted_changes( new_random_account, related_random_account_created_event, new_random_account_credit_event): account = Account(EventStream([related_random_account_created_event])) amount = Amount(new_random_account_credit_event.dollars, new_random_account_credit_event.cents) account.credit(amount, UniqueID(new_random_account_credit_event.operation_id)) assert account.uncommitted_changes[-1] == new_random_account_credit_event
def add_account_to_client(operation_id: UniqueID, client_id: UniqueID, repo: ClientWriteRepository, new_account_name: str) -> UniqueID: client = repo.get_by_id(client_id) new_account_id = UniqueID() client.add_account(account_name=new_account_name, account_id=new_account_id, operation_id=operation_id) repo.save(client) return new_account_id
def test_applying_new_event_with_same_operation_id_as_already_committed_event_raises_app_exception( ): aggregate = applyless_aggregate() operation_id = UniqueID() event1 = BaseEvent(operation_id=operation_id.value) event2 = BaseEvent(operation_id=operation_id.value) aggregate.apply_event(event1) aggregate.mark_changes_as_committed() with pytest.raises(OperationDuplicate): aggregate.apply_event(event2)
def test_create_client_adds_created_event_to_uncommitted_changes( fake_client_repo): create_client( UniqueID(), ClientDetailsDTO(social_security_number=123543234, first_name="asd", last_name="fdsf", birthdate="12/04/2012"), fake_client_repo) account_called_with: Client = fake_client_repo.save.call_args.args[0] assert isinstance(account_called_with.uncommitted_changes[-1], ClientCreated)
def wrapped(target_id): if client_id == target_id: client_created = ClientCreated( operation_id=str(UniqueID()), client_id=client_id.value, ssn=SocialSecurityNumber(123456789).value, first_name=FirstName('asd').value, last_name=LastName('asd').value, birthdate=Birthdate('22/01/1900').value) client = Client(EventStream([client_created])) return client return None
def create(ssn: SocialSecurityNumber, first_name: FirstName, last_name: LastName, birthdate: Birthdate, operation_id: UniqueID) -> 'Client': created_event = ClientCreated(client_id=str(UniqueID()), ssn=ssn.value, first_name=first_name.value, last_name=last_name.value, birthdate=birthdate.value, operation_id=operation_id.value) client = Client(EventStream([created_event])) client._initialize([created_event]) return client
def create_account( operation_id: UniqueID, account_name: str, account_id: UniqueID, client_id: UniqueID, account_repo: AccountWriteRepository, default_maximum_debt: Optional[AmountDTO] = None) -> Account: account = Account.create(client_id, account_id, operation_id, account_name) if default_maximum_debt: account.set_maximum_debt( Amount(default_maximum_debt.dollars, default_maximum_debt.cents), UniqueID()) account_repo.save(account) return account
def save_events(self, aggregate_id: UniqueID, events: List[BaseEvent], expected_version: Optional[int] = None) -> None: if expected_version and expected_version != -1: self._update_aggregate_version_check(aggregate_id, expected_version) else: self._create_aggregate(aggregate_id) for event in events: self.session.add( EventModel(uuid=str(UniqueID()), aggregate_uuid=str(aggregate_id), name=event.__class__.__name__, data=asdict(event)))
def post(self, client_id: str) -> (str, int): try: args = client_add_account_parser.parse_args() operation_id = get_operation_id(args) new_account_name = args['account_name'] with get_client_write_repo() as client_repo: new_account_id = add_account_to_client(operation_id, UniqueID(client_id), client_repo, new_account_name) return Response( status=StatusCodes.CREATED.value, headers={ 'Location': request.base_url + f'/{new_account_id}' } ) except AppException as e: return {'message': str(e)}, e.status except BadRequest as e: return e.data['errors'], e.code except ValueError as e: return {'message': str(e)}, StatusCodes.INVALID_USER_DATA.value except Exception as e: logger.error(e) return str(e), StatusCodes.INTERNAL_SERVER_ERROR.value
def patch(self, account_id): """ Change account balance, credit or debit the account """ try: args = account_balance_parser.parse_args() operation_id = get_operation_id(args) action = args['action'] amount = AmountDTO(dollars=args['dollars'], cents=args['cents']) with get_account_write_repo() as account_repo: action_func = self._get_func_by_action(action) action_func(operation_id, UniqueID(account_id), account_repo, amount) except AppException as e: return {'message': str(e)}, e.status except BadRequest as e: return e.data['errors'], e.code except ValueError as e: return {'message': str(e)}, StatusCodes.INVALID_REQUEST.value except Exception as e: logger.error(e) return str(e), StatusCodes.INTERNAL_SERVER_ERROR.value
def test_str_function_on_unique_id(): g_id = UniqueID('asd') assert str(g_id) == 'asd'