def test_send_welcome_sms(mocker, test_client, init_database, mock_sms_apis, preferred_language, org_key, expected_welcome, expected_terms, phone): from flask import g from server import db token = TokenFactory(name='Sarafu', symbol='Sarafu') organisation = OrganisationFactory(custom_welcome_message_key=org_key, token=token, country_code='AU') g.active_organisation = organisation transfer_account = TransferAccountFactory(token=token, organisation=organisation) transfer_account.set_balance_offset(10000) user = UserFactory(first_name='Magoo', phone=phone, preferred_language=preferred_language, organisations=[organisation], default_organisation=organisation, transfer_accounts=[transfer_account]) send_onboarding_sms_messages(user) messages = mock_sms_apis assert messages == [ { 'phone': f'+61{phone}', 'message': expected_welcome }, { 'phone': f'+61{phone}', 'message': expected_terms }, ]
def test_kenya_state_machine(test_client, init_database, user_factory, session_factory, user_input, expected): from flask import g g.active_organisation = OrganisationFactory(country_code='AU') session = session_factory() user = user_factory() user.phone = phone() state_machine = KenyaUssdStateMachine(session, user) state_machine.feed_char(user_input) assert state_machine.state == expected
def standard_user(test_client, init_database): from flask import g token = TokenFactory(name='Sarafu', symbol='Sarafu') organisation = OrganisationFactory(token=token, country_code='AU') g.active_organisation = organisation return UserFactory(first_name="Foo", last_name="Bar", pin_hash=User.salt_hash_secret('0000'), failed_pin_attempts=0, phone=phone(), default_organisation=organisation)
def test_exchange_token(mocker, test_client, init_database, initialised_blockchain_network, mock_sms_apis): org = OrganisationFactory(country_code='KE') sender = UserFactory(preferred_language="en", phone=phone(), first_name="Bob", last_name="Foo", default_organisation=org) sender.set_held_role('GROUP_ACCOUNT', 'group_account') token1 = Token.query.filter_by(symbol="SM1").first() create_transfer_account_for_user(sender, token1, 20000) agent = UserFactory(phone=phone(), first_name="Joe", last_name="Bar", default_organisation=org) agent.set_held_role('TOKEN_AGENT', 'token_agent') # this is under the assumption that token agent would have default token being the reserve token. is this the case? reserve = Token.query.filter_by(symbol="AUD").first() create_transfer_account_for_user(agent, reserve, 30000) def mock_convert(exchange_contract, from_token, to_token, from_amount, signing_address): return from_amount * 1.2 mocker.patch('server.bt.get_conversion_amount', mock_convert) def mock_validate(self, transfer): pass mocker.patch( 'server.utils.transfer_limits.MinimumSentLimit.validate_transfer', mock_validate) TokenProcessor.exchange_token(sender, agent, 1000) assert default_transfer_account(sender).balance == 19000 assert default_transfer_account(agent).balance == 31200 messages = mock_sms_apis assert len(messages) == 2 sent_message = messages[0] assert sent_message['phone'] == sender.phone assert 'sent a payment of 10.00 SM1 = 12.00 AUD' in sent_message['message'] received_message = messages[1] assert received_message['phone'] == agent.phone assert 'received a payment of 12.00 AUD = 10.00 SM1' in received_message[ 'message']
def test_kenya_state_machine(test_client, init_database, user_factory, session_factory, user_input, expected): from flask import g g.active_organisation = OrganisationFactory(country_code='AU') session = session_factory() session.session_data = { 'transfer_usage_mapping': fake_transfer_mapping(10), 'usage_menu': 1, 'usage_index_stack': [0, 8] } user = user_factory() user.phone = phone() db.session.commit() state_machine = KenyaUssdStateMachine(session, user) state_machine.feed_char(user_input) assert state_machine.state == expected
def test_send_token(mocker, test_client, init_database, initialised_blockchain_network, lang, mock_sms_apis, token1_symbol, token2_symbol, recipient_balance, expected_send_msg, expected_receive_msg): org = OrganisationFactory(country_code='KE') sender = UserFactory(preferred_language=lang, phone=phone(), first_name="Bob", last_name="Foo", default_organisation=org) token1 = Token.query.filter_by(symbol=token1_symbol).first() create_transfer_account_for_user(sender, token1, 20000) recipient = UserFactory(preferred_language=lang, phone=phone(), first_name="Joe", last_name="Bar", default_organisation=org) token2 = Token.query.filter_by(symbol=token2_symbol).first() create_transfer_account_for_user(recipient, token2, 30000) def mock_convert(exchange_contract, from_token, to_token, from_amount, signing_address): if from_token.symbol == "SM1": return from_amount * 1.5 else: return from_amount * 0.75 mocker.patch('server.bt.get_conversion_amount', mock_convert) TransferUsage.find_or_create("Alf DVDs") TokenProcessor.send_token(sender, recipient, 1000, "1", 1) assert default_transfer_account(sender).balance == 19000 assert default_transfer_account(recipient).balance == recipient_balance messages = mock_sms_apis assert len(messages) == 2 sent_message = messages[0] assert sent_message['phone'] == sender.phone assert expected_send_msg in sent_message['message'] received_message = messages[1] assert received_message['phone'] == recipient.phone assert expected_receive_msg in received_message['message']
def test_create_or_update_session(test_client, init_database): from flask import g g.active_organisation = OrganisationFactory(country_code='AU') user = UserFactory(phone="123") # create a session in db menu3 = UssdMenu(id=3, name='foo', display_key='foo') db.session.add(menu3) session = UssdSession(session_id="1", user_id=user.id, msisdn="123", ussd_menu=menu3, state="foo", service_code="*123#") db.session.add(session) db.session.commit() # test updating existing create_or_update_session("1", user, UssdMenu(id=4, name="bar", display_key='bar'), "input", "*123#") sessions = UssdSession.query.filter_by(session_id="1") assert sessions.count() == 1 session = sessions.first() assert session.state == "bar" assert session.user_input == "input" assert session.ussd_menu_id == 4 # test creating a new one sessions = UssdSession.query.filter_by(session_id="2") assert sessions.count() == 0 create_or_update_session("2", user, UssdMenu(id=5, name="bat", display_key='bat'), "", "*123#") sessions = UssdSession.query.filter_by(session_id="2") assert sessions.count() == 1 session = sessions.first() assert session.state == "bat" assert session.user_input == "" assert session.ussd_menu_id == 5
def test_prior_task_requirements(test_client, init_database): """ Test whether the calculated submission order for a set of transactions is what we expect it to be ie one that prevents an unrecoverable state from being reached """ token = TokenFactory(name='Sarafu', symbol='Sarafu') organisation = OrganisationFactory(token=token, country_code='AU') g.active_organisation = organisation ta1 = TransferAccountFactory(token=token, organisation=organisation) ta2 = TransferAccountFactory(token=token, organisation=organisation) ta3 = TransferAccountFactory(token=token, organisation=organisation) # First start with a plain transfer from ta1 to ta2 send1 = CreditTransferFactory(amount=1000, sender_transfer_account=ta1, recipient_transfer_account=ta2, require_sufficient_balance=False) send1.transfer_status = TransferStatusEnum.COMPLETE # Make sure that get prior tasks doesn't pick up its assert send1._get_required_prior_tasks() == set() init_database.session.commit() # Test with and without commit assert send1._get_required_prior_tasks() == set() # Same sender/recipient as original. send2 = CreditTransferFactory(amount=1000, sender_transfer_account=ta1, recipient_transfer_account=ta2, require_sufficient_balance=False) # Should pick up prior send. assert send2._get_required_prior_tasks() == {send1} # send1's batch shouldn't make a difference yet send1.batch_uuid = u1 assert send2._get_required_prior_tasks() == {send1} # different batch ID shouldn't make a difference either send2.batch_uuid = u2 assert send2._get_required_prior_tasks() == {send1} # Now send1 shouldn't be included as a prior requirement, because it's the same batch send2.batch_uuid = u1 assert send2._get_required_prior_tasks() == set() # Now ta1 is receiving receive1 = CreditTransferFactory(amount=1000, sender_transfer_account=ta3, recipient_transfer_account=ta1, require_sufficient_balance=False) receive2 = CreditTransferFactory(amount=1000, sender_transfer_account=ta3, recipient_transfer_account=ta1, require_sufficient_balance=False) send3 = CreditTransferFactory(amount=1000, sender_transfer_account=ta1, recipient_transfer_account=ta2, require_sufficient_balance=False) # Should just be send1, as send2, receive1 and receive2 aren't resolved as complete yet assert send3._get_required_prior_tasks() == {send1} send2.batch_uuid = None send2.transfer_status = TransferStatusEnum.COMPLETE receive1.transfer_status = TransferStatusEnum.COMPLETE receive2.transfer_status = TransferStatusEnum.COMPLETE # Now send1 should be excluded because the dependency is implicit from send2 assert send3._get_required_prior_tasks() == {receive2, receive1, send2} send3.transfer_status = TransferStatusEnum.COMPLETE assert send3._get_required_prior_tasks() == {receive2, receive1, send2} send4 = CreditTransferFactory(amount=1000, sender_transfer_account=ta1, recipient_transfer_account=ta2, require_sufficient_balance=False) send5 = CreditTransferFactory(amount=1000, sender_transfer_account=ta1, recipient_transfer_account=ta2, require_sufficient_balance=False) send3.batch_uuid = u3 send4.batch_uuid = u3 send5.batch_uuid = u3 assert send3._get_required_prior_tasks() == {receive2, receive1, send2} # big batch assert send5._get_required_prior_tasks() == {receive2, receive1, send2} send3.transfer_status = TransferStatusEnum.COMPLETE send4.transfer_status = TransferStatusEnum.COMPLETE send5.transfer_status = TransferStatusEnum.COMPLETE send6 = CreditTransferFactory(amount=1000, sender_transfer_account=ta1, recipient_transfer_account=ta2, require_sufficient_balance=False) assert send6._get_required_prior_tasks() == {send3, send4, send5}
def test_golden_path_send_token(mocker, test_client, init_database, initialised_blockchain_network, init_seed): token = Token.query.filter_by(symbol="SM1").first() org = OrganisationFactory(country_code='KE') sender = UserFactory(preferred_language="en", phone=make_kenyan_phone(phone()), first_name="Bob", last_name="Foo", pin_hash=User.salt_hash_secret('0000'), default_organisation=org) create_transfer_account_for_user(sender, token, 4220) recipient = UserFactory(preferred_language="sw", phone=make_kenyan_phone(phone()), first_name="Joe", last_name="Bar", default_organisation=org) create_transfer_account_for_user(recipient, token, 1980) messages = [] session_id = 'ATUid_05af06225e6163ec2dc9dc9cf8bc97aa' usages = TransferUsage.query.filter_by(default=True).order_by( TransferUsage.priority).all() top_priority = usages[0] # Take the last to ensure that we're not going to simply reinforce the existing order usage = usages[-1] # do two of these transfers to ensure last is is the first shown make_payment_transfer(100, token=token, send_user=sender, receive_user=recipient, transfer_use=str(int(usage.id)), is_ghost_transfer=False, require_sender_approved=False, require_recipient_approved=False) make_payment_transfer(100, token=token, send_user=sender, receive_user=recipient, transfer_use=str(int(usage.id)), is_ghost_transfer=False, require_sender_approved=False, require_recipient_approved=False) def mock_send_message(phone, message): messages.append({'phone': phone, 'message': message}) mocker.patch(f'server.utils.phone._send_twilio_message.submit', mock_send_message) mocker.patch(f'server.utils.phone._send_messagebird_message.submit', mock_send_message) mocker.patch(f'server.utils.phone._send_at_message.submit', mock_send_message) def req(text): response = test_client.post( f'/api/v1/ussd/kenya?username={config.EXTERNAL_AUTH_USERNAME}&password={config.EXTERNAL_AUTH_PASSWORD}', headers=dict(Accept='application/json'), json={ 'sessionId': session_id, 'phoneNumber': sender.phone, 'text': text, 'serviceCode': '*384*23216#' }) assert response.status_code == 200 return response.data.decode("utf-8") def get_session(): return UssdSession.query.filter_by(session_id=session_id).first() assert get_session() is None resp = req("") assert get_session() is not None assert "CON Welcome" in resp resp = req("1") assert "CON Enter Phone" in resp resp = req(recipient.phone) assert "CON Enter Amount" in resp resp = req("12.5") assert "CON Transfer Reason" in resp assert f"1. {top_priority.translations['en']}" in resp assert "9." in resp resp = req("9") assert "CON Please specify" in resp assert "10. Show previous" in resp assert "9." not in resp resp = req("10") resp = req("4") assert "CON Please enter your PIN" in resp resp = req("0000") assert "CON Send 12.5 SM1" in resp # went to second page, should not be the first assert f"for {top_priority.translations['en']}" not in resp resp = req("1") assert "END Your request has been sent." in resp assert default_transfer_account(sender).balance == (4220 - 100 - 100 - 1250) assert default_transfer_account(recipient).balance == (1980 + 100 + 100 + 1250) assert len(messages) == 3 sent_message = messages[1] assert sent_message['phone'] == sender.phone assert f"sent a payment of 12.50 SM1 to {recipient.first_name}" in sent_message[ 'message'] received_message = messages[2] assert received_message['phone'] == recipient.phone assert f"Umepokea 12.50 SM1 kutoka kwa {sender.first_name}" in received_message[ 'message']