def handle_CreateAccount(self, command): ''' Handles CreateAccount command Keyword Arguments: command -- Command entity with necessary account creation data Returns: An array containing violations ocurred during execution ''' assert isinstance( command, CreateAccount), '{} must be of type \'CreateAccount\''.format( command) violations = [] account = command.accountToCreate existingAccounts = self.accountRepository.getByFilter( lambda account: True) if existingAccounts: violations += ['account-already-initialized'] return Account.getAccountAndViolationsDict(existingAccounts[0], violations) self.accountRepository.add(account) returnDict = Account.getAccountAndViolationsDict(account, violations) return returnDict
def test_getAccountAndViolationsDict_empty(self): account = Account(True, 100) violations = [] expectedDict = { "account": { "activeCard": True, "availableLimit": 100 }, "violations": [] } result = Account.getAccountAndViolationsDict(account, violations) self.assertDictEqual(expectedDict, result)
def test_getAccountAndViolationsDict_Valid(self): account = Account(True, 99) violations = ['insufficient-limit'] expectedDict = { "account": { "activeCard": True, "availableLimit": 99 }, "violations": ['insufficient-limit'] } result = Account.getAccountAndViolationsDict(account, violations) self.assertDictEqual(expectedDict, result)
def test_JsonParseAccount_ok(self): jsonStr = '{ "account": { "activeCard": true, "availableLimit": 100 } }' expected = Account(True, 100) result = CreateAccount.parseAccountCreation(jsonStr) self.assertDictEqual(expected.toDict(), result.toDict())
def test_CreateAccountCtor_ok(self): jsonStr = '{ "account": { "activeCard": true, "availableLimit": 100 } }' expected = Account(True, 100) result = CreateAccount(jsonStr) self.assertDictEqual(expected.toDict(), result.accountToCreate.toDict())
def test_handleCreateAccount_ok(self): accountRepository = InMemoryRepository() transactionRepository = InMemoryRepository() commandHandler = TransactionCommandHandler(transactionRepository, accountRepository) accountRepository.add(Account(True, 100)) command = AuthorizeTransaction( '{ "transaction": { "merchant": "Burger King", "amount": 10, "time": "2019-02-13T10:00:00.000Z" } }' ) expectedResult = { "account": { "activeCard": True, "availableLimit": 90 }, "violations": [] } expectedAccount = { "account": { "activeCard": True, "availableLimit": 90 } } result = commandHandler.handle_AuthorizeTransaction(command) self.assertEqual(expectedResult, result) resultAccount = accountRepository.getByFilter( lambda _: True)[0].toDict() self.assertDictEqual(resultAccount, expectedAccount)
def test_handleCreateAccount_DoubledTransaction(self): accountRepository = InMemoryRepository() transactionRepository = InMemoryRepository() commandHandler = TransactionCommandHandler(transactionRepository, accountRepository) accountRepository.add(Account(True, 75)) transactionRepository.add( Transaction( "Burger King", 10, datetime.strptime("2019-02-13T11:00:00.000Z", '%Y-%m-%dT%H:%M:%S.%fZ'))) transactionRepository.add( Transaction( "Burger King", 10, datetime.strptime("2019-02-13T11:01:59.000Z", '%Y-%m-%dT%H:%M:%S.%fZ'))) command = AuthorizeTransaction( '{ "transaction": { "merchant": "Burger King", "amount": 10, "time": "2019-02-13T11:02:00.000Z" } }' ) expectedResult = { "account": { "activeCard": True, "availableLimit": 75 }, "violations": ['doubled-transaction'] } result = commandHandler.handle_AuthorizeTransaction(command) self.assertEqual(expectedResult, result)
def test_handleCreateAccount_InsufficientLimit(self): accountRepository = InMemoryRepository() transactionRepository = InMemoryRepository() commandHandler = TransactionCommandHandler(transactionRepository, accountRepository) accountRepository.add(Account(True, 100)) command = AuthorizeTransaction( '{ "transaction": { "merchant": "Burger King", "amount": 101, "time": "2019-02-13T10:00:00.000Z" } }' ) expectedResult = { "account": { "activeCard": True, "availableLimit": 100 }, "violations": ['insufficient-limit'] } result = commandHandler.handle_AuthorizeTransaction(command) self.assertEqual(expectedResult, result) transactions = transactionRepository.getByFilter(lambda _: True) self.assertListEqual([], transactions)
def parseAccountCreation(line): ''' Parses an AccountCreation command arguments Keyword arguments: line -- JSON operation line containing "activeCard": boolean and "availableLimit": Integer Returned Value: account -- Account object built with the fields from the operation ''' fieldsDict = json.loads(line)['account'] account = Account(fieldsDict['activeCard'], fieldsDict['availableLimit']) return account
def test_handleCreateAccount_HighFrequencySmallInterval(self): accountRepository = InMemoryRepository() transactionRepository = InMemoryRepository() commandHandler = TransactionCommandHandler(transactionRepository, accountRepository) accountRepository.add(Account(True, 75)) transactionRepository.add( Transaction( "Burger King", 10, datetime.strptime("2019-02-13T11:00:00.000Z", '%Y-%m-%dT%H:%M:%S.%fZ'))) transactionRepository.add( Transaction( "Burger Queen", 10, datetime.strptime("2019-02-13T11:01:00.000Z", '%Y-%m-%dT%H:%M:%S.%fZ'))) transactionRepository.add( Transaction( "Burger Prince", 5, datetime.strptime("2019-02-13T11:01:59.000Z", '%Y-%m-%dT%H:%M:%S.%fZ'))) command = AuthorizeTransaction( '{ "transaction": { "merchant": "Burger Pleb", "amount": 10, "time": "2019-02-13T10:00:00.000Z" } }' ) expectedResult = { "account": { "activeCard": True, "availableLimit": 75 }, "violations": ['high-frequency-small-interval'] } result = commandHandler.handle_AuthorizeTransaction(command) self.assertEqual(expectedResult, result)
def test_handleCreateAccount_AlreadyInitialized(self): repository = InMemoryRepository() repository.add(Account(True, 42)) commandHandler = AccountCommandHandler(repository) command = CreateAccount( '{ "account": { "activeCard": true, "availableLimit": 100 } }') expectedResult = { "account": { "activeCard": True, "availableLimit": 42 }, "violations": ["account-already-initialized"] } result = commandHandler.handle_CreateAccount(command) self.assertDictEqual(expectedResult, result) emptyResults = repository.getByFilter( lambda account: account.availableLimit == 100) self.assertListEqual([], emptyResults)
def handle_AuthorizeTransaction(self, command): ''' Handles AuthorizeTransaction command Keyword Arguments: command -- Command entity with necessary transaction authorization data Returns: A dictionary containing the account and a new field containing possible violations ''' assert isinstance( command, AuthorizeTransaction ), '{} must be of type \'AuthorizeTransaction\''.format(command) transaction = command.transactionToCreate violations = [] # Gets the first account account = self.accountRepository.getByFilter(lambda account: True)[0] # • No transaction should be accepted when the card is not active: card-not-active if not account.activeCard: violations += ['card-not-active'] return Account.getAccountAndViolationsDict(account, violations) # • The transaction amount should not exceed available limit: insufficient-limit if account.availableLimit < transaction.amount: violations += ['insufficient-limit'] return Account.getAccountAndViolationsDict(account, violations) # • There should not be more than 3 transactions on a 2 minute interval: high-frequency-small-interval transactionTime = datetime.strptime(transaction.time, '%Y-%m-%dT%H:%M:%S.%fZ') minDiffInMinutes = 2 fromDatetime = transactionTime - timedelta(minutes=minDiffInMinutes) recentTransactions = self.transactionRepository.getByFilter( lambda transactions: transactions.time >= fromDatetime) if len(recentTransactions) >= 3: violations += ['high-frequency-small-interval'] return Account.getAccountAndViolationsDict(account, violations) # • There should not be more than 2 similar transactions (same amount and merchant) in a 2 minutes interval: doubled-transaction doubledTransactions = list(filter(lambda transactions: \ transactions.amount == transaction.amount \ and transactions.merchant == transaction.merchant \ , recentTransactions)) if doubledTransactions and len(doubledTransactions) > 1: violations += ['doubled-transaction'] return Account.getAccountAndViolationsDict(account, violations) # Everything should be fine down here transaction.time = transactionTime self.transactionRepository.add(transaction) account.availableLimit -= transaction.amount self.accountRepository.update(account) return Account.getAccountAndViolationsDict(account, violations)