def write_authorized_transactions(self) -> None: """ Método responsável por validar se existe transações não aprovadas antes de abrir e efetuar a escrita no arquivo. :return: Não possui """ unaunthorized_transactions = self._transaction_facade.unauthorized_transactions() if not unaunthorized_transactions: self._logger.info(_('logging.no_transactions_to_write')) return with open(self.__file_path, 'w') as file: self.__write_file(file, unaunthorized_transactions) self._logger.info(_('logging.transactions_file_write_success'))
def load_data(self, file_name: str = get_file_operations_name()) -> Tuple[List[dict], List[str]]: """ Objetivo do método é efetuar iteração sobre as linhas do arquivo de importação, criando objeto conforme o modelo definido pelos fields :param file_name: Caminho do arquivo :return: Lista de dicionários representando o modelo definido pelos fields """ record_success = [] record_errors = [] file_path = f'{get_read_operations_path()}/{file_name}' with open(file_path, 'r') as file_data: lines = file_data.readlines() for line in lines: try: data = json.loads(line) for field, value in zip(self._fields.keys(), data.values): data[field] = self._fields[field](value) if value else None data = self.get_default_fields(data) record_success.append(data) except ValueError: self.__logger.error(_('logging.loader_field_value_error'), exc_info=True) record_errors.append(line) return record_success, record_errors
def test_authorize_transaction_when_payload_error_then_do_nothing_and_set_unauthorized_transaction( self, service_mock, schema_mock: dict): # arrange first_transaction_index = 0 loaded = mock.MagicMock() loaded.errors = 'teste' schema_mock.load.return_value = loaded service_mock.do_transaction.return_value = True # act self._transaction_facade.authorize_transaction(self.payload) # assert schema_mock.load.assert_called_once_with(self.payload, many=False) service_mock.do_transaction.assert_not_called() transaction_payload = self._transaction_facade.unauthorized_transactions( )[first_transaction_index] self.assertEqual(transaction_payload['violations'][0], _('messages.payload_error')) self.assertEqual( len(self._transaction_facade.unauthorized_transactions()), 1)
def __validate_transaction_low_score(transaction: Transaction) -> None: """ Método responsável por verificar se uma transação possui um score menor que o mínimo permitido :param transaction: Instância de um objeto dataclass com representando uma entidade "transaction" :return: Opcional exception "BusinessException" com uma mensagem padrão de erro. """ if transaction.score < MININUM_SCORE_VALUE_PERMITED: raise BusinessException(_('messages.low_score'))
def __validate_transaction_installments(transaction: Transaction) -> None: """ Método responsável por verificar se uma transação possui o mínimo de prestações permitidas :param transaction: Instância de um objeto dataclass com representando uma entidade "transaction" :return: Opcional exception "BusinessException" com uma mensagem padrão de erro. """ if transaction.installments < MINIMUM_INSTALLMENTS_PERMITED: raise BusinessException(_('messages.minimum_installments'))
def load_data( self, file_name: str = f'{get_file_operations_name()}.{get_type_file_loader()}' ) -> Tuple[List[dict], List[dict]]: """ Objetivo do método é efetuar iteração sobre as linhas do arquivo de importação, criando objeto conforme o modelo definido pelos fields :param file_name: Caminho do arquivo :return: Lista de dicionários representando o modelo definido pelos fields """ record_success = [] record_errors = [] file_path = f'{get_read_operations_path()}/{file_name}' with open(file_path, 'r') as file_data: lines = file_data.readlines() for line in lines: try: data = json.loads(line)['transaction'] for field, value in zip(self._fields.keys(), data.values()): data[field] = self._fields[field]( value) if value is not None else None data = self.get_default_fields(data) record_success.append(data) except ValueError as ex: self.__logger.error(_('logging.line_processed_with_error', line=line), exc_info=True) record_errors.append({ 'line_error': line, 'validations': [_('messages.line_processed_with_error', line=line)], }) return record_success, record_errors
def test_do_transaction_when_has_low_score_then_then_raise_exception(self): # arrange self.transaction.score = 100 self.transaction.income = 20000 # act / assert with self.assertRaises(BusinessException) as context: self._transaction_service.do_transaction(self.transaction) self.assertEqual(_('messages.low_score'), str(context.exception))
def __before_do_transaction(self, payload: dict) -> Optional[Transaction]: """ Metodo responsável por validar se o payload de uma transação não possui alguma inconsistência a nivel de contrato. :param payload: contrato de uma transação :return: Retorna uma transação no formato de instância de uma dataclass transaction quando o payload não possui erros. Caso contrário retorna None e adiciona o payload na lista de transações não autorizadas. """ loaded = self._transaction_schema.load(payload, many=False) if loaded.errors: payload_error = self.__build_payload_violation_error( payload, _('messages.payload_error')) self.__unauthorized_transactions.append(payload_error) self._logger.error(_('logging.payload_error', payload=payload)) return return loaded.data
def test_do_transaction_when_installments_value_excedeed_minimum_income_value_then_raise_exception( self): # arrange self.transaction.income = 600 # act / assert with self.assertRaises(BusinessException) as context: self._transaction_service.do_transaction(self.transaction) self.assertEqual(_('messages.compromised_income'), str(context.exception))
def __validate_committed_transaction_income( transaction: Transaction) -> Optional: """ Método responsável por validar se o valor da parcela de uma operação de crédito compromete mais que 30% do cliente requerinte do parcelamento :param transaction: :return: Opcional exception "BusinessException" com uma mensagem padrão de erro. """ minimum_income_committed = transaction.income * MINIMUM_COMMITED_VALUE_PERCENTAGE if transaction.installment_value > minimum_income_committed: raise BusinessException(_('messages.compromised_income'))
def test_do_transaction_when_transaction_installments_low_then_minimum_then_raise_exception( self): # arrange self.transaction.score = 900 self.transaction.installments = 2 self.transaction.requested_value = 1000 self.transaction.income = 30000 # act / assert with self.assertRaises(BusinessException) as context: self._transaction_service.do_transaction(self.transaction) self.assertEqual(_('messages.minimum_installments'), str(context.exception))
def __validate_transaction_time(self, transaction: Transaction) -> Optional: """ Método responsável por efetuar validações paralelas com transações já efetuadas anteriormente efetuando validações em cima de tempo e dia e se já foi processado ao menos uma transação. :param transaction: Opcional exception "BusinessException" com uma mensagem padrão de erro. :return: """ if not self.__last_transaction_approved: return if not self.__is_same_date_transaction(transaction.time): return if self.__is_transaction_interval_less_than_the_permitted( transaction.time): raise BusinessException(_('messages.doubled_transactions'))
def __prepare_unauthorized_transaction( self, transaction: Transaction, message_exception: str = None) -> None: """ Método responsável por preparar e adicionar uma transação não autorizada com um motivo pelo qual não foi autorizada :param transaction: Instância do objeto dataclass representando uma transação :param message_exception: mensagme pelo qual a transação não foi autorizada :return: Não possui, apenas adiciona a transação a lista de transações não autorizadas. """ data = self._transaction_schema.dump(transaction).data payload_error = self.__build_payload_violation_error( data, message_exception) self.__unauthorized_transactions.append(payload_error) self._logger.error( _('logging.transaction_unauthorized', transaction=transaction, motive=message_exception))
def test_do_transaction_when_transaction_interval_is_less_than_permitted_then_raise_exception( self): # arrange self.transaction_aux = self._transaction_fake.get_fake_transaction() self.transaction_aux.time += timedelta(hours=0, minutes=0, seconds=0) self.transaction.score = 900 self.transaction.installments = 7 self.transaction.income = 30000 self.transaction.time = self.transaction_aux.time self.transaction.time += timedelta(minutes=1, seconds=57) self._transaction_service._TransactionService__last_transaction_approved = self.transaction_aux # act / assert with self.assertRaises(BusinessException) as context: self._transaction_service.do_transaction(self.transaction) self.assertEqual(_('messages.doubled_transactions'), str(context.exception))
def run(): configure_logging() logger = logging.getLogger(__name__) logger.info(_('logging.app_starting')) program_interface = inject.instance(ProgramInterface) transaction_facade = inject.instance(TransactionFacade) authorized_transaction_file_builder = inject.instance( AuthorizedTransactionFileBuilder) option = program_interface.get_options() if option == STOP_INTERFACE: exit() success_loaded_operations, error_loaded_operations = program_interface.load_operations_from_file( ) for transaction in success_loaded_operations: transaction_facade.authorize_transaction(transaction) authorized_transaction_file_builder.write_authorized_transactions()
def authorize_transaction(self, payload: dict) -> Optional[dict]: """ Autoriza uma transação efetuando algumas validações permitentes ao negócio verificando se o payload enviado não possui alguma inconsistência :param payload: Contrato para efetuar uma transação :return: Retorna a transação no formato de dicionário caso tenha ocorrido sem errors. Caso contrário registra a transação na lista de transações não autorizadas. """ transaction = self.__before_do_transaction(payload) if not transaction: return try: self._transaction_service.do_transaction(transaction) self._logger.info( _('logging.transaction_authorired', transaction=transaction)) return self._transaction_schema.dump(transaction) except BusinessException as ex: self.__prepare_unauthorized_transaction(transaction, str(ex))
def test_authorize_transaction_with_integrated_test_then_validate_mock_result_file( self, json_method_mock): # arrange first_transaction_index = 0 second_transaction_index = 1 third_transaction_index = 2 four_transaction_index = 3 first_violation_index = 0 record_data_success, record_data_error = self._file_loader.load_data() # act with mock.patch('builtins.open', mock.mock_open()) as context: file = context() for payload in record_data_success: self._transaction_facade.authorize_transaction(payload) self._authorized_transaction_file_builder.write_authorized_transactions( ) unauthorized_transactions = self._transaction_facade.unauthorized_transactions( ) # assert self.assertEqual(len(unauthorized_transactions), 4) self.assertEqual( unauthorized_transactions[first_transaction_index]['violations'] [first_violation_index], _('messages.low_score')) self.assertEqual( unauthorized_transactions[second_transaction_index]['violations'] [first_violation_index], _('messages.compromised_income')) self.assertEqual( unauthorized_transactions[third_transaction_index]['violations'] [first_violation_index], _('messages.minimum_installments')) self.assertEqual( unauthorized_transactions[four_transaction_index]['violations'] [first_violation_index], _('messages.doubled_transactions')) self.assertEqual( json_method_mock.dumps.call_args_list[first_transaction_index], mock.call(unauthorized_transactions[first_transaction_index])) self.assertEqual( json_method_mock.dumps.call_args_list[second_transaction_index], mock.call(unauthorized_transactions[second_transaction_index])) self.assertEqual( json_method_mock.dumps.call_args_list[third_transaction_index], mock.call(unauthorized_transactions[third_transaction_index])) self.assertEqual( json_method_mock.dumps.call_args_list[four_transaction_index], mock.call(unauthorized_transactions[four_transaction_index])) file.write.assert_called()