The great bump
This commit is contained in:
68
apps/cic-ussd/tests/cic_ussd/account/test_balance.py
Normal file
68
apps/cic-ussd/tests/cic_ussd/account/test_balance.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# standard imports
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.balance import calculate_available_balance, get_balances, get_cached_available_balance
|
||||
from cic_ussd.account.chain import Chain
|
||||
from cic_ussd.error import CachedDataNotFoundError
|
||||
|
||||
# test imports
|
||||
from tests.helpers.accounts import blockchain_address
|
||||
|
||||
|
||||
def test_async_get_balances(activated_account,
|
||||
celery_session_worker,
|
||||
load_chain_spec,
|
||||
load_config,
|
||||
mock_async_balance_api_query):
|
||||
blockchain_address = activated_account.blockchain_address
|
||||
chain_str = Chain.spec.__str__()
|
||||
token_symbol = load_config.get('TEST_TOKEN_SYMBOL')
|
||||
get_balances(blockchain_address, chain_str, token_symbol, asynchronous=True)
|
||||
assert mock_async_balance_api_query.get('address') == blockchain_address
|
||||
assert mock_async_balance_api_query.get('token_symbol') == token_symbol
|
||||
|
||||
|
||||
def test_sync_get_balances(activated_account,
|
||||
balances,
|
||||
celery_session_worker,
|
||||
load_chain_spec,
|
||||
load_config,
|
||||
mock_sync_balance_api_query):
|
||||
blockchain_address = activated_account.blockchain_address
|
||||
chain_str = Chain.spec.__str__()
|
||||
token_symbol = load_config.get('TEST_TOKEN_SYMBOL')
|
||||
res = get_balances(blockchain_address, chain_str, token_symbol, asynchronous=False)
|
||||
assert res == balances
|
||||
|
||||
|
||||
@pytest.mark.parametrize('balance_incoming, balance_network, balance_outgoing, available_balance', [
|
||||
(0, 50000000, 0, 50.00),
|
||||
(5000000, 89000000, 67000000, 27.00)
|
||||
])
|
||||
def test_calculate_available_balance(activated_account,
|
||||
balance_incoming,
|
||||
balance_network,
|
||||
balance_outgoing,
|
||||
available_balance):
|
||||
balances = {
|
||||
'address': activated_account.blockchain_address,
|
||||
'converters': [],
|
||||
'balance_network': balance_network,
|
||||
'balance_outgoing': balance_outgoing,
|
||||
'balance_incoming': balance_incoming
|
||||
}
|
||||
assert calculate_available_balance(balances) == available_balance
|
||||
|
||||
|
||||
def test_get_cached_available_balance(activated_account, cache_balances, balances):
|
||||
cached_available_balance = get_cached_available_balance(activated_account.blockchain_address)
|
||||
available_balance = calculate_available_balance(balances[0])
|
||||
assert cached_available_balance == available_balance
|
||||
address = blockchain_address()
|
||||
with pytest.raises(CachedDataNotFoundError) as error:
|
||||
cached_available_balance = get_cached_available_balance(address)
|
||||
assert cached_available_balance is None
|
||||
assert str(error.value) == f'No cached available balance for address: {address}'
|
||||
28
apps/cic-ussd/tests/cic_ussd/account/test_maps.py
Normal file
28
apps/cic-ussd/tests/cic_ussd/account/test_maps.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# standard imports
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.maps import gender, language
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
@pytest.mark.parametrize('key, expected_value', [
|
||||
('1', 'male'),
|
||||
('2', 'female'),
|
||||
('3', 'other')
|
||||
])
|
||||
def test_gender(key, expected_value):
|
||||
g_map = gender()
|
||||
assert g_map[key] == expected_value
|
||||
|
||||
|
||||
@pytest.mark.parametrize('key, expected_value', [
|
||||
('1', 'en'),
|
||||
('2', 'sw'),
|
||||
])
|
||||
def test_language(key, expected_value):
|
||||
l_map = language()
|
||||
assert l_map[key] == expected_value
|
||||
28
apps/cic-ussd/tests/cic_ussd/account/test_metadata.py
Normal file
28
apps/cic-ussd/tests/cic_ussd/account/test_metadata.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# external imports
|
||||
from cic_types.models.person import get_contact_data_from_vcard
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.metadata import get_cached_preferred_language, parse_account_metadata
|
||||
|
||||
# test imports
|
||||
from tests.helpers.accounts import blockchain_address
|
||||
|
||||
|
||||
def test_get_cached_preferred_language(activated_account, cache_preferences, preferences):
|
||||
cached_preferred_language = get_cached_preferred_language(activated_account.blockchain_address)
|
||||
assert cached_preferred_language == preferences.get('preferred_language')
|
||||
cached_preferred_language = get_cached_preferred_language(blockchain_address())
|
||||
assert cached_preferred_language is None
|
||||
|
||||
|
||||
def test_parse_account_metadata(person_metadata):
|
||||
contact_information = get_contact_data_from_vcard(person_metadata.get('vcard'))
|
||||
given_name = contact_information.get('given')
|
||||
family_name = contact_information.get('family')
|
||||
phone_number = contact_information.get('tel')
|
||||
parsed_account_metadata = f'{given_name} {family_name} {phone_number}'
|
||||
assert parse_account_metadata(person_metadata) == parsed_account_metadata
|
||||
|
||||
85
apps/cic-ussd/tests/cic_ussd/account/test_statement.py
Normal file
85
apps/cic-ussd/tests/cic_ussd/account/test_statement.py
Normal file
@@ -0,0 +1,85 @@
|
||||
# standard imports
|
||||
import json
|
||||
import time
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
import requests_mock
|
||||
from chainlib.hash import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.statement import (filter_statement_transactions,
|
||||
generate,
|
||||
get_cached_statement,
|
||||
parse_statement_transactions,
|
||||
query_statement,
|
||||
statement_transaction_set)
|
||||
from cic_ussd.account.transaction import transaction_actors
|
||||
from cic_ussd.cache import cache_data_key, get_cached_data
|
||||
|
||||
# test imports
|
||||
from tests.helpers.accounts import blockchain_address
|
||||
|
||||
|
||||
def test_filter_statement_transactions(transactions_list):
|
||||
assert len(transactions_list) == 4
|
||||
assert len(filter_statement_transactions(transactions_list)) == 1
|
||||
|
||||
|
||||
def test_generate(activated_account,
|
||||
cache_preferences,
|
||||
celery_session_worker,
|
||||
init_cache,
|
||||
init_database,
|
||||
set_locale_files,
|
||||
preferences,
|
||||
preferences_metadata_url,
|
||||
transactions_list):
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri('GET', preferences_metadata_url, status_code=200, reason='OK', json=preferences)
|
||||
statement_transactions = filter_statement_transactions(transactions_list)
|
||||
for transaction in statement_transactions:
|
||||
querying_party = activated_account.blockchain_address
|
||||
recipient_transaction, sender_transaction = transaction_actors(transaction)
|
||||
if recipient_transaction.get('blockchain_address') == querying_party:
|
||||
generate(querying_party, None, recipient_transaction)
|
||||
if sender_transaction.get('blockchain_address') == querying_party:
|
||||
generate(querying_party, None, sender_transaction)
|
||||
time.sleep(2)
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
key = cache_data_key(identifier, ':cic.statement')
|
||||
statement = get_cached_data(key)
|
||||
statement = json.loads(statement)
|
||||
assert len(statement) == 1
|
||||
|
||||
|
||||
def test_get_cached_statement(activated_account, cache_statement, statement):
|
||||
cached_statement = get_cached_statement(activated_account.blockchain_address)
|
||||
assert cached_statement is not None
|
||||
cached_statement = json.loads(cached_statement)
|
||||
assert cached_statement[0].get('blockchain_address') == statement[0].get('blockchain_address')
|
||||
|
||||
|
||||
def test_parse_statement_transactions(statement):
|
||||
parsed_transactions = parse_statement_transactions(statement)
|
||||
parsed_transaction = parsed_transactions[0]
|
||||
parsed_transaction.startswith('Sent')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('blockchain_address, limit', [
|
||||
(blockchain_address(), 10),
|
||||
(blockchain_address(), 5)
|
||||
])
|
||||
def test_query_statement(blockchain_address, limit, load_chain_spec, activated_account, mock_transaction_list_query):
|
||||
query_statement(blockchain_address, limit)
|
||||
assert mock_transaction_list_query.get('address') == blockchain_address
|
||||
assert mock_transaction_list_query.get('limit') == limit
|
||||
|
||||
|
||||
def test_statement_transaction_set(preferences, set_locale_files, statement):
|
||||
parsed_transactions = parse_statement_transactions(statement)
|
||||
preferred_language = preferences.get('preferred_language')
|
||||
transaction_set = statement_transaction_set(preferred_language, parsed_transactions)
|
||||
transaction_set.startswith('Sent')
|
||||
transaction_set = statement_transaction_set(preferred_language, [])
|
||||
transaction_set.startswith('No')
|
||||
42
apps/cic-ussd/tests/cic_ussd/account/test_tokens.py
Normal file
42
apps/cic-ussd/tests/cic_ussd/account/test_tokens.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.chain import Chain
|
||||
from cic_ussd.account.tokens import get_cached_default_token, get_default_token_symbol, query_default_token
|
||||
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def test_get_cached_default_token(cache_default_token_data, default_token_data, load_chain_spec):
|
||||
chain_str = Chain.spec.__str__()
|
||||
cached_default_token = get_cached_default_token(chain_str)
|
||||
cached_default_token_data = json.loads(cached_default_token)
|
||||
assert cached_default_token_data['symbol'] == default_token_data['symbol']
|
||||
assert cached_default_token_data['address'] == default_token_data['address']
|
||||
assert cached_default_token_data['name'] == default_token_data['name']
|
||||
assert cached_default_token_data['decimals'] == default_token_data['decimals']
|
||||
|
||||
|
||||
def test_get_default_token_symbol_from_api(default_token_data, load_chain_spec, mock_sync_default_token_api_query):
|
||||
default_token_symbol = get_default_token_symbol()
|
||||
assert default_token_symbol == default_token_data['symbol']
|
||||
|
||||
|
||||
def test_query_default_token(default_token_data, load_chain_spec, mock_sync_default_token_api_query):
|
||||
chain_str = Chain.spec.__str__()
|
||||
queried_default_token_data = query_default_token(chain_str)
|
||||
assert queried_default_token_data['symbol'] == default_token_data['symbol']
|
||||
assert queried_default_token_data['address'] == default_token_data['address']
|
||||
assert queried_default_token_data['name'] == default_token_data['name']
|
||||
assert queried_default_token_data['decimals'] == default_token_data['decimals']
|
||||
|
||||
|
||||
def test_get_default_token_symbol_from_cache(cache_default_token_data, default_token_data, load_chain_spec):
|
||||
default_token_symbol = get_default_token_symbol()
|
||||
assert default_token_symbol is not None
|
||||
assert default_token_symbol == default_token_data.get('symbol')
|
||||
110
apps/cic-ussd/tests/cic_ussd/account/test_transaction.py
Normal file
110
apps/cic-ussd/tests/cic_ussd/account/test_transaction.py
Normal file
@@ -0,0 +1,110 @@
|
||||
# standard imports
|
||||
from decimal import Decimal
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.chain import Chain
|
||||
from cic_ussd.account.transaction import (aux_transaction_data,
|
||||
from_wei,
|
||||
to_wei,
|
||||
truncate,
|
||||
transaction_actors,
|
||||
validate_transaction_account,
|
||||
OutgoingTransaction)
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.error import UnknownUssdRecipient
|
||||
from cic_ussd.translation import translation_for
|
||||
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def check_aux_data(action_tag_key, direction_tag_key, preferred_language, transaction_with_aux_data):
|
||||
assert transaction_with_aux_data.get('action_tag') == translation_for(action_tag_key, preferred_language)
|
||||
assert transaction_with_aux_data.get('direction_tag') == translation_for(direction_tag_key, preferred_language)
|
||||
|
||||
|
||||
def test_aux_transaction_data(preferences, set_locale_files, transactions_list):
|
||||
sample_transaction = transactions_list[0]
|
||||
preferred_language = preferences.get('preferred_language')
|
||||
recipient_transaction, sender_transaction = transaction_actors(sample_transaction)
|
||||
recipient_tx_aux_data = aux_transaction_data(preferred_language, recipient_transaction)
|
||||
check_aux_data('helpers.received', 'helpers.from', preferred_language, recipient_tx_aux_data)
|
||||
sender_tx_aux_data = aux_transaction_data(preferred_language, sender_transaction)
|
||||
check_aux_data('helpers.sent', 'helpers.to', preferred_language, sender_tx_aux_data)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("wei, expected_result", [
|
||||
(50000000, Decimal('50.00')),
|
||||
(100000, Decimal('0.10'))
|
||||
])
|
||||
def test_from_wei(wei, expected_result):
|
||||
assert from_wei(wei) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value, expected_result", [
|
||||
(50, 50000000),
|
||||
(0.10, 100000)
|
||||
])
|
||||
def test_to_wei(value, expected_result):
|
||||
assert to_wei(value) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize("decimals, value, expected_result", [
|
||||
(3, 1234.32875, 1234.328),
|
||||
(2, 98.998, 98.99)
|
||||
])
|
||||
def test_truncate(decimals, value, expected_result):
|
||||
assert truncate(value=value, decimals=decimals).__float__() == expected_result
|
||||
|
||||
|
||||
def test_transaction_actors(activated_account, transaction_result, valid_recipient):
|
||||
recipient_transaction, sender_transaction = transaction_actors(transaction_result)
|
||||
assert recipient_transaction.get('blockchain_address') == valid_recipient.blockchain_address
|
||||
assert sender_transaction.get('blockchain_address') == activated_account.blockchain_address
|
||||
assert recipient_transaction.get('role') == 'recipient'
|
||||
assert sender_transaction.get('role') == 'sender'
|
||||
assert recipient_transaction.get('token_symbol') == transaction_result.get('destination_token_symbol')
|
||||
assert sender_transaction.get('token_symbol') == transaction_result.get('source_token_symbol')
|
||||
assert recipient_transaction.get('token_value') == transaction_result.get('destination_token_value')
|
||||
assert sender_transaction.get('token_value') == transaction_result.get('source_token_value')
|
||||
|
||||
|
||||
def test_validate_transaction_account(activated_account, init_database, transactions_list):
|
||||
sample_transaction = transactions_list[0]
|
||||
recipient_transaction, sender_transaction = transaction_actors(sample_transaction)
|
||||
recipient_account = validate_transaction_account(init_database, recipient_transaction)
|
||||
sender_account = validate_transaction_account(init_database, sender_transaction)
|
||||
assert isinstance(recipient_account, Account)
|
||||
assert isinstance(sender_account, Account)
|
||||
sample_transaction = transactions_list[1]
|
||||
recipient_transaction, sender_transaction = transaction_actors(sample_transaction)
|
||||
with pytest.raises(UnknownUssdRecipient) as error:
|
||||
validate_transaction_account(init_database, recipient_transaction)
|
||||
assert str(
|
||||
error.value) == f'Tx for recipient: {recipient_transaction.get("blockchain_address")} has no matching account in the system.'
|
||||
validate_transaction_account(init_database, sender_transaction)
|
||||
assert f'Tx from sender: {sender_transaction.get("blockchain_address")} has no matching account in system.'
|
||||
|
||||
|
||||
@pytest.mark.parametrize("amount", [50, 0.10])
|
||||
def test_outgoing_transaction_processor(activated_account,
|
||||
amount,
|
||||
celery_session_worker,
|
||||
load_config,
|
||||
load_chain_spec,
|
||||
mock_transfer_api,
|
||||
valid_recipient):
|
||||
chain_str = Chain.spec.__str__()
|
||||
token_symbol = load_config.get('TEST_TOKEN_SYMBOL')
|
||||
outgoing_tx_processor = OutgoingTransaction(chain_str,
|
||||
activated_account.blockchain_address,
|
||||
valid_recipient.blockchain_address)
|
||||
|
||||
outgoing_tx_processor.transfer(amount, token_symbol)
|
||||
assert mock_transfer_api.get('from_address') == activated_account.blockchain_address
|
||||
assert mock_transfer_api.get('to_address') == valid_recipient.blockchain_address
|
||||
assert mock_transfer_api.get('value') == to_wei(amount)
|
||||
assert mock_transfer_api.get('token_symbol') == token_symbol
|
||||
96
apps/cic-ussd/tests/cic_ussd/db/models/test_account.py
Normal file
96
apps/cic-ussd/tests/cic_ussd/db/models/test_account.py
Normal file
@@ -0,0 +1,96 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
from cic_types.models.person import get_contact_data_from_vcard
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.chain import Chain
|
||||
from cic_ussd.cache import get_cached_data
|
||||
from cic_ussd.db.enum import AccountStatus
|
||||
from cic_ussd.db.models.account import Account, create, cache_creation_task_uuid
|
||||
from cic_ussd.db.models.task_tracker import TaskTracker
|
||||
|
||||
# test imports
|
||||
from tests.helpers.accounts import blockchain_address, phone_number
|
||||
|
||||
|
||||
def test_account(init_database, set_fernet_key):
|
||||
address = blockchain_address()
|
||||
phone = phone_number()
|
||||
account = Account(address, phone)
|
||||
account.create_password('0000')
|
||||
account.activate_account()
|
||||
init_database.add(account)
|
||||
init_database.commit()
|
||||
|
||||
account = init_database.query(Account).get(1)
|
||||
assert account.blockchain_address == address
|
||||
assert account.phone_number == phone
|
||||
assert account.failed_pin_attempts == 0
|
||||
assert account.verify_password('0000') is True
|
||||
assert account.get_status(init_database) == AccountStatus.ACTIVE.name
|
||||
|
||||
|
||||
def test_account_repr(activated_account):
|
||||
assert repr(activated_account) == f'<Account: {activated_account.blockchain_address}>'
|
||||
|
||||
|
||||
def test_account_statuses(init_database, pending_account):
|
||||
assert pending_account.get_status(init_database) == AccountStatus.PENDING.name
|
||||
pending_account.create_password('1111')
|
||||
pending_account.activate_account()
|
||||
init_database.add(pending_account)
|
||||
init_database.commit()
|
||||
assert pending_account.get_status(init_database) == AccountStatus.ACTIVE.name
|
||||
pending_account.failed_pin_attempts = 3
|
||||
assert pending_account.get_status(init_database) == AccountStatus.LOCKED.name
|
||||
pending_account.reset_pin(init_database)
|
||||
assert pending_account.get_status(init_database) == AccountStatus.RESET.name
|
||||
pending_account.activate_account()
|
||||
assert pending_account.get_status(init_database) == AccountStatus.ACTIVE.name
|
||||
|
||||
|
||||
def test_get_by_phone_number(activated_account, init_database):
|
||||
account = Account.get_by_phone_number(activated_account.phone_number, init_database)
|
||||
assert account == activated_account
|
||||
|
||||
|
||||
def test_has_preferred_language(activated_account, cache_preferences):
|
||||
assert activated_account.has_preferred_language() is True
|
||||
|
||||
|
||||
def test_lacks_preferred_language(activated_account):
|
||||
assert activated_account.has_preferred_language() is False
|
||||
|
||||
|
||||
def test_has_valid_pin(activated_account, init_database, pending_account):
|
||||
assert activated_account.has_valid_pin(init_database) is True
|
||||
assert pending_account.has_valid_pin(init_database) is False
|
||||
|
||||
|
||||
def test_pin_is_blocked(activated_account, init_database):
|
||||
assert activated_account.pin_is_blocked(init_database) is False
|
||||
activated_account.failed_pin_attempts = 3
|
||||
init_database.add(activated_account)
|
||||
init_database.commit()
|
||||
assert activated_account.pin_is_blocked(init_database) is True
|
||||
|
||||
|
||||
def test_standard_metadata_id(activated_account, cache_person_metadata, pending_account, person_metadata):
|
||||
contact_information = get_contact_data_from_vcard(person_metadata.get('vcard'))
|
||||
given_name = contact_information.get('given')
|
||||
family_name = contact_information.get('family')
|
||||
phone_number = contact_information.get('tel')
|
||||
parsed_account_metadata = f'{given_name} {family_name} {phone_number}'
|
||||
assert activated_account.standard_metadata_id() == parsed_account_metadata
|
||||
assert pending_account.standard_metadata_id() == pending_account.phone_number
|
||||
|
||||
|
||||
def test_account_create(init_cache, init_database, load_chain_spec, mock_account_creation_task_result, task_uuid):
|
||||
chain_str = Chain.spec.__str__()
|
||||
create(chain_str, phone_number(), init_database)
|
||||
assert len(init_database.query(TaskTracker).all()) == 1
|
||||
account_creation_data = get_cached_data(task_uuid)
|
||||
assert json.loads(account_creation_data).get('status') == AccountStatus.PENDING.name
|
||||
@@ -6,9 +6,8 @@ def test_task_tracker(init_database):
|
||||
task_uuid = '31e85315-feee-4b6d-995e-223569082cc4'
|
||||
task_in_tracker = TaskTracker(task_uuid=task_uuid)
|
||||
|
||||
session = init_database
|
||||
session.add(task_in_tracker)
|
||||
session.commit()
|
||||
init_database.add(task_in_tracker)
|
||||
init_database.commit()
|
||||
|
||||
queried_task = session.query(TaskTracker).get(1)
|
||||
queried_task = init_database.query(TaskTracker).get(1)
|
||||
assert queried_task.task_uuid == task_uuid
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
"""Tests the persistence of the user record and associated functions to the user object"""
|
||||
|
||||
# standard imports
|
||||
import pytest
|
||||
|
||||
# platform imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
|
||||
|
||||
def test_user(init_database, set_fernet_key):
|
||||
user = Account(blockchain_address='0x417f5962fc52dc33ff0689659b25848680dec6dcedc6785b03d1df60fc6d5c51',
|
||||
phone_number='+254700000000')
|
||||
user.create_password('0000')
|
||||
|
||||
session = Account.session
|
||||
session.add(user)
|
||||
session.commit()
|
||||
|
||||
queried_user = session.query(Account).get(1)
|
||||
assert queried_user.blockchain_address == '0x417f5962fc52dc33ff0689659b25848680dec6dcedc6785b03d1df60fc6d5c51'
|
||||
assert queried_user.phone_number == '+254700000000'
|
||||
assert queried_user.failed_pin_attempts == 0
|
||||
assert queried_user.verify_password('0000') is True
|
||||
|
||||
|
||||
def test_user_state_transition(create_pending_user):
|
||||
user = create_pending_user
|
||||
session = Account.session
|
||||
|
||||
assert user.get_account_status() == 'PENDING'
|
||||
user.activate_account()
|
||||
assert user.get_account_status() == 'ACTIVE'
|
||||
user.failed_pin_attempts = 3
|
||||
assert user.get_account_status() == 'LOCKED'
|
||||
user.reset_account_pin()
|
||||
assert user.get_account_status() == 'RESET'
|
||||
user.activate_account()
|
||||
assert user.get_account_status() == 'ACTIVE'
|
||||
session.add(user)
|
||||
session.commit()
|
||||
@@ -1,54 +1,78 @@
|
||||
# standard imports
|
||||
import os
|
||||
|
||||
# third party imports
|
||||
import pytest
|
||||
from sqlalchemy import desc
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.ussd_session import UssdSession
|
||||
from cic_ussd.error import VersionTooLowError
|
||||
|
||||
|
||||
def test_ussd_session(init_database, create_in_redis_ussd_session, create_activated_user):
|
||||
session = init_database
|
||||
|
||||
def test_ussd_session(activated_account, init_database, init_cache, load_config):
|
||||
valid_service_codes = load_config.get('USSD_SERVICE_CODE').split(",")
|
||||
ussd_session = UssdSession(
|
||||
external_session_id='AT65423',
|
||||
service_code='*123#',
|
||||
msisdn=create_activated_user.phone_number,
|
||||
external_session_id=os.urandom(20).hex(),
|
||||
service_code=valid_service_codes[0],
|
||||
msisdn=activated_account.phone_number,
|
||||
user_input='1',
|
||||
state='start',
|
||||
session_data={},
|
||||
data={},
|
||||
version=1,
|
||||
)
|
||||
|
||||
session.add(ussd_session)
|
||||
session.commit()
|
||||
init_database.add(ussd_session)
|
||||
init_database.commit()
|
||||
|
||||
ussd_session.set_data(key='foo', session=init_database, value='bar')
|
||||
|
||||
assert ussd_session.get_data('foo') == 'bar'
|
||||
ussd_session.update(
|
||||
session=init_database,
|
||||
user_input='3',
|
||||
state='next',
|
||||
version=2
|
||||
)
|
||||
ussd_session.update('3', 'next', 2, init_database)
|
||||
assert ussd_session.version == 2
|
||||
session.add(ussd_session)
|
||||
session.commit()
|
||||
init_database.add(ussd_session)
|
||||
init_database.commit()
|
||||
|
||||
assert UssdSession.have_session_for_phone(create_activated_user.phone_number) is True
|
||||
assert UssdSession.has_record_for_phone_number(activated_account.phone_number, init_database) is True
|
||||
|
||||
|
||||
def test_version_too_low_error(init_database, create_in_redis_ussd_session, create_activated_user):
|
||||
def test_version_too_low_error(activated_account, init_database, init_cache):
|
||||
with pytest.raises(VersionTooLowError) as e:
|
||||
session = UssdSession(
|
||||
external_session_id='AT38745',
|
||||
service_code='*123#',
|
||||
msisdn=create_activated_user.phone_number,
|
||||
msisdn=activated_account.phone_number,
|
||||
user_input='1',
|
||||
state='start',
|
||||
session_data={},
|
||||
data={},
|
||||
version=3,
|
||||
)
|
||||
assert session.check_version(1)
|
||||
assert session.check_version(3)
|
||||
assert str(e.value) == 'New session version number is not greater than last saved version!'
|
||||
|
||||
|
||||
def test_set_data(init_database, ussd_session_data, persisted_ussd_session):
|
||||
assert persisted_ussd_session.data == {}
|
||||
for key, value in ussd_session_data.items():
|
||||
persisted_ussd_session.set_data(key, init_database, value)
|
||||
init_database.commit()
|
||||
|
||||
assert persisted_ussd_session.get_data('recipient') == ussd_session_data.get('recipient')
|
||||
|
||||
|
||||
def test_has_record_for_phone_number(activated_account, init_database, persisted_ussd_session):
|
||||
ussd_session = UssdSession.has_record_for_phone_number(activated_account.phone_number, init_database)
|
||||
assert ussd_session is not None
|
||||
|
||||
|
||||
def test_last_ussd_session(init_database, ussd_session_traffic):
|
||||
assert len(init_database.query(UssdSession).all()) >= 5
|
||||
ussd_session = init_database.query(UssdSession).order_by(desc(UssdSession.created)).first()
|
||||
phone_number = ussd_session.msisdn
|
||||
assert UssdSession.last_ussd_session(phone_number, init_database).id == ussd_session.id
|
||||
|
||||
|
||||
def test_persisted_to_json(persisted_ussd_session):
|
||||
assert isinstance(persisted_ussd_session, UssdSession)
|
||||
assert isinstance(persisted_ussd_session.to_json(), dict)
|
||||
@@ -10,11 +10,6 @@ from cic_ussd.db import dsn_from_config
|
||||
def test_dsn_from_config(load_config):
|
||||
"""
|
||||
"""
|
||||
# test dsn for sqlite engine
|
||||
dsn = dsn_from_config(load_config)
|
||||
scheme = f'{load_config.get("DATABASE_ENGINE")}+{load_config.get("DATABASE_DRIVER")}'
|
||||
assert dsn == f'{scheme}:///{load_config.get("DATABASE_NAME")}'
|
||||
|
||||
# test dsn for other db formats
|
||||
overrides = {
|
||||
'DATABASE_PASSWORD': 'password',
|
||||
@@ -28,3 +23,16 @@ def test_dsn_from_config(load_config):
|
||||
dsn = dsn_from_config(load_config)
|
||||
assert dsn == f"{scheme}://{load_config.get('DATABASE_USER')}:{load_config.get('DATABASE_PASSWORD')}@{load_config.get('DATABASE_HOST')}:{load_config.get('DATABASE_PORT')}/{load_config.get('DATABASE_NAME')}"
|
||||
|
||||
# undoes overrides to revert engine and drivers to sqlite
|
||||
overrides = {
|
||||
'DATABASE_PASSWORD': '',
|
||||
'DATABASE_DRIVER': 'pysqlite',
|
||||
'DATABASE_ENGINE': 'sqlite'
|
||||
}
|
||||
load_config.dict_override(dct=overrides, dct_description='Override values to test different db formats.')
|
||||
|
||||
# test dsn for sqlite engine
|
||||
dsn = dsn_from_config(load_config)
|
||||
scheme = f'{load_config.get("DATABASE_ENGINE")}+{load_config.get("DATABASE_DRIVER")}'
|
||||
assert dsn == f'{scheme}:///{load_config.get("DATABASE_NAME")}'
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ def test_json_file_parser(load_config):
|
||||
WHEN the json_file_parser function is passed a directory path containing JSON files
|
||||
THEN it dynamically loads all the files and compiles their content into one python array
|
||||
"""
|
||||
files_dir = load_config.get('STATEMACHINE_TRANSITIONS')
|
||||
files_dir = load_config.get('MACHINE_TRANSITIONS')
|
||||
files_dir = os.path.join(root_directory, files_dir)
|
||||
|
||||
# total files len
|
||||
@@ -46,10 +46,7 @@ def test_json_file_parser(load_config):
|
||||
# get path of data files
|
||||
filepath = os.path.join(files_dir, filepath)
|
||||
|
||||
# read file content
|
||||
data_file = open(filepath)
|
||||
data = json.load(data_file)
|
||||
file_content_length += len(data)
|
||||
data_file.close()
|
||||
with open(filepath) as data_file:
|
||||
data = json.load(data_file)
|
||||
file_content_length += len(data)
|
||||
assert len(json_file_parser(filepath=files_dir)) == file_content_length
|
||||
|
||||
|
||||
69
apps/cic-ussd/tests/cic_ussd/http/test_requests.py
Normal file
69
apps/cic-ussd/tests/cic_ussd/http/test_requests.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# standard imports
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
import requests
|
||||
from requests.exceptions import HTTPError
|
||||
import requests_mock
|
||||
|
||||
# local imports
|
||||
from cic_ussd.http.requests import (error_handler,
|
||||
get_query_parameters,
|
||||
get_request_endpoint,
|
||||
get_request_method,
|
||||
make_request)
|
||||
from cic_ussd.error import UnsupportedMethodError
|
||||
# test imports
|
||||
|
||||
|
||||
@pytest.mark.parametrize('status_code, starts_with', [
|
||||
(102, 'Informational errors'),
|
||||
(303, 'Redirect Issues'),
|
||||
(406, 'Client Error'),
|
||||
(500, 'Server Error')
|
||||
])
|
||||
def test_error_handler(status_code, starts_with, mocker):
|
||||
mock_result = mocker.patch('requests.Response')
|
||||
mock_result.status_code = status_code
|
||||
with pytest.raises(HTTPError) as error:
|
||||
error_handler(mock_result)
|
||||
assert str(error.value).startswith(starts_with)
|
||||
|
||||
|
||||
def test_get_query_parameters(with_params_env):
|
||||
assert get_query_parameters(with_params_env, 'phone') == with_params_env.get('REQUEST_URI')[8:]
|
||||
parsed_url = urlparse(with_params_env.get('REQUEST_URI'))
|
||||
params = parse_qs(parsed_url.query)
|
||||
assert get_query_parameters(with_params_env) == params
|
||||
|
||||
|
||||
def test_get_request_endpoint(with_params_env):
|
||||
assert get_request_endpoint(with_params_env) == with_params_env.get('PATH_INFO')
|
||||
|
||||
|
||||
def test_get_request_method(with_params_env):
|
||||
assert get_request_method(with_params_env) == with_params_env.get('REQUEST_METHOD')
|
||||
|
||||
|
||||
def test_make_request(mock_response, mock_url):
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri('GET', mock_url, status_code=200, reason='OK', json=mock_response)
|
||||
response = make_request(method='GET', url=mock_url)
|
||||
assert response.json() == requests.get(mock_url).json()
|
||||
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri('POST', mock_url, status_code=201, reason='CREATED', json=mock_response)
|
||||
response = make_request('POST', mock_url, {'test': 'data'})
|
||||
assert response.content == requests.post(mock_url).content
|
||||
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri('PUT', mock_url, status_code=200, reason='OK')
|
||||
response = make_request('PUT', mock_url, data={'test': 'data'})
|
||||
assert response.content == requests.put(mock_url).content
|
||||
|
||||
with pytest.raises(UnsupportedMethodError) as error:
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri('DELETE', mock_url, status_code=200, reason='OK')
|
||||
make_request('DELETE', mock_url)
|
||||
assert str(error.value) == 'Unsupported method: DELETE'
|
||||
18
apps/cic-ussd/tests/cic_ussd/http/test_responses.py
Normal file
18
apps/cic-ussd/tests/cic_ussd/http/test_responses.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# standard imports
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.http.responses import with_content_headers
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
@pytest.mark.parametrize('headers, response, expected_result',[
|
||||
([('Content-Type', 'text/plain')], 'some-text', (b'some-text', [('Content-Type', 'text/plain'), ('Content-Length', '9')])),
|
||||
([('Content-Type', 'text/plain'), ('Content-Length', '0')], 'some-text', (b'some-text', [('Content-Type', 'text/plain'), ('Content-Length', '9')]))
|
||||
])
|
||||
def test_with_content_headers(headers, response, expected_result):
|
||||
response_bytes, headers = with_content_headers(headers, response)
|
||||
assert response_bytes, headers == expected_result
|
||||
45
apps/cic-ussd/tests/cic_ussd/http/test_routes.py
Normal file
45
apps/cic-ussd/tests/cic_ussd/http/test_routes.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.http.routes import locked_accounts, pin_reset
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
@pytest.mark.parametrize('method, expected_response, expected_message', [
|
||||
('GET', '{"status": "LOCKED"}', '200 OK'),
|
||||
('PUT', 'Pin reset successful.', '200 OK'),
|
||||
])
|
||||
def test_pin_reset(method, expected_response, expected_message, init_database, pin_blocked_account, uwsgi_env):
|
||||
uwsgi_env['REQUEST_METHOD'] = method
|
||||
response, message = pin_reset(uwsgi_env, pin_blocked_account.phone_number, init_database)
|
||||
assert response == expected_response
|
||||
assert message == expected_message
|
||||
|
||||
response, message = pin_reset(uwsgi_env, '070000000', init_database)
|
||||
assert response == ''
|
||||
assert message == '404 Not found'
|
||||
|
||||
|
||||
def test_locked_accounts(init_database, locked_accounts_env, locked_accounts_traffic):
|
||||
response, message = locked_accounts(locked_accounts_env, init_database)
|
||||
assert message == '200 OK'
|
||||
locked_account_addresses = json.loads(response)
|
||||
assert len(locked_account_addresses) == 10
|
||||
account_1 = init_database.query(Account).filter_by(blockchain_address=locked_account_addresses[2]).first()
|
||||
account_2 = init_database.query(Account).filter_by(blockchain_address=locked_account_addresses[7]).first()
|
||||
assert account_1.updated > account_2.updated
|
||||
locked_accounts_env['PATH_INFO'] = '/accounts/locked/10'
|
||||
response, message = locked_accounts(locked_accounts_env, init_database)
|
||||
assert message == '200 OK'
|
||||
locked_account_addresses = json.loads(response)
|
||||
assert len(locked_account_addresses) == 10
|
||||
locked_accounts_env['REQUEST_METHOD'] = 'POST'
|
||||
response, message = locked_accounts(locked_accounts_env, init_database)
|
||||
assert message == '405 Play by the rules'
|
||||
assert response == ''
|
||||
@@ -1,12 +1,14 @@
|
||||
# standard imports
|
||||
import os
|
||||
|
||||
# third party imports
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.files.local_files import create_local_file_data_stores
|
||||
from cic_ussd.menu.ussd_menu import UssdMenu
|
||||
|
||||
# tests imports
|
||||
from tests.helpers.tmp_files import create_tmp_file
|
||||
|
||||
|
||||
@@ -49,4 +51,3 @@ def test_failed_create_ussd_menu():
|
||||
assert str(error.value) == "Menu already exists!"
|
||||
os.close(descriptor)
|
||||
os.remove(tmp_file)
|
||||
|
||||
|
||||
44
apps/cic-ussd/tests/cic_ussd/metadata/test_base.py
Normal file
44
apps/cic-ussd/tests/cic_ussd/metadata/test_base.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# standard imports
|
||||
import json
|
||||
import os
|
||||
|
||||
# external imports
|
||||
import requests_mock
|
||||
from chainlib.hash import strip_0x
|
||||
from cic_types.processor import generate_metadata_pointer
|
||||
|
||||
# local imports
|
||||
from cic_ussd.metadata.base import MetadataRequestsHandler
|
||||
|
||||
|
||||
# external imports
|
||||
|
||||
|
||||
def test_metadata_requests_handler(activated_account,
|
||||
init_cache,
|
||||
load_config,
|
||||
person_metadata,
|
||||
setup_metadata_request_handler,
|
||||
setup_metadata_signer):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
cic_type = ':cic.person'
|
||||
metadata_client = MetadataRequestsHandler(cic_type, identifier)
|
||||
assert metadata_client.cic_type == cic_type
|
||||
assert metadata_client.engine == 'pgp'
|
||||
assert metadata_client.identifier == identifier
|
||||
assert metadata_client.metadata_pointer == generate_metadata_pointer(identifier, cic_type)
|
||||
assert metadata_client.url == os.path.join(load_config.get('CIC_META_URL'), metadata_client.metadata_pointer)
|
||||
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri('POST', metadata_client.url, status_code=200, reason='OK', json=person_metadata)
|
||||
person_metadata['digest'] = os.urandom(20).hex()
|
||||
request_mocker.register_uri('PUT', metadata_client.url, status_code=200, reason='OK', json=person_metadata)
|
||||
result = metadata_client.create(person_metadata)
|
||||
assert result.json() == person_metadata
|
||||
assert result.status_code == 200
|
||||
person_metadata.pop('digest')
|
||||
request_mocker.register_uri('GET', metadata_client.url, status_code=200, reason='OK', json=person_metadata)
|
||||
result = metadata_client.query()
|
||||
assert result == person_metadata
|
||||
cached_metadata = metadata_client.get_cached_metadata()
|
||||
assert json.loads(cached_metadata) == person_metadata
|
||||
22
apps/cic-ussd/tests/cic_ussd/metadata/test_custom.py
Normal file
22
apps/cic-ussd/tests/cic_ussd/metadata/test_custom.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# standard imports
|
||||
import os
|
||||
# external imports
|
||||
from chainlib.hash import strip_0x
|
||||
from cic_types.processor import generate_metadata_pointer
|
||||
|
||||
# local imports
|
||||
from cic_ussd.metadata import CustomMetadata
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def test_custom_metadata(activated_account, load_config, setup_metadata_request_handler, setup_metadata_signer):
|
||||
cic_type = ':cic.custom'
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
custom_metadata_client = CustomMetadata(identifier)
|
||||
assert custom_metadata_client.cic_type == cic_type
|
||||
assert custom_metadata_client.engine == 'pgp'
|
||||
assert custom_metadata_client.identifier == identifier
|
||||
assert custom_metadata_client.metadata_pointer == generate_metadata_pointer(identifier, cic_type)
|
||||
assert custom_metadata_client.url == os.path.join(
|
||||
load_config.get('CIC_META_URL'), custom_metadata_client.metadata_pointer)
|
||||
@@ -1,80 +0,0 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# third-party imports
|
||||
import pytest
|
||||
import requests
|
||||
import requests_mock
|
||||
|
||||
# local imports
|
||||
from cic_ussd.error import UnsupportedMethodError
|
||||
from cic_ussd.metadata import blockchain_address_to_metadata_pointer, make_request
|
||||
|
||||
|
||||
def test_make_request(define_metadata_pointer_url, mock_meta_get_response, mock_meta_post_response, person_metadata):
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri(
|
||||
'GET',
|
||||
define_metadata_pointer_url,
|
||||
status_code=200,
|
||||
reason='OK',
|
||||
content=json.dumps(mock_meta_get_response).encode('utf-8')
|
||||
)
|
||||
response = make_request(method='GET', url=define_metadata_pointer_url)
|
||||
assert response.content == requests.get(define_metadata_pointer_url).content
|
||||
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri(
|
||||
'POST',
|
||||
define_metadata_pointer_url,
|
||||
status_code=201,
|
||||
reason='CREATED',
|
||||
content=json.dumps(mock_meta_post_response).encode('utf-8')
|
||||
)
|
||||
response = make_request(
|
||||
method='POST',
|
||||
url=define_metadata_pointer_url,
|
||||
data=json.dumps(person_metadata).encode('utf-8'),
|
||||
headers={
|
||||
'X-CIC-AUTOMERGE': 'server',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
)
|
||||
assert response.content == requests.post(define_metadata_pointer_url).content
|
||||
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri(
|
||||
'PUT',
|
||||
define_metadata_pointer_url,
|
||||
status_code=200,
|
||||
reason='OK'
|
||||
)
|
||||
response = make_request(
|
||||
method='PUT',
|
||||
url=define_metadata_pointer_url,
|
||||
data=json.dumps(person_metadata).encode('utf-8'),
|
||||
headers={
|
||||
'X-CIC-AUTOMERGE': 'server',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
)
|
||||
assert response.content == requests.put(define_metadata_pointer_url).content
|
||||
|
||||
with pytest.raises(UnsupportedMethodError) as error:
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri(
|
||||
'DELETE',
|
||||
define_metadata_pointer_url,
|
||||
status_code=200,
|
||||
reason='OK'
|
||||
)
|
||||
make_request(
|
||||
method='DELETE',
|
||||
url=define_metadata_pointer_url
|
||||
)
|
||||
assert str(error.value) == 'Unsupported method: DELETE'
|
||||
|
||||
|
||||
def test_blockchain_address_to_metadata_pointer(create_activated_user):
|
||||
blockchain_address = create_activated_user.blockchain_address
|
||||
assert type(blockchain_address_to_metadata_pointer(blockchain_address)) == bytes
|
||||
22
apps/cic-ussd/tests/cic_ussd/metadata/test_person.py
Normal file
22
apps/cic-ussd/tests/cic_ussd/metadata/test_person.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# standard imports
|
||||
import os
|
||||
# external imports
|
||||
from chainlib.hash import strip_0x
|
||||
from cic_types.processor import generate_metadata_pointer
|
||||
|
||||
# local imports
|
||||
from cic_ussd.metadata import PersonMetadata
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def test_person_metadata(activated_account, load_config, setup_metadata_request_handler, setup_metadata_signer):
|
||||
cic_type = ':cic.person'
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
person_metadata_client = PersonMetadata(identifier)
|
||||
assert person_metadata_client.cic_type == cic_type
|
||||
assert person_metadata_client.engine == 'pgp'
|
||||
assert person_metadata_client.identifier == identifier
|
||||
assert person_metadata_client.metadata_pointer == generate_metadata_pointer(identifier, cic_type)
|
||||
assert person_metadata_client.url == os.path.join(
|
||||
load_config.get('CIC_META_URL'), person_metadata_client.metadata_pointer)
|
||||
23
apps/cic-ussd/tests/cic_ussd/metadata/test_phone.py
Normal file
23
apps/cic-ussd/tests/cic_ussd/metadata/test_phone.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# standard imports
|
||||
import os
|
||||
# external imports
|
||||
from chainlib.hash import strip_0x
|
||||
from cic_types.processor import generate_metadata_pointer
|
||||
|
||||
# local imports
|
||||
from cic_ussd.metadata import PhonePointerMetadata
|
||||
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def test_phone_pointer_metadata(activated_account, load_config, setup_metadata_request_handler, setup_metadata_signer):
|
||||
cic_type = ':cic.phone'
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
phone_pointer_metadata = PhonePointerMetadata(identifier)
|
||||
assert phone_pointer_metadata.cic_type == cic_type
|
||||
assert phone_pointer_metadata.engine == 'pgp'
|
||||
assert phone_pointer_metadata.identifier == identifier
|
||||
assert phone_pointer_metadata.metadata_pointer == generate_metadata_pointer(identifier, cic_type)
|
||||
assert phone_pointer_metadata.url == os.path.join(
|
||||
load_config.get('CIC_META_URL'), phone_pointer_metadata.metadata_pointer)
|
||||
22
apps/cic-ussd/tests/cic_ussd/metadata/test_preferences.py
Normal file
22
apps/cic-ussd/tests/cic_ussd/metadata/test_preferences.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# standard imports
|
||||
import os
|
||||
# external imports
|
||||
from chainlib.hash import strip_0x
|
||||
from cic_types.processor import generate_metadata_pointer
|
||||
|
||||
# local imports
|
||||
from cic_ussd.metadata import PreferencesMetadata
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def test_preferences_metadata(activated_account, load_config, setup_metadata_request_handler, setup_metadata_signer):
|
||||
cic_type = ':cic.preferences'
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
preferences_metadata_client = PreferencesMetadata(identifier)
|
||||
assert preferences_metadata_client.cic_type == cic_type
|
||||
assert preferences_metadata_client.engine == 'pgp'
|
||||
assert preferences_metadata_client.identifier == identifier
|
||||
assert preferences_metadata_client.metadata_pointer == generate_metadata_pointer(identifier, cic_type)
|
||||
assert preferences_metadata_client.url == os.path.join(
|
||||
load_config.get('CIC_META_URL'), preferences_metadata_client.metadata_pointer)
|
||||
@@ -9,26 +9,9 @@ from cic_ussd.metadata.signer import Signer
|
||||
|
||||
def test_client(load_config, setup_metadata_signer, person_metadata):
|
||||
signer = Signer()
|
||||
# get gpg used
|
||||
digest = 'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2'
|
||||
person_metadata['digest'] = digest
|
||||
gpg = signer.gpg
|
||||
|
||||
# check that key data was loaded
|
||||
assert signer.key_data is not None
|
||||
|
||||
# check that correct operational key is returned
|
||||
gpg.import_keys(key_data=signer.key_data)
|
||||
gpg_keys = gpg.list_keys()
|
||||
assert signer.get_operational_key() == gpg_keys[0]
|
||||
|
||||
# check that correct signature is returned
|
||||
key_id = signer.get_operational_key().get('keyid')
|
||||
signature = gpg.sign(message=digest, passphrase=load_config.get('KEYS_PASSPHRASE'), keyid=key_id)
|
||||
assert str(signature) == signer.sign_digest(data=person_metadata)
|
||||
|
||||
# remove tmp gpg file
|
||||
shutil.rmtree(Signer.gpg_path)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# third-party imports
|
||||
import pytest
|
||||
import requests_mock
|
||||
from cic_types.models.person import generate_metadata_pointer
|
||||
|
||||
# local imports
|
||||
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
||||
from cic_ussd.metadata.signer import Signer
|
||||
from cic_ussd.metadata.person import PersonMetadata
|
||||
from cic_ussd.redis import get_cached_data
|
||||
|
||||
|
||||
def test_user_metadata(create_activated_user, define_metadata_pointer_url, load_config):
|
||||
PersonMetadata.base_url = load_config.get('CIC_META_URL')
|
||||
identifier = blockchain_address_to_metadata_pointer(blockchain_address=create_activated_user.blockchain_address)
|
||||
person_metadata_client = PersonMetadata(identifier=identifier)
|
||||
|
||||
assert person_metadata_client.url == define_metadata_pointer_url
|
||||
|
||||
|
||||
def test_create_person_metadata(caplog,
|
||||
create_activated_user,
|
||||
define_metadata_pointer_url,
|
||||
load_config,
|
||||
mock_meta_post_response,
|
||||
person_metadata):
|
||||
identifier = blockchain_address_to_metadata_pointer(blockchain_address=create_activated_user.blockchain_address)
|
||||
person_metadata_client = PersonMetadata(identifier=identifier)
|
||||
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri(
|
||||
'POST',
|
||||
define_metadata_pointer_url,
|
||||
status_code=201,
|
||||
reason='CREATED',
|
||||
content=json.dumps(mock_meta_post_response).encode('utf-8')
|
||||
)
|
||||
person_metadata_client.create(data=person_metadata)
|
||||
assert 'Get signed material response status: 201' in caplog.text
|
||||
|
||||
with pytest.raises(RuntimeError) as error:
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri(
|
||||
'POST',
|
||||
define_metadata_pointer_url,
|
||||
status_code=400,
|
||||
reason='BAD REQUEST'
|
||||
)
|
||||
person_metadata_client.create(data=person_metadata)
|
||||
assert str(error.value) == f'400 Client Error: BAD REQUEST for url: {define_metadata_pointer_url}'
|
||||
|
||||
|
||||
def test_edit_person_metadata(caplog,
|
||||
create_activated_user,
|
||||
define_metadata_pointer_url,
|
||||
load_config,
|
||||
person_metadata,
|
||||
setup_metadata_signer):
|
||||
Signer.gpg_passphrase = load_config.get('KEYS_PASSPHRASE')
|
||||
identifier = blockchain_address_to_metadata_pointer(blockchain_address=create_activated_user.blockchain_address)
|
||||
person_metadata_client = PersonMetadata(identifier=identifier)
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri(
|
||||
'PUT',
|
||||
define_metadata_pointer_url,
|
||||
status_code=200,
|
||||
reason='OK'
|
||||
)
|
||||
person_metadata_client.edit(data=person_metadata)
|
||||
assert 'Signed content submission status: 200' in caplog.text
|
||||
|
||||
with pytest.raises(RuntimeError) as error:
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri(
|
||||
'PUT',
|
||||
define_metadata_pointer_url,
|
||||
status_code=400,
|
||||
reason='BAD REQUEST'
|
||||
)
|
||||
person_metadata_client.edit(data=person_metadata)
|
||||
assert str(error.value) == f'400 Client Error: BAD REQUEST for url: {define_metadata_pointer_url}'
|
||||
|
||||
|
||||
def test_get_user_metadata(caplog,
|
||||
create_activated_user,
|
||||
define_metadata_pointer_url,
|
||||
init_redis_cache,
|
||||
load_config,
|
||||
person_metadata,
|
||||
setup_metadata_signer):
|
||||
identifier = blockchain_address_to_metadata_pointer(blockchain_address=create_activated_user.blockchain_address)
|
||||
person_metadata_client = PersonMetadata(identifier=identifier)
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri(
|
||||
'GET',
|
||||
define_metadata_pointer_url,
|
||||
status_code=200,
|
||||
content=json.dumps(person_metadata).encode('utf-8'),
|
||||
reason='OK'
|
||||
)
|
||||
person_metadata_client.query()
|
||||
assert 'Get latest data status: 200' in caplog.text
|
||||
key = generate_metadata_pointer(
|
||||
identifier=identifier,
|
||||
cic_type=':cic.person'
|
||||
)
|
||||
cached_user_metadata = get_cached_data(key=key)
|
||||
assert cached_user_metadata
|
||||
|
||||
with pytest.raises(RuntimeError) as error:
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri(
|
||||
'GET',
|
||||
define_metadata_pointer_url,
|
||||
status_code=404,
|
||||
reason='NOT FOUND'
|
||||
)
|
||||
person_metadata_client.query()
|
||||
assert 'The data is not available and might need to be added.' in caplog.text
|
||||
assert str(error.value) == f'400 Client Error: NOT FOUND for url: {define_metadata_pointer_url}'
|
||||
183
apps/cic-ussd/tests/cic_ussd/processor/test_menu.py
Normal file
183
apps/cic-ussd/tests/cic_ussd/processor/test_menu.py
Normal file
@@ -0,0 +1,183 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# external imports
|
||||
from chainlib.hash import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.balance import get_cached_available_balance
|
||||
from cic_ussd.account.metadata import get_cached_preferred_language
|
||||
from cic_ussd.account.statement import (
|
||||
get_cached_statement,
|
||||
parse_statement_transactions,
|
||||
statement_transaction_set
|
||||
)
|
||||
from cic_ussd.account.tokens import get_default_token_symbol
|
||||
from cic_ussd.account.transaction import from_wei, to_wei
|
||||
from cic_ussd.menu.ussd_menu import UssdMenu
|
||||
from cic_ussd.metadata import PersonMetadata
|
||||
from cic_ussd.phone_number import Support
|
||||
from cic_ussd.processor.menu import response
|
||||
from cic_ussd.processor.util import parse_person_metadata
|
||||
from cic_ussd.translation import translation_for
|
||||
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def test_menu_processor(activated_account,
|
||||
balances,
|
||||
cache_balances,
|
||||
cache_default_token_data,
|
||||
cache_preferences,
|
||||
cache_person_metadata,
|
||||
cache_statement,
|
||||
celery_session_worker,
|
||||
generic_ussd_session,
|
||||
init_database,
|
||||
load_chain_spec,
|
||||
load_support_phone,
|
||||
load_ussd_menu,
|
||||
mock_sync_balance_api_query,
|
||||
mock_transaction_list_query,
|
||||
valid_recipient):
|
||||
preferred_language = get_cached_preferred_language(activated_account.blockchain_address)
|
||||
available_balance = get_cached_available_balance(activated_account.blockchain_address)
|
||||
token_symbol = get_default_token_symbol()
|
||||
|
||||
tax = ''
|
||||
bonus = ''
|
||||
display_key = 'ussd.kenya.account_balances'
|
||||
ussd_menu = UssdMenu.find_by_name('account_balances')
|
||||
name = ussd_menu.get('name')
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
assert resp == translation_for(display_key,
|
||||
preferred_language,
|
||||
available_balance=available_balance,
|
||||
tax=tax,
|
||||
bonus=bonus,
|
||||
token_symbol=token_symbol)
|
||||
|
||||
cached_statement = get_cached_statement(activated_account.blockchain_address)
|
||||
statement = json.loads(cached_statement)
|
||||
statement_transactions = parse_statement_transactions(statement)
|
||||
transaction_sets = [statement_transactions[tx:tx + 3] for tx in range(0, len(statement_transactions), 3)]
|
||||
first_transaction_set = []
|
||||
middle_transaction_set = []
|
||||
last_transaction_set = []
|
||||
if transaction_sets:
|
||||
first_transaction_set = statement_transaction_set(preferred_language, transaction_sets[0])
|
||||
if len(transaction_sets) >= 2:
|
||||
middle_transaction_set = statement_transaction_set(preferred_language, transaction_sets[1])
|
||||
if len(transaction_sets) >= 3:
|
||||
last_transaction_set = statement_transaction_set(preferred_language, transaction_sets[2])
|
||||
|
||||
display_key = 'ussd.kenya.first_transaction_set'
|
||||
ussd_menu = UssdMenu.find_by_name('first_transaction_set')
|
||||
name = ussd_menu.get('name')
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
|
||||
assert resp == translation_for(display_key, preferred_language, first_transaction_set=first_transaction_set)
|
||||
|
||||
display_key = 'ussd.kenya.middle_transaction_set'
|
||||
ussd_menu = UssdMenu.find_by_name('middle_transaction_set')
|
||||
name = ussd_menu.get('name')
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
|
||||
assert resp == translation_for(display_key, preferred_language, middle_transaction_set=middle_transaction_set)
|
||||
|
||||
display_key = 'ussd.kenya.last_transaction_set'
|
||||
ussd_menu = UssdMenu.find_by_name('last_transaction_set')
|
||||
name = ussd_menu.get('name')
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
|
||||
assert resp == translation_for(display_key, preferred_language, last_transaction_set=last_transaction_set)
|
||||
|
||||
display_key = 'ussd.kenya.display_user_metadata'
|
||||
ussd_menu = UssdMenu.find_by_name('display_user_metadata')
|
||||
name = ussd_menu.get('name')
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
person_metadata = PersonMetadata(identifier)
|
||||
cached_person_metadata = person_metadata.get_cached_metadata()
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
assert resp == parse_person_metadata(cached_person_metadata, display_key, preferred_language)
|
||||
|
||||
display_key = 'ussd.kenya.account_balances_pin_authorization'
|
||||
ussd_menu = UssdMenu.find_by_name('account_balances_pin_authorization')
|
||||
name = ussd_menu.get('name')
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
assert resp == translation_for(f'{display_key}.first', preferred_language)
|
||||
|
||||
activated_account.failed_pin_attempts = 1
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
retry_pin_entry = translation_for('ussd.kenya.retry_pin_entry', preferred_language, remaining_attempts=2)
|
||||
assert resp == translation_for(f'{display_key}.retry', preferred_language, retry_pin_entry=retry_pin_entry)
|
||||
activated_account.failed_pin_attempts = 0
|
||||
|
||||
display_key = 'ussd.kenya.start'
|
||||
ussd_menu = UssdMenu.find_by_name('start')
|
||||
name = ussd_menu.get('name')
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
assert resp == translation_for(display_key,
|
||||
preferred_language,
|
||||
account_balance=available_balance,
|
||||
account_token_name=token_symbol)
|
||||
|
||||
display_key = 'ussd.kenya.transaction_pin_authorization'
|
||||
ussd_menu = UssdMenu.find_by_name('transaction_pin_authorization')
|
||||
name = ussd_menu.get('name')
|
||||
generic_ussd_session['data'] = {
|
||||
'recipient_phone_number': valid_recipient.phone_number,
|
||||
'transaction_amount': '15'
|
||||
}
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
user_input = generic_ussd_session.get('data').get('transaction_amount')
|
||||
transaction_amount = to_wei(value=int(user_input))
|
||||
tx_recipient_information = valid_recipient.standard_metadata_id()
|
||||
tx_sender_information = activated_account.standard_metadata_id()
|
||||
assert resp == translation_for(f'{display_key}.first',
|
||||
preferred_language,
|
||||
recipient_information=tx_recipient_information,
|
||||
transaction_amount=from_wei(transaction_amount),
|
||||
token_symbol=token_symbol,
|
||||
sender_information=tx_sender_information)
|
||||
|
||||
display_key = 'ussd.kenya.exit_insufficient_balance'
|
||||
ussd_menu = UssdMenu.find_by_name('exit_insufficient_balance')
|
||||
name = ussd_menu.get('name')
|
||||
generic_ussd_session['data'] = {
|
||||
'recipient_phone_number': valid_recipient.phone_number,
|
||||
'transaction_amount': '85'
|
||||
}
|
||||
transaction_amount = generic_ussd_session.get('data').get('transaction_amount')
|
||||
transaction_amount = to_wei(value=int(transaction_amount))
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
assert resp == translation_for(display_key,
|
||||
preferred_language,
|
||||
amount=from_wei(transaction_amount),
|
||||
token_symbol=token_symbol,
|
||||
recipient_information=tx_recipient_information,
|
||||
token_balance=available_balance)
|
||||
|
||||
display_key = 'ussd.kenya.exit_invalid_menu_option'
|
||||
ussd_menu = UssdMenu.find_by_name('exit_invalid_menu_option')
|
||||
name = ussd_menu.get('name')
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
assert resp == translation_for(display_key, preferred_language, support_phone=Support.phone_number)
|
||||
|
||||
display_key = 'ussd.kenya.exit_successful_transaction'
|
||||
ussd_menu = UssdMenu.find_by_name('exit_successful_transaction')
|
||||
name = ussd_menu.get('name')
|
||||
generic_ussd_session['data'] = {
|
||||
'recipient_phone_number': valid_recipient.phone_number,
|
||||
'transaction_amount': '15'
|
||||
}
|
||||
transaction_amount = generic_ussd_session.get('data').get('transaction_amount')
|
||||
transaction_amount = to_wei(value=int(transaction_amount))
|
||||
resp = response(activated_account, display_key, name, init_database, generic_ussd_session)
|
||||
assert resp == translation_for(display_key,
|
||||
preferred_language,
|
||||
transaction_amount=from_wei(transaction_amount),
|
||||
token_symbol=token_symbol,
|
||||
recipient_information=tx_recipient_information,
|
||||
sender_information=tx_sender_information)
|
||||
121
apps/cic-ussd/tests/cic_ussd/processor/test_ussd.py
Normal file
121
apps/cic-ussd/tests/cic_ussd/processor/test_ussd.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# standard imports
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
|
||||
# external imports
|
||||
import i18n
|
||||
import requests_mock
|
||||
from chainlib.hash import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.chain import Chain
|
||||
from cic_ussd.account.metadata import get_cached_preferred_language
|
||||
from cic_ussd.cache import cache_data, cache_data_key, get_cached_data
|
||||
from cic_ussd.db.models.task_tracker import TaskTracker
|
||||
from cic_ussd.menu.ussd_menu import UssdMenu
|
||||
from cic_ussd.metadata import PersonMetadata
|
||||
from cic_ussd.processor.ussd import get_menu, handle_menu, handle_menu_operations
|
||||
from cic_ussd.translation import translation_for
|
||||
|
||||
# test imports
|
||||
from tests.helpers.accounts import phone_number
|
||||
|
||||
|
||||
def test_handle_menu(activated_account,
|
||||
init_cache,
|
||||
init_database,
|
||||
load_ussd_menu,
|
||||
pending_account,
|
||||
persisted_ussd_session,
|
||||
pin_blocked_account,
|
||||
preferences):
|
||||
persisted_ussd_session.state = 'account_creation_prompt'
|
||||
menu_resp = handle_menu(activated_account, init_database)
|
||||
ussd_menu = UssdMenu.find_by_name('start')
|
||||
assert menu_resp.get('name') == ussd_menu.get('name')
|
||||
persisted_ussd_session.state = 'display_user_metadata'
|
||||
menu_resp = handle_menu(activated_account, init_database)
|
||||
ussd_menu = UssdMenu.find_by_name('display_user_metadata')
|
||||
assert menu_resp.get('name') == ussd_menu.get('name')
|
||||
menu_resp = handle_menu(pin_blocked_account, init_database)
|
||||
ussd_menu = UssdMenu.find_by_name('exit_pin_blocked')
|
||||
assert menu_resp.get('name') == ussd_menu.get('name')
|
||||
menu_resp = handle_menu(pending_account, init_database)
|
||||
ussd_menu = UssdMenu.find_by_name('initial_language_selection')
|
||||
assert menu_resp.get('name') == ussd_menu.get('name')
|
||||
identifier = bytes.fromhex(strip_0x(pending_account.blockchain_address))
|
||||
key = cache_data_key(identifier, ':cic.preferences')
|
||||
cache_data(key, json.dumps(preferences))
|
||||
time.sleep(2)
|
||||
menu_resp = handle_menu(pending_account, init_database)
|
||||
ussd_menu = UssdMenu.find_by_name('initial_pin_entry')
|
||||
assert menu_resp.get('name') == ussd_menu.get('name')
|
||||
|
||||
|
||||
def test_get_menu(activated_account,
|
||||
cache_preferences,
|
||||
cache_person_metadata,
|
||||
generic_ussd_session,
|
||||
init_database,
|
||||
init_state_machine,
|
||||
persisted_ussd_session):
|
||||
menu_resp = get_menu(activated_account, init_database, '', generic_ussd_session)
|
||||
ussd_menu = UssdMenu.find_by_name(name='exit_invalid_input')
|
||||
assert menu_resp.get('name') == ussd_menu.get('name')
|
||||
|
||||
menu_resp = get_menu(activated_account, init_database, '1111', None)
|
||||
ussd_menu = UssdMenu.find_by_name('initial_language_selection')
|
||||
assert menu_resp.get('name') == ussd_menu.get('name')
|
||||
|
||||
generic_ussd_session['state'] = 'start'
|
||||
menu_resp = get_menu(activated_account, init_database, '1', generic_ussd_session)
|
||||
ussd_menu = UssdMenu.find_by_name(name='enter_transaction_recipient')
|
||||
assert menu_resp.get('name') == ussd_menu.get('name')
|
||||
|
||||
|
||||
def test_handle_menu_operations(activated_account,
|
||||
cache_preferences,
|
||||
celery_session_worker,
|
||||
generic_ussd_session,
|
||||
init_database,
|
||||
init_cache,
|
||||
load_chain_spec,
|
||||
load_config,
|
||||
mock_account_creation_task_result,
|
||||
persisted_ussd_session,
|
||||
person_metadata,
|
||||
set_locale_files,
|
||||
setup_metadata_request_handler,
|
||||
setup_metadata_signer,
|
||||
task_uuid):
|
||||
# sourcery skip: extract-duplicate-method
|
||||
chain_str = Chain.spec.__str__()
|
||||
phone = phone_number()
|
||||
external_session_id = os.urandom(20).hex()
|
||||
valid_service_codes = load_config.get('USSD_SERVICE_CODE').split(",")
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
resp = handle_menu_operations(chain_str, external_session_id, phone, None, valid_service_codes[0], init_database, '4444')
|
||||
assert resp == translation_for('ussd.kenya.account_creation_prompt', preferred_language)
|
||||
cached_ussd_session = get_cached_data(external_session_id)
|
||||
ussd_session = json.loads(cached_ussd_session)
|
||||
assert ussd_session['msisdn'] == phone
|
||||
task_tracker = init_database.query(TaskTracker).filter_by(task_uuid=task_uuid).first()
|
||||
assert task_tracker.task_uuid == task_uuid
|
||||
cached_creation_task_uuid = get_cached_data(task_uuid)
|
||||
creation_task_uuid_data = json.loads(cached_creation_task_uuid)
|
||||
assert creation_task_uuid_data['status'] == 'PENDING'
|
||||
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
person_metadata_client = PersonMetadata(identifier)
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
request_mocker.register_uri('GET', person_metadata_client.url, status_code=200, reason='OK',
|
||||
json=person_metadata)
|
||||
person_metadata_client.query()
|
||||
external_session_id = os.urandom(20).hex()
|
||||
phone = activated_account.phone_number
|
||||
preferred_language = get_cached_preferred_language(activated_account.blockchain_address)
|
||||
persisted_ussd_session.state = 'enter_transaction_recipient'
|
||||
resp = handle_menu_operations(chain_str, external_session_id, phone, None, valid_service_codes[0], init_database, '1')
|
||||
assert resp == translation_for('ussd.kenya.enter_transaction_recipient', preferred_language)
|
||||
|
||||
62
apps/cic-ussd/tests/cic_ussd/processor/test_util.py
Normal file
62
apps/cic-ussd/tests/cic_ussd/processor/test_util.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# standard imports
|
||||
import datetime
|
||||
import json
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
from chainlib.hash import strip_0x
|
||||
from cic_types.models.person import get_contact_data_from_vcard
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.metadata import get_cached_preferred_language
|
||||
from cic_ussd.metadata import PersonMetadata
|
||||
from cic_ussd.processor.util import latest_input, parse_person_metadata, resume_last_ussd_session
|
||||
from cic_ussd.translation import translation_for
|
||||
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
@pytest.mark.parametrize('user_input, expected_value', [
|
||||
('1*9*6*7', '7'),
|
||||
('1', '1'),
|
||||
('', '')
|
||||
])
|
||||
def test_latest_input(user_input, expected_value):
|
||||
assert latest_input(user_input) == expected_value
|
||||
|
||||
|
||||
def test_parse_person_metadata(activated_account, cache_person_metadata, cache_preferences):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
person_metadata = PersonMetadata(identifier)
|
||||
cached_person_metadata = person_metadata.get_cached_metadata()
|
||||
person_metadata = json.loads(cached_person_metadata)
|
||||
preferred_language = get_cached_preferred_language(activated_account.blockchain_address)
|
||||
display_key = 'ussd.kenya.display_person_metadata'
|
||||
parsed_person_metadata = parse_person_metadata(cached_person_metadata,
|
||||
display_key,
|
||||
preferred_language)
|
||||
contact_data = get_contact_data_from_vcard(person_metadata.get('vcard'))
|
||||
full_name = f'{contact_data.get("given")} {contact_data.get("family")}'
|
||||
date_of_birth = person_metadata.get('date_of_birth')
|
||||
year_of_birth = date_of_birth.get('year')
|
||||
present_year = datetime.datetime.now().year
|
||||
age = present_year - year_of_birth
|
||||
gender = person_metadata.get('gender')
|
||||
products = ', '.join(person_metadata.get('products'))
|
||||
location = person_metadata.get('location').get('area_name')
|
||||
assert parsed_person_metadata == translation_for(key=display_key,
|
||||
preferred_language=preferred_language,
|
||||
full_name=full_name,
|
||||
age=age,
|
||||
gender=gender,
|
||||
location=location,
|
||||
products=products)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('last_state, expected_menu_name', [
|
||||
('account_creation_prompt', 'start'),
|
||||
('enter_transaction_recipient', 'enter_transaction_recipient')
|
||||
])
|
||||
def test_resume_last_ussd_session(expected_menu_name, last_state, load_ussd_menu):
|
||||
assert resume_last_ussd_session(last_state).get('name') == expected_menu_name
|
||||
@@ -0,0 +1,72 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# external imports
|
||||
|
||||
# local imports
|
||||
from cic_ussd.cache import get_cached_data
|
||||
from cic_ussd.db.models.ussd_session import UssdSession as PersistedUssdSession
|
||||
from cic_ussd.menu.ussd_menu import UssdMenu
|
||||
from cic_ussd.session.ussd_session import (create_or_update_session,
|
||||
create_ussd_session,
|
||||
persist_ussd_session,
|
||||
save_session_data,
|
||||
update_ussd_session,
|
||||
UssdSession)
|
||||
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def test_ussd_session(cached_ussd_session, load_ussd_menu):
|
||||
assert UssdMenu.find_by_name(name='initial_language_selection').get('name') == cached_ussd_session.state
|
||||
cached_ussd_session.set_data('some_key', 'some_value')
|
||||
assert cached_ussd_session.get_data('some_key') == 'some_value'
|
||||
assert isinstance(cached_ussd_session, UssdSession)
|
||||
assert isinstance(cached_ussd_session.to_json(), dict)
|
||||
|
||||
|
||||
def test_create_or_update_session(activated_account_ussd_session, cached_ussd_session, init_cache, init_database):
|
||||
external_session_id = activated_account_ussd_session.get('external_session_id')
|
||||
ussd_session = create_or_update_session(external_session_id=external_session_id,
|
||||
service_code=activated_account_ussd_session.get('service_code'),
|
||||
msisdn=activated_account_ussd_session.get('msisdn'),
|
||||
user_input=activated_account_ussd_session.get('user_input'),
|
||||
state=activated_account_ussd_session.get('state'),
|
||||
session=init_database)
|
||||
cached_ussd_session = get_cached_data(external_session_id)
|
||||
assert json.loads(cached_ussd_session).get('external_session_id') == ussd_session.external_session_id
|
||||
|
||||
|
||||
def test_update_ussd_session(activated_account_ussd_session, cached_ussd_session, init_cache, load_ussd_menu):
|
||||
ussd_session = create_ussd_session(external_session_id=activated_account_ussd_session.get('external_session_id'),
|
||||
service_code=activated_account_ussd_session.get('service_code'),
|
||||
msisdn=activated_account_ussd_session.get('msisdn'),
|
||||
user_input=activated_account_ussd_session.get('user_input'),
|
||||
state=activated_account_ussd_session.get('state'))
|
||||
assert ussd_session.user_input == activated_account_ussd_session.get('user_input')
|
||||
assert ussd_session.state == activated_account_ussd_session.get('state')
|
||||
ussd_session = update_ussd_session(ussd_session=ussd_session, user_input='1*2', state='initial_pin_entry')
|
||||
assert ussd_session.user_input == '1*2'
|
||||
assert ussd_session.state == 'initial_pin_entry'
|
||||
|
||||
|
||||
def test_save_session_data(activated_account_ussd_session,
|
||||
cached_ussd_session,
|
||||
celery_session_worker,
|
||||
init_cache,
|
||||
init_database,
|
||||
ussd_session_data):
|
||||
external_session_id = activated_account_ussd_session.get('external_session_id')
|
||||
ussd_session = get_cached_data(external_session_id)
|
||||
ussd_session = json.loads(ussd_session)
|
||||
assert ussd_session.get('data') == {}
|
||||
save_session_data(
|
||||
queue='cic-ussd',
|
||||
data=ussd_session_data,
|
||||
ussd_session=cached_ussd_session.to_json(),
|
||||
session=init_database
|
||||
)
|
||||
ussd_session = get_cached_data(external_session_id)
|
||||
ussd_session = json.loads(ussd_session)
|
||||
assert ussd_session.get('data') == ussd_session_data
|
||||
@@ -1,11 +0,0 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# local imports
|
||||
from cic_ussd.menu.ussd_menu import UssdMenu
|
||||
from cic_ussd.session.ussd_session import UssdSession
|
||||
|
||||
|
||||
def test_ussd_session(load_ussd_menu, get_in_redis_ussd_session):
|
||||
ussd_session = get_in_redis_ussd_session
|
||||
assert UssdMenu.find_by_name(name='initial_language_selection').get('name') == ussd_session.state
|
||||
@@ -0,0 +1,154 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
import requests_mock
|
||||
from chainlib.hash import strip_0x
|
||||
from cic_types.models.person import Person, get_contact_data_from_vcard
|
||||
|
||||
# local imports
|
||||
from cic_ussd.cache import get_cached_data
|
||||
from cic_ussd.account.maps import gender
|
||||
from cic_ussd.account.metadata import get_cached_preferred_language
|
||||
from cic_ussd.db.enum import AccountStatus
|
||||
from cic_ussd.metadata import PreferencesMetadata
|
||||
from cic_ussd.state_machine.logic.account import (change_preferred_language,
|
||||
edit_user_metadata_attribute,
|
||||
parse_gender,
|
||||
parse_person_metadata,
|
||||
save_complete_person_metadata,
|
||||
save_metadata_attribute_to_session_data,
|
||||
update_account_status_to_active)
|
||||
from cic_ussd.translation import translation_for
|
||||
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
@pytest.mark.parametrize('user_input, expected_preferred_language', [
|
||||
('1', 'en'),
|
||||
('2', 'sw')
|
||||
])
|
||||
def test_change_preferred_language(activated_account,
|
||||
celery_session_worker,
|
||||
expected_preferred_language,
|
||||
init_database,
|
||||
generic_ussd_session,
|
||||
mock_response,
|
||||
preferences,
|
||||
setup_metadata_request_handler,
|
||||
user_input):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
preferences_metadata_client = PreferencesMetadata(identifier)
|
||||
with requests_mock.Mocker(real_http=False) as requests_mocker:
|
||||
requests_mocker.register_uri(
|
||||
'POST', preferences_metadata_client.url, status_code=200, reason='OK', json=mock_response
|
||||
)
|
||||
state_machine_data = (user_input, generic_ussd_session, activated_account, init_database)
|
||||
res = change_preferred_language(state_machine_data)
|
||||
init_database.commit()
|
||||
assert res.id is not None
|
||||
assert activated_account.preferred_language == expected_preferred_language
|
||||
|
||||
|
||||
@pytest.mark.parametrize('user_input', [
|
||||
'1',
|
||||
'2',
|
||||
'3'
|
||||
])
|
||||
def test_parse_gender(activated_account, cache_preferences, user_input):
|
||||
preferred_language = get_cached_preferred_language(activated_account.blockchain_address)
|
||||
parsed_gender = parse_gender(activated_account, user_input)
|
||||
r_user_input = gender().get(user_input)
|
||||
assert parsed_gender == translation_for(f'helpers.{r_user_input}', preferred_language)
|
||||
|
||||
|
||||
def test_parse_person_metadata(activated_account, load_chain_spec, raw_person_metadata):
|
||||
parsed_user_metadata = parse_person_metadata(activated_account, raw_person_metadata)
|
||||
person = Person()
|
||||
user_metadata = person.deserialize(parsed_user_metadata)
|
||||
assert parsed_user_metadata == user_metadata.serialize()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("current_state, expected_key, expected_result, user_input", [
|
||||
("enter_given_name", "given_name", "John", "John"),
|
||||
("enter_family_name", "family_name", "Doe", "Doe"),
|
||||
("enter_location", "location", "Kangemi", "Kangemi"),
|
||||
("enter_products", "products", "Mandazi", "Mandazi"),
|
||||
])
|
||||
def test_save_metadata_attribute_to_session_data(activated_account,
|
||||
cached_ussd_session,
|
||||
celery_session_worker,
|
||||
current_state,
|
||||
expected_key,
|
||||
expected_result,
|
||||
init_cache,
|
||||
init_database,
|
||||
load_chain_spec,
|
||||
set_locale_files,
|
||||
persisted_ussd_session,
|
||||
user_input):
|
||||
persisted_ussd_session.state = current_state
|
||||
ussd_session = persisted_ussd_session.to_json()
|
||||
state_machine_data = (user_input, ussd_session, activated_account, init_database)
|
||||
ussd_session_in_cache = get_cached_data(cached_ussd_session.external_session_id)
|
||||
ussd_session_in_cache = json.loads(ussd_session_in_cache)
|
||||
assert ussd_session_in_cache.get('data') == {}
|
||||
ussd_session['state'] = current_state
|
||||
save_metadata_attribute_to_session_data(state_machine_data)
|
||||
cached_ussd_session = get_cached_data(cached_ussd_session.external_session_id)
|
||||
cached_ussd_session = json.loads(cached_ussd_session)
|
||||
assert cached_ussd_session.get('data')[expected_key] == expected_result
|
||||
|
||||
|
||||
def test_update_account_status_to_active(generic_ussd_session, init_database, pending_account):
|
||||
state_machine_data = ('', generic_ussd_session, pending_account, init_database)
|
||||
assert pending_account.get_status(init_database) == AccountStatus.PENDING.name
|
||||
update_account_status_to_active(state_machine_data)
|
||||
assert pending_account.get_status(init_database) == AccountStatus.ACTIVE.name
|
||||
|
||||
|
||||
def test_save_complete_person_metadata(activated_account,
|
||||
cached_ussd_session,
|
||||
celery_session_worker,
|
||||
init_database,
|
||||
load_chain_spec,
|
||||
mocker,
|
||||
person_metadata,
|
||||
raw_person_metadata):
|
||||
ussd_session = get_cached_data(cached_ussd_session.external_session_id)
|
||||
ussd_session = json.loads(ussd_session)
|
||||
ussd_session['data'] = raw_person_metadata
|
||||
metadata = parse_person_metadata(activated_account, raw_person_metadata)
|
||||
state_machine_data = ('', ussd_session, activated_account, init_database)
|
||||
mocked_create_metadata_task = mocker.patch('cic_ussd.tasks.metadata.create_person_metadata.apply_async')
|
||||
save_complete_person_metadata(state_machine_data=state_machine_data)
|
||||
mocked_create_metadata_task.assert_called_with(
|
||||
(activated_account.blockchain_address, metadata), {}, queue='cic-ussd')
|
||||
|
||||
|
||||
def test_edit_user_metadata_attribute(activated_account,
|
||||
cache_person_metadata,
|
||||
cached_ussd_session,
|
||||
celery_session_worker,
|
||||
init_cache,
|
||||
init_database,
|
||||
load_chain_spec,
|
||||
mocker,
|
||||
person_metadata):
|
||||
ussd_session = get_cached_data(cached_ussd_session.external_session_id)
|
||||
ussd_session = json.loads(ussd_session)
|
||||
assert person_metadata['location']['area_name'] == 'kayaba'
|
||||
ussd_session['data'] = {'location': 'nairobi'}
|
||||
contact_data = get_contact_data_from_vcard(person_metadata.get('vcard'))
|
||||
phone_number = contact_data.get('tel')
|
||||
activated_account.phone_number = phone_number
|
||||
state_machine_data = ('', ussd_session, activated_account, init_database)
|
||||
mocked_edit_metadata = mocker.patch('cic_ussd.tasks.metadata.create_person_metadata.apply_async')
|
||||
edit_user_metadata_attribute(state_machine_data)
|
||||
person_metadata['date_registered'] = int(activated_account.created.replace().timestamp())
|
||||
person_metadata['location']['area_name'] = 'nairobi'
|
||||
mocked_edit_metadata.assert_called_with(
|
||||
(activated_account.blockchain_address, person_metadata), {}, queue='cic-ussd')
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# local imports
|
||||
from cic_ussd.state_machine.logic.menu import (menu_one_selected,
|
||||
menu_two_selected,
|
||||
menu_three_selected,
|
||||
menu_four_selected)
|
||||
|
||||
|
||||
def test_menu_selection(create_pending_user, create_in_db_ussd_session):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
assert menu_one_selected(('1', serialized_in_db_ussd_session, create_pending_user)) is True
|
||||
assert menu_one_selected(('x', serialized_in_db_ussd_session, create_pending_user)) is False
|
||||
|
||||
assert menu_two_selected(('2', serialized_in_db_ussd_session, create_pending_user)) is True
|
||||
assert menu_two_selected(('1', serialized_in_db_ussd_session, create_pending_user)) is False
|
||||
|
||||
assert menu_three_selected(('3', serialized_in_db_ussd_session, create_pending_user)) is True
|
||||
assert menu_three_selected(('4', serialized_in_db_ussd_session, create_pending_user)) is False
|
||||
|
||||
assert menu_four_selected(('4', serialized_in_db_ussd_session, create_pending_user)) is True
|
||||
assert menu_four_selected(('d', serialized_in_db_ussd_session, create_pending_user)) is False
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# standard imports
|
||||
|
||||
# external imports
|
||||
|
||||
# local imports
|
||||
from cic_ussd.state_machine.logic.menu import (menu_one_selected,
|
||||
menu_two_selected,
|
||||
menu_three_selected,
|
||||
menu_four_selected,
|
||||
menu_five_selected,
|
||||
menu_six_selected,
|
||||
menu_zero_zero_selected,
|
||||
menu_ninety_nine_selected)
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def test_menu_selection(init_database, pending_account, persisted_ussd_session):
|
||||
ussd_session = persisted_ussd_session.to_json()
|
||||
assert menu_one_selected(('1', ussd_session, pending_account, init_database)) is True
|
||||
assert menu_one_selected(('x', ussd_session, pending_account, init_database)) is False
|
||||
assert menu_two_selected(('2', ussd_session, pending_account, init_database)) is True
|
||||
assert menu_two_selected(('1', ussd_session, pending_account, init_database)) is False
|
||||
assert menu_three_selected(('3', ussd_session, pending_account, init_database)) is True
|
||||
assert menu_three_selected(('4', ussd_session, pending_account, init_database)) is False
|
||||
assert menu_four_selected(('4', ussd_session, pending_account, init_database)) is True
|
||||
assert menu_four_selected(('d', ussd_session, pending_account, init_database)) is False
|
||||
assert menu_five_selected(('5', ussd_session, pending_account, init_database)) is True
|
||||
assert menu_five_selected(('e', ussd_session, pending_account, init_database)) is False
|
||||
assert menu_six_selected(('6', ussd_session, pending_account, init_database)) is True
|
||||
assert menu_six_selected(('8', ussd_session, pending_account, init_database)) is False
|
||||
assert menu_zero_zero_selected(('00', ussd_session, pending_account, init_database)) is True
|
||||
assert menu_zero_zero_selected(('/', ussd_session, pending_account, init_database)) is False
|
||||
assert menu_ninety_nine_selected(('99', ussd_session, pending_account, init_database)) is True
|
||||
assert menu_ninety_nine_selected(('d', ussd_session, pending_account, init_database)) is False
|
||||
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
# standards imports
|
||||
import json
|
||||
|
||||
# third party imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.encoder import check_password_hash, create_password_hash
|
||||
from cic_ussd.state_machine.logic.pin import (complete_pin_change,
|
||||
is_valid_pin,
|
||||
is_valid_new_pin,
|
||||
is_authorized_pin,
|
||||
is_blocked_pin,
|
||||
pins_match,
|
||||
save_initial_pin_to_session_data)
|
||||
|
||||
|
||||
def test_complete_pin_change(init_database, create_pending_user, create_in_db_ussd_session):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = ('1212', serialized_in_db_ussd_session, create_pending_user)
|
||||
assert create_pending_user.password_hash is None
|
||||
create_in_db_ussd_session.set_data(key='initial_pin', session=init_database, value=create_password_hash('1212'))
|
||||
complete_pin_change(state_machine_data)
|
||||
assert create_pending_user.password_hash is not None
|
||||
assert create_pending_user.verify_password(password='1212') is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize('user_input, expected', [
|
||||
('4562', True),
|
||||
('jksu', False),
|
||||
('ij45', False),
|
||||
])
|
||||
def test_is_valid_pin(create_pending_user, create_in_db_ussd_session, user_input, expected):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = (user_input, serialized_in_db_ussd_session, create_pending_user)
|
||||
assert is_valid_pin(state_machine_data) is expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize('user_input, expected', [
|
||||
('1212', True),
|
||||
('0000', False)
|
||||
])
|
||||
def test_pins_match(init_database, create_pending_user, create_in_db_ussd_session, user_input, expected):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = (user_input, serialized_in_db_ussd_session, create_pending_user)
|
||||
create_in_db_ussd_session.set_data(key='initial_pin', session=init_database, value=create_password_hash(user_input))
|
||||
assert pins_match(state_machine_data) is True
|
||||
|
||||
|
||||
def test_save_initial_pin_to_session_data(create_pending_user,
|
||||
create_in_redis_ussd_session,
|
||||
create_in_db_ussd_session,
|
||||
celery_session_worker):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = ('1212', serialized_in_db_ussd_session, create_pending_user)
|
||||
save_initial_pin_to_session_data(state_machine_data)
|
||||
external_session_id = create_in_db_ussd_session.external_session_id
|
||||
in_memory_ussd_session = create_in_redis_ussd_session.get(external_session_id)
|
||||
in_memory_ussd_session = json.loads(in_memory_ussd_session)
|
||||
assert check_password_hash(
|
||||
password='1212', hashed_password=in_memory_ussd_session.get('session_data')['initial_pin'])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('user_input, expected_result', [
|
||||
('1212', False),
|
||||
('0000', True)
|
||||
])
|
||||
def test_is_authorized_pin(create_activated_user, create_in_db_ussd_session, expected_result, user_input):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = (user_input, serialized_in_db_ussd_session, create_activated_user)
|
||||
assert is_authorized_pin(state_machine_data=state_machine_data) is expected_result
|
||||
|
||||
|
||||
def test_is_not_blocked_pin(create_activated_user, create_in_db_ussd_session):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = ('', serialized_in_db_ussd_session, create_activated_user)
|
||||
assert is_blocked_pin(state_machine_data=state_machine_data) is False
|
||||
|
||||
|
||||
def test_is_blocked_pin(create_pin_blocked_user, create_in_db_ussd_session):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
alt_state_machine_data = ('user_input', serialized_in_db_ussd_session, create_pin_blocked_user)
|
||||
assert is_blocked_pin(state_machine_data=alt_state_machine_data) is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize('user_input, expected_result', [
|
||||
('1212', True),
|
||||
('0000', False)
|
||||
])
|
||||
def test_is_valid_new_pin(create_activated_user, create_in_db_ussd_session, expected_result, user_input):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = (user_input, serialized_in_db_ussd_session, create_activated_user)
|
||||
assert is_valid_new_pin(state_machine_data=state_machine_data) is expected_result
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.cache import get_cached_data
|
||||
from cic_ussd.encoder import check_password_hash, create_password_hash
|
||||
from cic_ussd.state_machine.logic.pin import (complete_pin_change,
|
||||
is_valid_pin,
|
||||
is_valid_new_pin,
|
||||
is_authorized_pin,
|
||||
is_blocked_pin,
|
||||
is_locked_account,
|
||||
pins_match,
|
||||
save_initial_pin_to_session_data)
|
||||
|
||||
|
||||
def test_complete_pin_change(activated_account, cached_ussd_session, init_database):
|
||||
state_machine_data = ('1212', cached_ussd_session.to_json(), activated_account, init_database)
|
||||
assert activated_account.password_hash is not None
|
||||
cached_ussd_session.set_data('initial_pin', create_password_hash('1212'))
|
||||
complete_pin_change(state_machine_data)
|
||||
assert activated_account.verify_password('1212') is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize('user_input, expected', [
|
||||
('4562', True),
|
||||
('jksu', False),
|
||||
('ij45', False),
|
||||
])
|
||||
def test_is_valid_pin(activated_account, expected, generic_ussd_session, init_database, user_input):
|
||||
state_machine_data = (user_input, generic_ussd_session, activated_account, init_database)
|
||||
assert is_valid_pin(state_machine_data) is expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize('user_input', [
|
||||
'1212',
|
||||
'0000'
|
||||
])
|
||||
def test_pins_match(activated_account, cached_ussd_session, init_cache, init_database, user_input):
|
||||
state_machine_data = (user_input, cached_ussd_session.to_json(), activated_account, init_database)
|
||||
cached_ussd_session.set_data('initial_pin', create_password_hash(user_input))
|
||||
assert pins_match(state_machine_data) is True
|
||||
|
||||
|
||||
def test_save_initial_pin_to_session_data(activated_account,
|
||||
cached_ussd_session,
|
||||
celery_session_worker,
|
||||
init_cache,
|
||||
init_database,
|
||||
persisted_ussd_session,
|
||||
set_fernet_key):
|
||||
state_machine_data = ('1212', cached_ussd_session.to_json(), activated_account, init_database)
|
||||
save_initial_pin_to_session_data(state_machine_data)
|
||||
ussd_session = get_cached_data(cached_ussd_session.external_session_id)
|
||||
ussd_session = json.loads(ussd_session)
|
||||
assert check_password_hash('1212', ussd_session.get('data')['initial_pin'])
|
||||
cached_ussd_session.set_data('some_key', 'some_value')
|
||||
state_machine_data = ('1212', cached_ussd_session.to_json(), activated_account, init_database)
|
||||
save_initial_pin_to_session_data(state_machine_data)
|
||||
ussd_session = get_cached_data(cached_ussd_session.external_session_id)
|
||||
ussd_session = json.loads(ussd_session)
|
||||
assert ussd_session.get('data')['some_key'] == 'some_value'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('user_input, expected_result', [
|
||||
('1212', False),
|
||||
('0000', True)
|
||||
])
|
||||
def test_is_authorized_pin(activated_account, cached_ussd_session, expected_result, init_database, user_input):
|
||||
state_machine_data = (user_input, cached_ussd_session.to_json(), activated_account, init_database)
|
||||
assert is_authorized_pin(state_machine_data) is expected_result
|
||||
|
||||
|
||||
def test_is_not_blocked_pin(activated_account, cached_ussd_session, init_database):
|
||||
state_machine_data = ('', cached_ussd_session.to_json(), activated_account, init_database)
|
||||
assert is_blocked_pin(state_machine_data) is False
|
||||
|
||||
|
||||
def test_is_blocked_pin(cached_ussd_session, init_database, pin_blocked_account):
|
||||
state_machine_data = ('user_input', cached_ussd_session, pin_blocked_account, init_database)
|
||||
assert is_blocked_pin(state_machine_data) is True
|
||||
|
||||
|
||||
def test_is_locked_account(activated_account, generic_ussd_session, init_database, pin_blocked_account):
|
||||
state_machine_data = ('', generic_ussd_session, activated_account, init_database)
|
||||
assert is_locked_account(state_machine_data) is False
|
||||
state_machine_data = ('', generic_ussd_session, pin_blocked_account, init_database)
|
||||
assert is_locked_account(state_machine_data) is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize('user_input, expected_result', [
|
||||
('1212', True),
|
||||
('0000', False)
|
||||
])
|
||||
def test_is_valid_new_pin(activated_account, cached_ussd_session, expected_result, init_database, user_input):
|
||||
state_machine_data = (user_input, cached_ussd_session.to_json(), activated_account, init_database)
|
||||
assert is_valid_new_pin(state_machine_data) is expected_result
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
# standard imports
|
||||
|
||||
# third-party imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.state_machine.logic.sms import (send_terms_to_user_if_required,
|
||||
process_mini_statement_request,
|
||||
upsell_unregistered_recipient)
|
||||
|
||||
|
||||
def test_send_terms_to_user_if_required(caplog,
|
||||
create_in_db_ussd_session,
|
||||
create_activated_user):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = ('', serialized_in_db_ussd_session, create_activated_user)
|
||||
send_terms_to_user_if_required(state_machine_data=state_machine_data)
|
||||
assert 'Requires integration to cic-notify.' in caplog.text
|
||||
|
||||
|
||||
def test_process_mini_statement_request(caplog,
|
||||
create_in_db_ussd_session,
|
||||
create_activated_user):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = ('', serialized_in_db_ussd_session, create_activated_user)
|
||||
process_mini_statement_request(state_machine_data=state_machine_data)
|
||||
assert 'Requires integration to cic-notify.' in caplog.text
|
||||
|
||||
|
||||
def test_upsell_unregistered_recipient(caplog,
|
||||
create_in_db_ussd_session,
|
||||
create_activated_user):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = ('', serialized_in_db_ussd_session, create_activated_user)
|
||||
upsell_unregistered_recipient(state_machine_data=state_machine_data)
|
||||
assert 'Requires integration to cic-notify.' in caplog.text
|
||||
@@ -0,0 +1,42 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# external imports
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.metadata import get_cached_preferred_language
|
||||
from cic_ussd.account.tokens import get_default_token_symbol
|
||||
from cic_ussd.cache import get_cached_data
|
||||
from cic_ussd.phone_number import Support
|
||||
from cic_ussd.state_machine.logic.sms import upsell_unregistered_recipient
|
||||
from cic_ussd.translation import translation_for
|
||||
|
||||
|
||||
# tests imports
|
||||
|
||||
|
||||
def test_upsell_unregistered_recipient(activated_account,
|
||||
cache_default_token_data,
|
||||
cache_preferences,
|
||||
cached_ussd_session,
|
||||
init_database,
|
||||
load_support_phone,
|
||||
mock_notifier_api,
|
||||
set_locale_files,
|
||||
valid_recipient):
|
||||
cached_ussd_session.set_data('recipient_phone_number', valid_recipient.phone_number)
|
||||
state_machine_data = ('', cached_ussd_session.to_json(), activated_account, init_database)
|
||||
upsell_unregistered_recipient(state_machine_data)
|
||||
ussd_session = get_cached_data(cached_ussd_session.external_session_id)
|
||||
ussd_session = json.loads(ussd_session)
|
||||
phone_number = ussd_session.get('data')['recipient_phone_number']
|
||||
preferred_language = get_cached_preferred_language(activated_account.blockchain_address)
|
||||
token_symbol = get_default_token_symbol()
|
||||
tx_sender_information = activated_account.standard_metadata_id()
|
||||
message = translation_for('sms.upsell_unregistered_recipient',
|
||||
preferred_language,
|
||||
tx_sender_information=tx_sender_information,
|
||||
token_symbol=token_symbol,
|
||||
support_phone=Support.phone_number)
|
||||
assert mock_notifier_api.get('message') == message
|
||||
assert mock_notifier_api.get('recipient') == phone_number
|
||||
@@ -1,111 +0,0 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# third-party imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.state_machine.logic.transaction import (has_sufficient_balance,
|
||||
is_valid_recipient,
|
||||
is_valid_transaction_amount,
|
||||
process_transaction_request,
|
||||
save_recipient_phone_to_session_data,
|
||||
save_transaction_amount_to_session_data)
|
||||
from cic_ussd.redis import InMemoryStore
|
||||
|
||||
|
||||
@pytest.mark.parametrize("amount, expected_result", [
|
||||
('50', True),
|
||||
('', False)
|
||||
])
|
||||
def test_is_valid_transaction_amount(create_activated_user, create_in_db_ussd_session, amount, expected_result):
|
||||
state_machine_data = (amount, create_in_db_ussd_session, create_activated_user)
|
||||
validity = is_valid_transaction_amount(state_machine_data=state_machine_data)
|
||||
assert validity == expected_result
|
||||
|
||||
|
||||
def test_save_recipient_phone_to_session_data(create_activated_user,
|
||||
create_in_db_ussd_session,
|
||||
celery_session_worker,
|
||||
create_in_redis_ussd_session,
|
||||
init_database):
|
||||
phone_number = '+254712345678'
|
||||
in_memory_ussd_session = InMemoryStore.cache.get('AT974186')
|
||||
in_memory_ussd_session = json.loads(in_memory_ussd_session)
|
||||
|
||||
assert in_memory_ussd_session.get('session_data') == {}
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = (phone_number, serialized_in_db_ussd_session, create_activated_user)
|
||||
save_recipient_phone_to_session_data(state_machine_data=state_machine_data)
|
||||
|
||||
in_memory_ussd_session = InMemoryStore.cache.get('AT974186')
|
||||
in_memory_ussd_session = json.loads(in_memory_ussd_session)
|
||||
|
||||
assert in_memory_ussd_session.get('session_data')['recipient_phone_number'] == phone_number
|
||||
|
||||
|
||||
def test_save_transaction_amount_to_session_data(create_activated_user,
|
||||
create_in_db_ussd_session,
|
||||
celery_session_worker,
|
||||
create_in_redis_ussd_session,
|
||||
init_database):
|
||||
transaction_amount = '100'
|
||||
in_memory_ussd_session = InMemoryStore.cache.get('AT974186')
|
||||
in_memory_ussd_session = json.loads(in_memory_ussd_session)
|
||||
|
||||
assert in_memory_ussd_session.get('session_data') == {}
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = (transaction_amount, serialized_in_db_ussd_session, create_activated_user)
|
||||
save_transaction_amount_to_session_data(state_machine_data=state_machine_data)
|
||||
|
||||
in_memory_ussd_session = InMemoryStore.cache.get('AT974186')
|
||||
in_memory_ussd_session = json.loads(in_memory_ussd_session)
|
||||
|
||||
assert in_memory_ussd_session.get('session_data')['transaction_amount'] == transaction_amount
|
||||
|
||||
|
||||
@pytest.mark.parametrize("test_value, expected_result", [
|
||||
('45', True),
|
||||
('75', False)
|
||||
])
|
||||
def test_has_sufficient_balance(mock_balance,
|
||||
create_in_db_ussd_session,
|
||||
create_valid_tx_sender,
|
||||
expected_result,
|
||||
test_value):
|
||||
mock_balance(60)
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = (test_value, serialized_in_db_ussd_session, create_valid_tx_sender)
|
||||
result = has_sufficient_balance(state_machine_data=state_machine_data)
|
||||
assert result == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize("test_value, expected_result", [
|
||||
('+25498765432', True),
|
||||
('+25498765433', False)
|
||||
])
|
||||
def test_is_valid_recipient(create_in_db_ussd_session,
|
||||
create_valid_tx_recipient,
|
||||
create_valid_tx_sender,
|
||||
expected_result,
|
||||
test_value):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = (test_value, serialized_in_db_ussd_session, create_valid_tx_sender)
|
||||
result = is_valid_recipient(state_machine_data=state_machine_data)
|
||||
assert result == expected_result
|
||||
|
||||
|
||||
def test_process_transaction_request(create_valid_tx_recipient,
|
||||
create_valid_tx_sender,
|
||||
load_config,
|
||||
mock_outgoing_transactions,
|
||||
setup_chain_spec,
|
||||
ussd_session_data):
|
||||
ussd_session_data['session_data'] = {
|
||||
'recipient_phone_number': create_valid_tx_recipient.phone_number,
|
||||
'transaction_amount': '50'
|
||||
}
|
||||
state_machine_data = ('', ussd_session_data, create_valid_tx_sender)
|
||||
process_transaction_request(state_machine_data=state_machine_data)
|
||||
assert mock_outgoing_transactions[0].get('amount') == 50.0
|
||||
assert mock_outgoing_transactions[0].get('token_symbol') == 'SRF'
|
||||
@@ -0,0 +1,112 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
import requests_mock
|
||||
from chainlib.hash import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.transaction import to_wei
|
||||
from cic_ussd.cache import get_cached_data
|
||||
from cic_ussd.metadata import PersonMetadata
|
||||
from cic_ussd.state_machine.logic.transaction import (is_valid_recipient,
|
||||
is_valid_transaction_amount,
|
||||
has_sufficient_balance,
|
||||
process_transaction_request,
|
||||
retrieve_recipient_metadata,
|
||||
save_recipient_phone_to_session_data,
|
||||
save_transaction_amount_to_session_data)
|
||||
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def test_is_valid_recipient(activated_account,
|
||||
generic_ussd_session,
|
||||
init_database,
|
||||
load_e164_region,
|
||||
pending_account,
|
||||
valid_recipient):
|
||||
state_machine = ('0112365478', generic_ussd_session, valid_recipient, init_database)
|
||||
assert is_valid_recipient(state_machine) is False
|
||||
state_machine = (pending_account.phone_number, generic_ussd_session, valid_recipient, init_database)
|
||||
assert is_valid_recipient(state_machine) is False
|
||||
state_machine = (valid_recipient.phone_number, generic_ussd_session, activated_account, init_database)
|
||||
assert is_valid_recipient(state_machine) is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("amount, expected_result", [
|
||||
('50', True),
|
||||
('', False)
|
||||
])
|
||||
def test_is_valid_transaction_amount(activated_account, amount, expected_result, generic_ussd_session, init_database):
|
||||
state_machine_data = (amount, generic_ussd_session, activated_account, init_database)
|
||||
assert is_valid_transaction_amount(state_machine_data) is expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value, expected_result", [
|
||||
('45', True),
|
||||
('75', False)
|
||||
])
|
||||
def test_has_sufficient_balance(activated_account,
|
||||
cache_balances,
|
||||
expected_result,
|
||||
generic_ussd_session,
|
||||
init_database,
|
||||
value):
|
||||
state_machine_data = (value, generic_ussd_session, activated_account, init_database)
|
||||
assert has_sufficient_balance(state_machine_data=state_machine_data) == expected_result
|
||||
|
||||
|
||||
def test_process_transaction_request(activated_account,
|
||||
cache_default_token_data,
|
||||
cached_ussd_session,
|
||||
celery_session_worker,
|
||||
init_cache,
|
||||
init_database,
|
||||
load_chain_spec,
|
||||
load_config,
|
||||
mock_transfer_api,
|
||||
valid_recipient):
|
||||
cached_ussd_session.set_data('recipient_phone_number', valid_recipient.phone_number)
|
||||
cached_ussd_session.set_data('transaction_amount', '50')
|
||||
ussd_session = get_cached_data(cached_ussd_session.external_session_id)
|
||||
ussd_session = json.loads(ussd_session)
|
||||
state_machine_data = ('', ussd_session, activated_account, init_database)
|
||||
process_transaction_request(state_machine_data)
|
||||
assert mock_transfer_api['from_address'] == activated_account.blockchain_address
|
||||
assert mock_transfer_api['to_address'] == valid_recipient.blockchain_address
|
||||
assert mock_transfer_api['value'] == to_wei(50)
|
||||
assert mock_transfer_api['token_symbol'] == load_config.get('TEST_TOKEN_SYMBOL')
|
||||
|
||||
|
||||
def test_retrieve_recipient_metadata(activated_account,
|
||||
generic_ussd_session,
|
||||
init_database,
|
||||
load_chain_spec,
|
||||
mocker,
|
||||
valid_recipient):
|
||||
state_machine_data = (valid_recipient.phone_number, generic_ussd_session, activated_account, init_database)
|
||||
mocked_query_metadata = mocker.patch('cic_ussd.tasks.metadata.query_person_metadata.apply_async')
|
||||
retrieve_recipient_metadata(state_machine_data)
|
||||
mocked_query_metadata.assert_called_with((valid_recipient.blockchain_address, ), {}, queue='cic-ussd')
|
||||
|
||||
|
||||
def test_transaction_information_to_session_data(activated_account,
|
||||
cached_ussd_session,
|
||||
init_cache,
|
||||
init_database,
|
||||
load_e164_region,
|
||||
valid_recipient):
|
||||
assert cached_ussd_session.to_json()['data'] == {}
|
||||
state_machine_data = (valid_recipient.phone_number, cached_ussd_session.to_json(), activated_account, init_database)
|
||||
save_recipient_phone_to_session_data(state_machine_data)
|
||||
ussd_session = get_cached_data(cached_ussd_session.external_session_id)
|
||||
ussd_session = json.loads(ussd_session)
|
||||
assert ussd_session.get('data')['recipient_phone_number'] == valid_recipient.phone_number
|
||||
state_machine_data = ('25', cached_ussd_session.to_json(), activated_account, init_database)
|
||||
save_transaction_amount_to_session_data(state_machine_data)
|
||||
ussd_session = get_cached_data(cached_ussd_session.external_session_id)
|
||||
ussd_session = json.loads(ussd_session)
|
||||
assert ussd_session.get('data')['transaction_amount'] == '25'
|
||||
@@ -1,155 +0,0 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# third-party-imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.chain import Chain
|
||||
from cic_ussd.redis import InMemoryStore
|
||||
from cic_ussd.state_machine.logic.user import (
|
||||
change_preferred_language_to_en,
|
||||
change_preferred_language_to_sw,
|
||||
edit_user_metadata_attribute,
|
||||
format_user_metadata,
|
||||
get_user_metadata,
|
||||
save_complete_user_metadata,
|
||||
process_gender_user_input,
|
||||
save_metadata_attribute_to_session_data,
|
||||
update_account_status_to_active)
|
||||
|
||||
|
||||
def test_change_preferred_language(create_pending_user, create_in_db_ussd_session):
|
||||
state_machine_data = ('', create_in_db_ussd_session, create_pending_user)
|
||||
assert create_pending_user.preferred_language is None
|
||||
change_preferred_language_to_en(state_machine_data)
|
||||
assert create_pending_user.preferred_language == 'en'
|
||||
change_preferred_language_to_sw(state_machine_data)
|
||||
assert create_pending_user.preferred_language == 'sw'
|
||||
|
||||
|
||||
def test_update_account_status_to_active(create_pending_user, create_in_db_ussd_session):
|
||||
state_machine_data = ('', create_in_db_ussd_session, create_pending_user)
|
||||
update_account_status_to_active(state_machine_data)
|
||||
assert create_pending_user.get_account_status() == 'ACTIVE'
|
||||
|
||||
|
||||
@pytest.mark.parametrize("current_state, expected_key, expected_result, user_input", [
|
||||
("enter_given_name", "given_name", "John", "John"),
|
||||
("enter_family_name", "family_name", "Doe", "Doe"),
|
||||
("enter_gender", "gender", "Male", "1"),
|
||||
("enter_location", "location", "Kangemi", "Kangemi"),
|
||||
("enter_products", "products", "Mandazi", "Mandazi"),
|
||||
])
|
||||
def test_save_metadata_attribute_to_session_data(current_state,
|
||||
expected_key,
|
||||
expected_result,
|
||||
user_input,
|
||||
celery_session_worker,
|
||||
create_activated_user,
|
||||
create_in_db_ussd_session,
|
||||
create_in_redis_ussd_session):
|
||||
create_in_db_ussd_session.state = current_state
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = (user_input, serialized_in_db_ussd_session, create_activated_user)
|
||||
in_memory_ussd_session = InMemoryStore.cache.get('AT974186')
|
||||
in_memory_ussd_session = json.loads(in_memory_ussd_session)
|
||||
assert in_memory_ussd_session.get('session_data') == {}
|
||||
serialized_in_db_ussd_session['state'] = current_state
|
||||
save_metadata_attribute_to_session_data(state_machine_data=state_machine_data)
|
||||
|
||||
in_memory_ussd_session = InMemoryStore.cache.get('AT974186')
|
||||
in_memory_ussd_session = json.loads(in_memory_ussd_session)
|
||||
|
||||
assert in_memory_ussd_session.get('session_data')[expected_key] == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize("preferred_language, user_input, expected_gender_value", [
|
||||
("en", "1", "Male"),
|
||||
("en", "2", "Female"),
|
||||
("sw", "1", "Mwanaume"),
|
||||
("sw", "2", "Mwanamke"),
|
||||
])
|
||||
def test_process_gender_user_input(create_activated_user, expected_gender_value, preferred_language, user_input):
|
||||
create_activated_user.preferred_language = preferred_language
|
||||
gender = process_gender_user_input(user=create_activated_user, user_input=user_input)
|
||||
assert gender == expected_gender_value
|
||||
|
||||
|
||||
def test_format_user_metadata(create_activated_user,
|
||||
complete_user_metadata,
|
||||
setup_chain_spec):
|
||||
from cic_types.models.person import Person
|
||||
formatted_user_metadata = format_user_metadata(metadata=complete_user_metadata, user=create_activated_user)
|
||||
person = Person()
|
||||
user_metadata = person.deserialize(person_data=formatted_user_metadata)
|
||||
assert formatted_user_metadata == user_metadata.serialize()
|
||||
|
||||
|
||||
def test_save_complete_user_metadata(celery_session_worker,
|
||||
complete_user_metadata,
|
||||
create_activated_user,
|
||||
create_in_redis_ussd_session,
|
||||
mocker,
|
||||
setup_chain_spec,
|
||||
ussd_session_data):
|
||||
ussd_session = create_in_redis_ussd_session.get(ussd_session_data.get('external_session_id'))
|
||||
ussd_session = json.loads(ussd_session)
|
||||
ussd_session['session_data'] = complete_user_metadata
|
||||
user_metadata = format_user_metadata(metadata=ussd_session.get('session_data'), user=create_activated_user)
|
||||
state_machine_data = ('', ussd_session, create_activated_user)
|
||||
mocked_create_metadata_task = mocker.patch('cic_ussd.tasks.metadata.create_person_metadata.apply_async')
|
||||
save_complete_user_metadata(state_machine_data=state_machine_data)
|
||||
mocked_create_metadata_task.assert_called_with(
|
||||
(user_metadata, create_activated_user.blockchain_address),
|
||||
{},
|
||||
queue='cic-ussd'
|
||||
)
|
||||
|
||||
|
||||
def test_edit_user_metadata_attribute(celery_session_worker,
|
||||
cached_user_metadata,
|
||||
create_activated_user,
|
||||
create_in_redis_ussd_session,
|
||||
init_redis_cache,
|
||||
mocker,
|
||||
person_metadata,
|
||||
setup_chain_spec,
|
||||
ussd_session_data):
|
||||
ussd_session = create_in_redis_ussd_session.get(ussd_session_data.get('external_session_id'))
|
||||
ussd_session = json.loads(ussd_session)
|
||||
|
||||
assert person_metadata['location']['area_name'] == 'kayaba'
|
||||
|
||||
# appropriately format session
|
||||
ussd_session['session_data'] = {
|
||||
'location': 'nairobi'
|
||||
}
|
||||
state_machine_data = ('', ussd_session, create_activated_user)
|
||||
|
||||
mocked_edit_metadata = mocker.patch('cic_ussd.tasks.metadata.edit_person_metadata.apply_async')
|
||||
edit_user_metadata_attribute(state_machine_data=state_machine_data)
|
||||
person_metadata['location']['area_name'] = 'nairobi'
|
||||
mocked_edit_metadata.assert_called_with(
|
||||
(create_activated_user.blockchain_address, person_metadata, Chain.spec.engine()),
|
||||
{},
|
||||
queue='cic-ussd'
|
||||
)
|
||||
|
||||
|
||||
def test_get_user_metadata_attribute(celery_session_worker,
|
||||
create_activated_user,
|
||||
create_in_redis_ussd_session,
|
||||
mocker,
|
||||
ussd_session_data):
|
||||
ussd_session = create_in_redis_ussd_session.get(ussd_session_data.get('external_session_id'))
|
||||
ussd_session = json.loads(ussd_session)
|
||||
state_machine_data = ('', ussd_session, create_activated_user)
|
||||
|
||||
mocked_get_metadata = mocker.patch('cic_ussd.tasks.metadata.query_person_metadata.apply_async')
|
||||
get_user_metadata(state_machine_data=state_machine_data)
|
||||
mocked_get_metadata.assert_called_with(
|
||||
(create_activated_user.blockchain_address,),
|
||||
{},
|
||||
queue='cic-ussd'
|
||||
)
|
||||
@@ -1,55 +1,54 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# third-party imports
|
||||
# external imports
|
||||
import pytest
|
||||
from cic_types.models.person import generate_metadata_pointer
|
||||
|
||||
# local imports
|
||||
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
||||
from cic_ussd.redis import cache_data
|
||||
from cic_ussd.state_machine.logic.validator import (is_valid_name,
|
||||
from cic_ussd.state_machine.logic.validator import (has_cached_person_metadata,
|
||||
is_valid_name,
|
||||
is_valid_gender_selection,
|
||||
has_cached_user_metadata)
|
||||
is_valid_date)
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def test_has_cached_person_metadata(activated_account,
|
||||
cache_person_metadata,
|
||||
generic_ussd_session,
|
||||
init_database,
|
||||
pending_account):
|
||||
state_machine_data = ('', generic_ussd_session, activated_account, init_database)
|
||||
assert has_cached_person_metadata(state_machine_data) is True
|
||||
state_machine_data = ('', generic_ussd_session, pending_account, init_database)
|
||||
assert has_cached_person_metadata(state_machine_data) is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize("user_input, expected_result", [
|
||||
("Arya", True),
|
||||
("1234", False)
|
||||
])
|
||||
def test_is_valid_name(create_in_db_ussd_session, create_pending_user, user_input, expected_result):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = (user_input, serialized_in_db_ussd_session, create_pending_user)
|
||||
result = is_valid_name(state_machine_data=state_machine_data)
|
||||
assert result is expected_result
|
||||
def test_is_valid_name(expected_result, generic_ussd_session, init_database, pending_account, user_input):
|
||||
|
||||
|
||||
def test_has_cached_user_metadata(create_in_db_ussd_session,
|
||||
create_activated_user,
|
||||
init_redis_cache,
|
||||
person_metadata):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = ('', serialized_in_db_ussd_session, create_activated_user)
|
||||
result = has_cached_user_metadata(state_machine_data=state_machine_data)
|
||||
assert result is False
|
||||
# cache metadata
|
||||
user = create_activated_user
|
||||
key = generate_metadata_pointer(
|
||||
identifier=blockchain_address_to_metadata_pointer(blockchain_address=user.blockchain_address),
|
||||
cic_type=':cic.person'
|
||||
)
|
||||
cache_data(key=key, data=json.dumps(person_metadata))
|
||||
result = has_cached_user_metadata(state_machine_data=state_machine_data)
|
||||
assert result
|
||||
state_machine_data = (user_input, generic_ussd_session, pending_account, init_database)
|
||||
assert is_valid_name(state_machine_data) is expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize("user_input, expected_result", [
|
||||
("1", True),
|
||||
("2", True),
|
||||
("3", True),
|
||||
("4", False)
|
||||
])
|
||||
def test_is_valid_gender_selection(expected_result, generic_ussd_session, init_database, pending_account, user_input):
|
||||
state_machine_data = (user_input, generic_ussd_session, pending_account, init_database)
|
||||
assert is_valid_gender_selection(state_machine_data) is expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize("user_input, expected_result", [
|
||||
("1935", True),
|
||||
("1825", False),
|
||||
("3", False)
|
||||
])
|
||||
def test_is_valid_gender_selection(create_in_db_ussd_session, create_pending_user, user_input, expected_result):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine_data = (user_input, serialized_in_db_ussd_session, create_pending_user)
|
||||
result = is_valid_gender_selection(state_machine_data=state_machine_data)
|
||||
assert result is expected_result
|
||||
def test_is_valid_date(expected_result, generic_ussd_session, init_database, pending_account, user_input):
|
||||
state_machine_data = (user_input, generic_ussd_session, pending_account, init_database)
|
||||
assert is_valid_date(state_machine_data) is expected_result
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
from cic_ussd.state_machine import UssdStateMachine
|
||||
|
||||
|
||||
def test_state_machine(create_in_db_ussd_session,
|
||||
get_in_redis_ussd_session,
|
||||
load_data_into_state_machine,
|
||||
create_pending_user):
|
||||
serialized_in_db_ussd_session = create_in_db_ussd_session.to_json()
|
||||
state_machine = UssdStateMachine(ussd_session=get_in_redis_ussd_session.to_json())
|
||||
state_machine.scan_data(('1', serialized_in_db_ussd_session, create_pending_user))
|
||||
def test_state_machine(activated_account_ussd_session,
|
||||
celery_session_worker,
|
||||
init_database,
|
||||
init_state_machine,
|
||||
pending_account):
|
||||
state_machine = UssdStateMachine(activated_account_ussd_session)
|
||||
state_machine.scan_data(('1', activated_account_ussd_session, pending_account, init_database))
|
||||
assert state_machine.__repr__() == f'<KenyaUssdStateMachine: {state_machine.state}>'
|
||||
assert state_machine.state == 'initial_pin_entry'
|
||||
|
||||
178
apps/cic-ussd/tests/cic_ussd/tasks/test_callback_handler.py
Normal file
178
apps/cic-ussd/tests/cic_ussd/tasks/test_callback_handler.py
Normal file
@@ -0,0 +1,178 @@
|
||||
# standard imports
|
||||
import json
|
||||
from decimal import Decimal
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
import pytest
|
||||
import requests_mock
|
||||
from chainlib.hash import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.statement import generate, filter_statement_transactions
|
||||
from cic_ussd.account.transaction import transaction_actors
|
||||
from cic_ussd.cache import cache_data_key, get_cached_data
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.error import AccountCreationDataNotFound
|
||||
from cic_ussd.metadata import PreferencesMetadata
|
||||
|
||||
|
||||
# test imports
|
||||
from tests.helpers.accounts import blockchain_address
|
||||
|
||||
|
||||
def test_account_creation_callback(account_creation_data,
|
||||
cache_account_creation_data,
|
||||
celery_session_worker,
|
||||
custom_metadata,
|
||||
init_cache,
|
||||
init_database,
|
||||
load_chain_spec,
|
||||
mocker,
|
||||
setup_metadata_request_handler,
|
||||
setup_metadata_signer):
|
||||
phone_number = account_creation_data.get('phone_number')
|
||||
result = blockchain_address()
|
||||
task_uuid = account_creation_data.get('task_uuid')
|
||||
|
||||
mock_task = mocker.patch('celery.app.task.Task.request')
|
||||
mock_task.root_id = task_uuid
|
||||
mock_task.delivery_info = {'routing_key': 'cic-ussd'}
|
||||
|
||||
status_code = 1
|
||||
with pytest.raises(ValueError) as error:
|
||||
s_account_creation_callback = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.account_creation_callback', [task_uuid, '', status_code]
|
||||
)
|
||||
s_account_creation_callback.apply_async().get()
|
||||
assert str(error.value) == f'Unexpected status code: {status_code}'
|
||||
|
||||
cached_account_creation_data = get_cached_data(task_uuid)
|
||||
cached_account_creation_data = json.loads(cached_account_creation_data)
|
||||
assert cached_account_creation_data.get('status') == account_creation_data.get('status')
|
||||
mock_add_phone_pointer = mocker.patch('cic_ussd.tasks.metadata.add_phone_pointer.apply_async')
|
||||
mock_add_custom_metadata = mocker.patch('cic_ussd.tasks.metadata.add_custom_metadata.apply_async')
|
||||
s_account_creation_callback = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.account_creation_callback', [result, '', 0]
|
||||
)
|
||||
s_account_creation_callback.apply_async().get()
|
||||
account = init_database.query(Account).filter_by(phone_number=phone_number).first()
|
||||
assert account.blockchain_address == result
|
||||
cached_account_creation_data = get_cached_data(task_uuid)
|
||||
cached_account_creation_data = json.loads(cached_account_creation_data)
|
||||
assert cached_account_creation_data.get('status') == 'CREATED'
|
||||
mock_add_phone_pointer.assert_called_with((result, phone_number), {}, queue='cic-ussd')
|
||||
mock_add_custom_metadata.assert_called_with((result, custom_metadata), {}, queue='cic-ussd')
|
||||
|
||||
task_uuid = celery.uuid()
|
||||
mock_task.root_id = task_uuid
|
||||
with pytest.raises(AccountCreationDataNotFound) as error:
|
||||
s_account_creation_callback = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.account_creation_callback', [task_uuid, '', 0]
|
||||
)
|
||||
s_account_creation_callback.apply_async().get()
|
||||
assert str(error.value) == f'No account creation data found for task id: {task_uuid}'
|
||||
|
||||
|
||||
def test_balances_callback(activated_account, balances, celery_session_worker):
|
||||
status_code = 1
|
||||
with pytest.raises(ValueError) as error:
|
||||
s_balances_callback = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.balances_callback',
|
||||
[balances, activated_account.blockchain_address, status_code])
|
||||
s_balances_callback.apply_async().get()
|
||||
assert str(error.value) == f'Unexpected status code: {status_code}.'
|
||||
|
||||
status_code = 0
|
||||
s_balances_callback = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.balances_callback',
|
||||
[balances, activated_account.blockchain_address, status_code])
|
||||
s_balances_callback.apply_async().get()
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
key = cache_data_key(identifier, ':cic.balances')
|
||||
cached_balances = get_cached_data(key)
|
||||
cached_balances = json.loads(cached_balances)
|
||||
assert cached_balances == balances[0]
|
||||
|
||||
|
||||
def test_statement_callback(activated_account, mocker, transactions_list):
|
||||
status_code = 1
|
||||
with pytest.raises(ValueError) as error:
|
||||
s_statement_callback = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.statement_callback',
|
||||
[transactions_list, activated_account.blockchain_address, status_code])
|
||||
s_statement_callback.apply_async().get()
|
||||
assert str(error.value) == f'Unexpected status code: {status_code}.'
|
||||
|
||||
mock_task = mocker.patch('celery.app.task.Task.request')
|
||||
mock_task.delivery_info = {'routing_key': 'cic-ussd'}
|
||||
mock_statement_generate = mocker.patch('cic_ussd.tasks.processor.generate_statement.apply_async')
|
||||
status_code = 0
|
||||
s_statement_callback = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.statement_callback',
|
||||
[transactions_list, activated_account.blockchain_address, status_code])
|
||||
s_statement_callback.apply_async().get()
|
||||
statement_transactions = filter_statement_transactions(transactions_list)
|
||||
recipient_transaction, sender_transaction = transaction_actors(statement_transactions[0])
|
||||
mock_statement_generate.assert_called_with(
|
||||
(activated_account.blockchain_address, sender_transaction), {}, queue='cic-ussd')
|
||||
|
||||
|
||||
def test_transaction_balances_callback(activated_account,
|
||||
balances,
|
||||
cache_balances,
|
||||
cache_person_metadata,
|
||||
cache_preferences,
|
||||
load_chain_spec,
|
||||
mocker,
|
||||
preferences,
|
||||
setup_metadata_signer,
|
||||
setup_metadata_request_handler,
|
||||
set_locale_files,
|
||||
transaction_result):
|
||||
status_code = 1
|
||||
recipient_transaction, sender_transaction = transaction_actors(transaction_result)
|
||||
with pytest.raises(ValueError) as error:
|
||||
s_transaction_balances_callback = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.transaction_balances_callback',
|
||||
[balances, sender_transaction, status_code])
|
||||
s_transaction_balances_callback.apply_async().get()
|
||||
assert str(error.value) == f'Unexpected status code: {status_code}.'
|
||||
mocked_chain = mocker.patch('celery.chain')
|
||||
mock_task_request = mocker.patch('celery.app.task.Task.request')
|
||||
mock_task_request.delivery_info = {'routing_key': 'cic-ussd'}
|
||||
sender_transaction['transaction_type'] = 'transfer'
|
||||
status_code = 0
|
||||
s_transaction_balances_callback = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.transaction_balances_callback',
|
||||
[balances, sender_transaction, status_code])
|
||||
s_transaction_balances_callback.apply_async().get()
|
||||
mocked_chain.assert_called()
|
||||
sender_transaction['transaction_type'] = 'tokengift'
|
||||
status_code = 0
|
||||
s_transaction_balances_callback = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.transaction_balances_callback',
|
||||
[balances, sender_transaction, status_code])
|
||||
s_transaction_balances_callback.apply_async().get()
|
||||
mocked_chain.assert_called()
|
||||
|
||||
|
||||
def test_transaction_callback(load_chain_spec, mock_async_balance_api_query, transaction_result):
|
||||
status_code = 1
|
||||
with pytest.raises(ValueError) as error:
|
||||
s_transaction_callback = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.transaction_callback',
|
||||
[transaction_result, 'transfer', status_code])
|
||||
s_transaction_callback.apply_async().get()
|
||||
assert str(error.value) == f'Unexpected status code: {status_code}.'
|
||||
|
||||
status_code = 0
|
||||
s_transaction_callback = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.transaction_callback',
|
||||
[transaction_result, 'transfer', status_code])
|
||||
s_transaction_callback.apply_async().get()
|
||||
recipient_transaction, sender_transaction = transaction_actors(transaction_result)
|
||||
assert mock_async_balance_api_query.get('address') == recipient_transaction.get('blockchain_address') or sender_transaction.get('blockchain_address')
|
||||
assert mock_async_balance_api_query.get('token_symbol') == recipient_transaction.get('token_symbol') or sender_transaction.get('token_symbol')
|
||||
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
# standard imports
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
# third party imports
|
||||
import celery
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.error import ActionDataNotFoundError
|
||||
from cic_ussd.conversions import from_wei
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
def test_successful_process_account_creation_callback_task(account_creation_action_data,
|
||||
celery_session_worker,
|
||||
init_database,
|
||||
init_redis_cache,
|
||||
mocker,
|
||||
set_account_creation_action_data):
|
||||
phone_number = account_creation_action_data.get('phone_number')
|
||||
task_id = account_creation_action_data.get('task_id')
|
||||
|
||||
mocked_task_request = mocker.patch('celery.app.task.Task.request')
|
||||
|
||||
# WARNING: [THE SETTING OF THE ROOT ID IS A HACK AND SHOULD BE REVIEWED OR IMPROVED]
|
||||
mocked_task_request.root_id = task_id
|
||||
|
||||
user = init_database.query(Account).filter_by(phone_number=phone_number).first()
|
||||
assert user is None
|
||||
|
||||
redis_cache = init_redis_cache
|
||||
action_data = redis_cache.get(task_id)
|
||||
action_data = json.loads(action_data)
|
||||
|
||||
assert action_data.get('status') == 'PENDING'
|
||||
|
||||
status_code = 0
|
||||
result = '0x6315c185fd23bDbbba058E2a504197915aCC5065'
|
||||
url = ''
|
||||
|
||||
s_process_callback_request = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.process_account_creation_callback',
|
||||
[result, url, status_code]
|
||||
)
|
||||
s_process_callback_request.apply_async().get()
|
||||
|
||||
user = init_database.query(Account).filter_by(phone_number=phone_number).first()
|
||||
assert user.blockchain_address == result
|
||||
|
||||
action_data = redis_cache.get(task_id)
|
||||
action_data = json.loads(action_data)
|
||||
|
||||
assert action_data.get('status') == 'CREATED'
|
||||
|
||||
|
||||
def test_unsuccessful_process_account_creation_callback_task(init_database,
|
||||
init_redis_cache,
|
||||
celery_session_worker):
|
||||
with pytest.raises(ActionDataNotFoundError) as error:
|
||||
status_code = 0
|
||||
result = '0x6315c185fd23bDbbba058E2a504197915aCC5065'
|
||||
url = ''
|
||||
|
||||
s_process_callback_request = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.process_account_creation_callback',
|
||||
[result, url, status_code]
|
||||
)
|
||||
result = s_process_callback_request.apply_async()
|
||||
task_id = result.get()
|
||||
|
||||
assert str(error.value) == f'Account creation task: {task_id}, returned unexpected response: {status_code}'
|
||||
|
||||
|
||||
def test_successful_token_gift_incoming_transaction(celery_session_worker,
|
||||
create_activated_user,
|
||||
mock_notifier_api,
|
||||
set_locale_files,
|
||||
successful_incoming_token_gift_callback):
|
||||
result = successful_incoming_token_gift_callback.get('RESULT')
|
||||
param = successful_incoming_token_gift_callback.get('PARAM')
|
||||
status_code = successful_incoming_token_gift_callback.get('STATUS_CODE')
|
||||
|
||||
s_process_token_gift = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.process_incoming_transfer_callback',
|
||||
[result, param, status_code]
|
||||
)
|
||||
s_process_token_gift.apply_async().get()
|
||||
|
||||
balance = from_wei(result.get('destination_value'))
|
||||
token_symbol = result.get('token_symbol')
|
||||
|
||||
messages = mock_notifier_api
|
||||
|
||||
assert messages[0].get('recipient') == create_activated_user.phone_number
|
||||
assert messages[0].get(
|
||||
'message') == f'Hello {""} you have been registered on Sarafu Network! Your balance is {balance} {token_symbol}. To use dial *483*46#. For help 0757628885.'
|
||||
|
||||
|
||||
def test_successful_transfer_incoming_transaction(celery_session_worker,
|
||||
create_valid_tx_sender,
|
||||
create_valid_tx_recipient,
|
||||
mock_notifier_api,
|
||||
set_locale_files,
|
||||
successful_incoming_transfer_callback):
|
||||
result = successful_incoming_transfer_callback.get('RESULT')
|
||||
param = successful_incoming_transfer_callback.get('PARAM')
|
||||
status_code = successful_incoming_transfer_callback.get('STATUS_CODE')
|
||||
|
||||
s_process_token_gift = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.process_incoming_transfer_callback',
|
||||
[result, param, status_code]
|
||||
)
|
||||
s_process_token_gift.apply_async().get()
|
||||
|
||||
value = result.get('destination_value')
|
||||
balance = ''
|
||||
token_symbol = result.get('token_symbol')
|
||||
|
||||
sender_first_name = ''
|
||||
sender_last_name = ''
|
||||
phone_number = create_valid_tx_sender.phone_number
|
||||
tx_sender_information = f'{phone_number}, {sender_first_name}, {sender_last_name}'
|
||||
amount = from_wei(value=value)
|
||||
timestamp = datetime.now().strftime('%d-%m-%y, %H:%M %p')
|
||||
|
||||
messages = mock_notifier_api
|
||||
|
||||
assert messages[0].get('recipient') == create_valid_tx_recipient.phone_number
|
||||
assert messages[0].get(
|
||||
'message') == f'Successfully received {amount} {token_symbol} from {tx_sender_information} {timestamp}. New balance is {balance} {token_symbol}.'
|
||||
|
||||
|
||||
def test_unsuccessful_incoming_transaction_recipient_not_found(celery_session_worker,
|
||||
create_valid_tx_sender,
|
||||
successful_incoming_transfer_callback):
|
||||
result = successful_incoming_transfer_callback.get('RESULT')
|
||||
param = successful_incoming_transfer_callback.get('PARAM')
|
||||
status_code = successful_incoming_transfer_callback.get('STATUS_CODE')
|
||||
|
||||
with pytest.raises(ValueError) as error:
|
||||
s_process_token_gift = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.process_incoming_transfer_callback',
|
||||
[result, param, status_code]
|
||||
)
|
||||
s_process_token_gift.apply_async().get()
|
||||
|
||||
recipient_blockchain_address = result.get('recipient')
|
||||
assert str(error.value) == f'Tx for recipient: {recipient_blockchain_address} was received but has no matching user in the system.'
|
||||
|
||||
|
||||
def test_successful_incoming_transaction_sender_not_found(caplog,
|
||||
celery_session_worker,
|
||||
create_valid_tx_recipient,
|
||||
mock_notifier_api,
|
||||
successful_incoming_transfer_callback):
|
||||
result = successful_incoming_transfer_callback.get('RESULT')
|
||||
param = successful_incoming_transfer_callback.get('PARAM')
|
||||
status_code = successful_incoming_transfer_callback.get('STATUS_CODE')
|
||||
s_process_token_gift = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.process_incoming_transfer_callback',
|
||||
[result, param, status_code]
|
||||
)
|
||||
s_process_token_gift.apply_async().get()
|
||||
|
||||
sender_blockchain_address = result.get('sender')
|
||||
assert 'Balance requires implementation of cic-eth integration with balance.' in caplog.text
|
||||
# assert f'Tx with sender: {sender_blockchain_address} was received but has no matching user in the system.\n' in caplog.text
|
||||
|
||||
|
||||
def test_unsuccessful_incoming_transaction_invalid_status_code(celery_session_worker,
|
||||
incoming_transfer_callback_invalid_tx_status_code):
|
||||
result = incoming_transfer_callback_invalid_tx_status_code.get('RESULT')
|
||||
param = incoming_transfer_callback_invalid_tx_status_code.get('PARAM')
|
||||
status_code = incoming_transfer_callback_invalid_tx_status_code.get('STATUS_CODE')
|
||||
|
||||
with pytest.raises(ValueError) as error:
|
||||
s_process_token_gift = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.process_incoming_transfer_callback',
|
||||
[result, param, status_code]
|
||||
)
|
||||
s_process_token_gift.apply_async().get()
|
||||
|
||||
assert str(error.value) == f'Unexpected status code: {status_code}'
|
||||
|
||||
|
||||
def test_unsuccessful_incoming_transaction_invalid_param(celery_session_worker,
|
||||
incoming_transfer_callback_invalid_tx_param):
|
||||
result = incoming_transfer_callback_invalid_tx_param.get('RESULT')
|
||||
param = incoming_transfer_callback_invalid_tx_param.get('PARAM')
|
||||
status_code = incoming_transfer_callback_invalid_tx_param.get('STATUS_CODE')
|
||||
|
||||
with pytest.raises(ValueError) as error:
|
||||
s_process_token_gift = celery.signature(
|
||||
'cic_ussd.tasks.callback_handler.process_incoming_transfer_callback',
|
||||
[result, param, status_code]
|
||||
)
|
||||
s_process_token_gift.apply_async().get()
|
||||
|
||||
assert str(error.value) == f'Unexpected transaction: param {status_code}'
|
||||
52
apps/cic-ussd/tests/cic_ussd/tasks/test_metadata_tasks.py
Normal file
52
apps/cic-ussd/tests/cic_ussd/tasks/test_metadata_tasks.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# standard imports
|
||||
import json
|
||||
import os
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
import requests_mock
|
||||
from chainlib.hash import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_ussd.cache import cache_data_key, get_cached_data
|
||||
from cic_ussd.metadata import PersonMetadata, PreferencesMetadata
|
||||
|
||||
# tests imports
|
||||
|
||||
|
||||
def test_query_person_metadata(activated_account,
|
||||
celery_session_worker,
|
||||
init_cache,
|
||||
person_metadata,
|
||||
setup_metadata_request_handler,
|
||||
setup_metadata_signer):
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
metadata_client = PersonMetadata(identifier)
|
||||
request_mocker.register_uri('GET', metadata_client.url, json=person_metadata, reason='OK', status_code=200)
|
||||
s_query_person_metadata = celery.signature(
|
||||
'cic_ussd.tasks.metadata.query_person_metadata', [activated_account.blockchain_address])
|
||||
s_query_person_metadata.apply().get()
|
||||
key = cache_data_key(identifier, ':cic.person')
|
||||
cached_person_metadata = get_cached_data(key)
|
||||
cached_person_metadata = json.loads(cached_person_metadata)
|
||||
assert cached_person_metadata == person_metadata
|
||||
|
||||
|
||||
def test_query_preferences_metadata(activated_account,
|
||||
celery_session_worker,
|
||||
init_cache,
|
||||
preferences,
|
||||
setup_metadata_request_handler,
|
||||
setup_metadata_signer):
|
||||
with requests_mock.Mocker(real_http=False) as request_mocker:
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
metadata_client = PreferencesMetadata(identifier)
|
||||
request_mocker.register_uri('GET', metadata_client.url, json=preferences, reason='OK', status_code=200)
|
||||
query_preferences_metadata = celery.signature(
|
||||
'cic_ussd.tasks.metadata.query_preferences_metadata', [activated_account.blockchain_address])
|
||||
query_preferences_metadata.apply().get()
|
||||
key = cache_data_key(identifier, ':cic.preferences')
|
||||
cached_preferences_metadata = get_cached_data(key)
|
||||
cached_preferences_metadata = json.loads(cached_preferences_metadata)
|
||||
assert cached_preferences_metadata == preferences
|
||||
@@ -0,0 +1,71 @@
|
||||
# standard imports
|
||||
import datetime
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.transaction import from_wei
|
||||
from cic_ussd.phone_number import Support
|
||||
from cic_ussd.translation import translation_for
|
||||
|
||||
|
||||
# tests imports
|
||||
|
||||
|
||||
def test_transaction(celery_session_worker,
|
||||
load_support_phone,
|
||||
mock_notifier_api,
|
||||
notification_data,
|
||||
set_locale_files):
|
||||
notification_data['transaction_type'] = 'transfer'
|
||||
amount = from_wei(notification_data.get('token_value'))
|
||||
balance = notification_data.get('available_balance')
|
||||
phone_number = notification_data.get('phone_number')
|
||||
preferred_language = notification_data.get('preferred_language')
|
||||
token_symbol = notification_data.get('token_symbol')
|
||||
transaction_account_metadata = notification_data.get('metadata_id')
|
||||
timestamp = datetime.datetime.now().strftime('%d-%m-%y, %H:%M %p')
|
||||
s_transaction = celery.signature(
|
||||
'cic_ussd.tasks.notifications.transaction', [notification_data]
|
||||
)
|
||||
s_transaction.apply_async().get()
|
||||
assert mock_notifier_api.get('recipient') == phone_number
|
||||
message = translation_for(key='sms.sent_tokens',
|
||||
phone_number=phone_number,
|
||||
preferred_language=preferred_language,
|
||||
amount=amount,
|
||||
token_symbol=token_symbol,
|
||||
tx_recipient_information=transaction_account_metadata,
|
||||
timestamp=timestamp,
|
||||
balance=balance)
|
||||
assert mock_notifier_api.get('message') == message
|
||||
|
||||
notification_data['role'] = 'recipient'
|
||||
notification_data['direction_tag'] = 'From'
|
||||
s_transaction = celery.signature(
|
||||
'cic_ussd.tasks.notifications.transaction', [notification_data]
|
||||
)
|
||||
s_transaction.apply_async().get()
|
||||
message = translation_for(key='sms.received_tokens',
|
||||
phone_number=phone_number,
|
||||
preferred_language=preferred_language,
|
||||
amount=amount,
|
||||
token_symbol=token_symbol,
|
||||
tx_sender_information=transaction_account_metadata,
|
||||
timestamp=timestamp,
|
||||
balance=balance)
|
||||
assert mock_notifier_api.get('message') == message
|
||||
|
||||
notification_data['transaction_type'] = 'tokengift'
|
||||
s_transaction = celery.signature(
|
||||
'cic_ussd.tasks.notifications.transaction', [notification_data]
|
||||
)
|
||||
s_transaction.apply_async().get()
|
||||
support_phone = Support.phone_number
|
||||
message = translation_for(key='sms.account_successfully_created',
|
||||
preferred_language=preferred_language,
|
||||
balance=balance,
|
||||
support_phone=support_phone,
|
||||
token_symbol=token_symbol)
|
||||
assert mock_notifier_api.get('message') == message
|
||||
@@ -1,46 +0,0 @@
|
||||
# third party imports
|
||||
import celery
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.ussd_session import UssdSession
|
||||
from cic_ussd.error import SessionNotFoundError
|
||||
|
||||
|
||||
def test_persist_session_to_db_task(
|
||||
init_database,
|
||||
create_activated_user,
|
||||
ussd_session_data,
|
||||
celery_session_worker,
|
||||
create_in_redis_ussd_session):
|
||||
external_session_id = ussd_session_data.get('external_session_id')
|
||||
s_persist_session_to_db = celery.signature(
|
||||
'cic_ussd.tasks.ussd_session.persist_session_to_db',
|
||||
[external_session_id]
|
||||
)
|
||||
result = s_persist_session_to_db.apply_async()
|
||||
result.get()
|
||||
db_session = init_database.query(UssdSession).filter_by(external_session_id=external_session_id).first()
|
||||
assert db_session.external_session_id == 'AT974186'
|
||||
assert db_session.service_code == '*483*46#'
|
||||
assert db_session.msisdn == '+25498765432'
|
||||
assert db_session.user_input == '1'
|
||||
assert db_session.state == 'initial_language_selection'
|
||||
assert db_session.session_data is None
|
||||
assert db_session.version == 2
|
||||
|
||||
assert UssdSession.have_session_for_phone(create_activated_user.phone_number)
|
||||
|
||||
|
||||
def test_session_not_found_error(
|
||||
celery_session_worker,
|
||||
create_in_redis_ussd_session):
|
||||
with pytest.raises(SessionNotFoundError) as error:
|
||||
external_session_id = 'SomeRandomValue'
|
||||
s_persist_session_to_db = celery.signature(
|
||||
'cic_ussd.tasks.ussd_session.persist_session_to_db',
|
||||
[external_session_id]
|
||||
)
|
||||
result = s_persist_session_to_db.apply_async()
|
||||
result.get()
|
||||
assert str(error.value) == "Session does not exist!"
|
||||
75
apps/cic-ussd/tests/cic_ussd/tasks/test_processor_tasks.py
Normal file
75
apps/cic-ussd/tests/cic_ussd/tasks/test_processor_tasks.py
Normal file
@@ -0,0 +1,75 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
from chainlib.hash import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.transaction import transaction_actors
|
||||
from cic_ussd.cache import cache_data_key, get_cached_data
|
||||
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def test_generate_statement(activated_account,
|
||||
celery_session_worker,
|
||||
cache_preferences,
|
||||
mocker,
|
||||
transaction_result):
|
||||
mock_task = mocker.patch('celery.app.task.Task.request')
|
||||
mock_task.delivery_info = {'routing_key': 'cic-ussd'}
|
||||
mock_chain = mocker.patch('celery.chain')
|
||||
recipient_transaction, sender_transaction = transaction_actors(transaction_result)
|
||||
s_generate_statement = celery.signature(
|
||||
'cic_ussd.tasks.processor.generate_statement', [activated_account.blockchain_address, sender_transaction]
|
||||
)
|
||||
result = s_generate_statement.apply_async().get()
|
||||
mock_chain.assert_called_once()
|
||||
|
||||
|
||||
def test_cache_statement(activated_account,
|
||||
cache_person_metadata,
|
||||
celery_session_worker,
|
||||
init_database,
|
||||
preferences,
|
||||
transaction_result):
|
||||
recipient_transaction, sender_transaction = transaction_actors(transaction_result)
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
key = cache_data_key(identifier, ':cic.statement')
|
||||
cached_statement = get_cached_data(key)
|
||||
assert cached_statement is None
|
||||
s_parse_transaction = celery.signature(
|
||||
'cic_ussd.tasks.processor.parse_transaction', [preferences, sender_transaction])
|
||||
result = s_parse_transaction.apply_async().get()
|
||||
s_cache_statement = celery.signature(
|
||||
'cic_ussd.tasks.processor.cache_statement', [result, activated_account.blockchain_address]
|
||||
)
|
||||
s_cache_statement.apply_async().get()
|
||||
cached_statement = get_cached_data(key)
|
||||
cached_statement = json.loads(cached_statement)
|
||||
assert len(cached_statement) == 1
|
||||
s_cache_statement = celery.signature(
|
||||
'cic_ussd.tasks.processor.cache_statement', [result, activated_account.blockchain_address]
|
||||
)
|
||||
s_cache_statement.apply_async().get()
|
||||
cached_statement = get_cached_data(key)
|
||||
cached_statement = json.loads(cached_statement)
|
||||
assert len(cached_statement) == 2
|
||||
|
||||
|
||||
def test_parse_transaction(activated_account,
|
||||
cache_person_metadata,
|
||||
celery_session_worker,
|
||||
init_database,
|
||||
preferences,
|
||||
transaction_result):
|
||||
recipient_transaction, sender_transaction = transaction_actors(transaction_result)
|
||||
assert sender_transaction.get('metadata_id') is None
|
||||
assert sender_transaction.get('phone_number') is None
|
||||
s_parse_transaction = celery.signature(
|
||||
'cic_ussd.tasks.processor.parse_transaction', [preferences, sender_transaction])
|
||||
result = s_parse_transaction.apply_async().get()
|
||||
assert result.get('metadata_id') == activated_account.standard_metadata_id()
|
||||
assert result.get('phone_number') == activated_account.phone_number
|
||||
@@ -0,0 +1,33 @@
|
||||
# standard imports
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.ussd_session import UssdSession
|
||||
from cic_ussd.error import SessionNotFoundError
|
||||
|
||||
# tests imports
|
||||
|
||||
|
||||
def test_persist_session_to_db(cached_ussd_session, celery_session_worker, init_cache, init_database):
|
||||
external_session_id = cached_ussd_session.external_session_id
|
||||
s_persist_session_to_db = celery.signature(
|
||||
'cic_ussd.tasks.ussd_session.persist_session_to_db', [external_session_id])
|
||||
s_persist_session_to_db.apply_async().get()
|
||||
ussd_session = init_database.query(UssdSession).filter_by(external_session_id=external_session_id).first()
|
||||
assert ussd_session.external_session_id == cached_ussd_session.external_session_id
|
||||
assert ussd_session.service_code == cached_ussd_session.service_code
|
||||
assert ussd_session.msisdn == cached_ussd_session.msisdn
|
||||
assert ussd_session.user_input == cached_ussd_session.user_input
|
||||
assert ussd_session.state == cached_ussd_session.state
|
||||
assert ussd_session.data is None
|
||||
assert ussd_session.version == cached_ussd_session.version
|
||||
assert UssdSession.has_record_for_phone_number(ussd_session.msisdn, init_database)
|
||||
with pytest.raises(SessionNotFoundError) as error:
|
||||
external_session_id = 'SomeRandomValue'
|
||||
s_persist_session_to_db = celery.signature(
|
||||
'cic_ussd.tasks.ussd_session.persist_session_to_db', [external_session_id])
|
||||
result = s_persist_session_to_db.apply_async().get()
|
||||
assert str(error.value) == "Session does not exist!"
|
||||
@@ -1,20 +0,0 @@
|
||||
# standard imports
|
||||
|
||||
# third-party imports
|
||||
|
||||
# local imports
|
||||
from cic_ussd.balance import BalanceManager
|
||||
from cic_ussd.chain import Chain
|
||||
|
||||
|
||||
def test_balance_manager(create_valid_tx_recipient, load_config, mocker, setup_chain_spec):
|
||||
chain_str = Chain.spec.__str__()
|
||||
balance_manager = BalanceManager(
|
||||
address=create_valid_tx_recipient.blockchain_address,
|
||||
chain_str=chain_str,
|
||||
token_symbol='SRF'
|
||||
)
|
||||
balance_manager.get_balances = mocker.MagicMock()
|
||||
balance_manager.get_balances()
|
||||
|
||||
balance_manager.get_balances.assert_called_once()
|
||||
33
apps/cic-ussd/tests/cic_ussd/test_cache.py
Normal file
33
apps/cic-ussd/tests/cic_ussd/test_cache.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# standard imports
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
# external imports
|
||||
|
||||
# local imports
|
||||
from cic_ussd.cache import cache_data, cache_data_key, get_cached_data
|
||||
|
||||
# test imports
|
||||
|
||||
|
||||
def test_cache_data(init_cache):
|
||||
identifier = 'some_key'.encode()
|
||||
key = cache_data_key(identifier, ':testing')
|
||||
assert get_cached_data(key) is None
|
||||
cache_data(key, json.dumps('some_value'))
|
||||
assert get_cached_data(key) is not None
|
||||
|
||||
|
||||
def test_cache_data_key():
|
||||
identifier = 'some_key'.encode()
|
||||
key = cache_data_key(identifier, ':testing')
|
||||
hash_object = hashlib.new("sha256")
|
||||
hash_object.update(identifier)
|
||||
hash_object.update(':testing'.encode(encoding="utf-8"))
|
||||
assert hash_object.digest().hex() == key
|
||||
|
||||
|
||||
def test_get_cached_data(cached_ussd_session):
|
||||
ussd_session = get_cached_data(cached_ussd_session.external_session_id)
|
||||
ussd_session = json.loads(ussd_session)
|
||||
assert ussd_session.get('msisdn') == cached_ussd_session.msisdn
|
||||
@@ -1,6 +1,6 @@
|
||||
# standard imports
|
||||
|
||||
# third-party imports
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
@@ -14,19 +14,15 @@ from cic_ussd.notifications import Notifier
|
||||
def test_send_sms_notification(celery_session_worker,
|
||||
expected_message,
|
||||
key,
|
||||
mock_notifier_api,
|
||||
preferred_language,
|
||||
recipient,
|
||||
set_locale_files,
|
||||
mock_notifier_api):
|
||||
|
||||
set_locale_files):
|
||||
notifier = Notifier()
|
||||
notifier.queue = None
|
||||
|
||||
notifier.send_sms_notification(key=key, phone_number=recipient, preferred_language=preferred_language)
|
||||
messages = mock_notifier_api
|
||||
|
||||
assert messages[0].get('message') == expected_message
|
||||
assert messages[0].get('recipient') == recipient
|
||||
assert mock_notifier_api.get('message') == expected_message
|
||||
assert mock_notifier_api.get('recipient') == recipient
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
# standard imports
|
||||
import json
|
||||
import uuid
|
||||
|
||||
# third party imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.chain import Chain
|
||||
from cic_ussd.db.models.task_tracker import TaskTracker
|
||||
from cic_ussd.menu.ussd_menu import UssdMenu
|
||||
from cic_ussd.operations import (add_tasks_to_tracker,
|
||||
create_ussd_session,
|
||||
create_or_update_session,
|
||||
define_response_with_content,
|
||||
define_multilingual_responses,
|
||||
get_account_status,
|
||||
get_latest_input,
|
||||
initiate_account_creation_request,
|
||||
process_current_menu,
|
||||
process_menu_interaction_requests,
|
||||
cache_account_creation_task_id,
|
||||
reset_pin,
|
||||
update_ussd_session,
|
||||
save_to_in_memory_ussd_session_data)
|
||||
from cic_ussd.phone_number import get_user_by_phone_number,process_phone_number
|
||||
from cic_ussd.transactions import truncate
|
||||
from cic_ussd.redis import InMemoryStore
|
||||
from cic_ussd.session.ussd_session import UssdSession as InMemoryUssdSession
|
||||
|
||||
|
||||
def test_add_tasks_to_tracker(init_database):
|
||||
task_uuid = '31e85315-feee-4b6d-995e-223569082cc4'
|
||||
session = init_database
|
||||
assert len(session.query(TaskTracker).all()) == 0
|
||||
|
||||
add_tasks_to_tracker(task_uuid=task_uuid)
|
||||
task_in_tracker = session.query(TaskTracker).filter_by(task_uuid=task_uuid).first()
|
||||
assert task_in_tracker.id == 1
|
||||
assert task_in_tracker.task_uuid == task_uuid
|
||||
|
||||
|
||||
def test_create_ussd_session(create_in_redis_ussd_session, ussd_session_data):
|
||||
external_session_id = ussd_session_data.get('external_session_id')
|
||||
ussd_session = create_ussd_session(
|
||||
external_session_id=external_session_id,
|
||||
service_code=ussd_session_data.get('service_code'),
|
||||
phone=ussd_session_data.get('msisdn'),
|
||||
user_input=ussd_session_data.get('user_input'),
|
||||
current_menu=ussd_session_data.get('state')
|
||||
)
|
||||
in_memory_ussd_session = create_in_redis_ussd_session.get(external_session_id)
|
||||
assert json.loads(in_memory_ussd_session).get('external_session_id') == ussd_session.external_session_id
|
||||
|
||||
|
||||
def test_create_or_update_session(init_database, create_in_redis_ussd_session, ussd_session_data):
|
||||
external_session_id = ussd_session_data.get('external_session_id')
|
||||
ussd_session = create_or_update_session(external_session_id=external_session_id,
|
||||
service_code=ussd_session_data.get('service_code'),
|
||||
phone=ussd_session_data.get('msisdn'),
|
||||
user_input=ussd_session_data.get('user_input'),
|
||||
current_menu=ussd_session_data.get('state'))
|
||||
in_memory_ussd_session = create_in_redis_ussd_session.get(external_session_id)
|
||||
assert json.loads(in_memory_ussd_session).get('external_session_id') == ussd_session.external_session_id
|
||||
|
||||
|
||||
@pytest.mark.parametrize('headers, response, expected_result',[
|
||||
([('Content-Type', 'text/plain')], 'some-text', (b'some-text', [('Content-Type', 'text/plain'), ('Content-Length', '9')])),
|
||||
([('Content-Type', 'text/plain'), ('Content-Length', '0')], 'some-text', (b'some-text', [('Content-Type', 'text/plain'), ('Content-Length', '9')]))
|
||||
])
|
||||
def test_define_response_with_content(headers, response, expected_result):
|
||||
response_bytes, headers = define_response_with_content(headers=headers, response=response)
|
||||
assert response_bytes, headers == expected_result
|
||||
|
||||
|
||||
def test_define_multilingual_responses(load_ussd_menu, set_locale_files):
|
||||
response = define_multilingual_responses(
|
||||
key='ussd.kenya.account_creation_prompt', locales=['en', 'sw'], prefix='END')
|
||||
assert response == "END Your account is being created. You will receive an SMS when your account is ready.\nAkaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.\n"
|
||||
|
||||
|
||||
def test_get_account_status(create_pending_user):
|
||||
user = create_pending_user
|
||||
assert get_account_status(user.phone_number) == 'PENDING'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('user_input, expected_value', [
|
||||
('1*9*6*7', '7'),
|
||||
('1', '1'),
|
||||
('', '')
|
||||
])
|
||||
def test_get_latest_input(user_input, expected_value):
|
||||
assert get_latest_input(user_input=user_input) == expected_value
|
||||
|
||||
|
||||
def test_initiate_account_creation_request(account_creation_action_data,
|
||||
create_in_redis_ussd_session,
|
||||
init_database,
|
||||
load_config,
|
||||
load_ussd_menu,
|
||||
mocker,
|
||||
setup_chain_spec,
|
||||
set_locale_files,
|
||||
ussd_session_data):
|
||||
external_session_id = ussd_session_data.get('external_session_id')
|
||||
phone_number = account_creation_action_data.get('phone_number')
|
||||
task_id = account_creation_action_data.get('task_id')
|
||||
|
||||
class Callable:
|
||||
id = task_id
|
||||
|
||||
mocker.patch('cic_eth.api.api_task.Api.create_account', return_value=Callable)
|
||||
mocked_cache_function = mocker.patch('cic_ussd.operations.cache_account_creation_task_id')
|
||||
mocked_cache_function(phone_number, task_id)
|
||||
|
||||
chain_str = Chain.spec.__str__()
|
||||
response = initiate_account_creation_request(chain_str=chain_str,
|
||||
external_session_id=external_session_id,
|
||||
phone_number=ussd_session_data.get('msisdn'),
|
||||
service_code=ussd_session_data.get('service_code'),
|
||||
user_input=ussd_session_data.get('user_input'))
|
||||
in_memory_ussd_session = InMemoryUssdSession.redis_cache.get(external_session_id)
|
||||
|
||||
# check that ussd session was created
|
||||
assert json.loads(in_memory_ussd_session).get('external_session_id') == external_session_id
|
||||
assert response == "END Your account is being created. You will receive an SMS when your account is ready.\nAkaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.\n"
|
||||
|
||||
|
||||
def test_reset_pin(create_pin_blocked_user):
|
||||
user = create_pin_blocked_user
|
||||
assert user.get_account_status() == 'LOCKED'
|
||||
reset_pin(user.phone_number)
|
||||
assert user.get_account_status() == 'RESET'
|
||||
|
||||
|
||||
def test_update_ussd_session(create_in_redis_ussd_session, load_ussd_menu, ussd_session_data):
|
||||
external_session_id = ussd_session_data.get('external_session_id')
|
||||
ussd_session = create_ussd_session(external_session_id=external_session_id,
|
||||
service_code=ussd_session_data.get('service_code'),
|
||||
phone=ussd_session_data.get('msisdn'),
|
||||
user_input=ussd_session_data.get('user_input'),
|
||||
current_menu=ussd_session_data.get('state')
|
||||
)
|
||||
assert ussd_session.user_input == ussd_session_data.get('user_input')
|
||||
assert ussd_session.state == ussd_session_data.get('state')
|
||||
ussd_session = update_ussd_session(ussd_session=ussd_session, user_input='1*2', current_menu='initial_pin_entry')
|
||||
assert ussd_session.user_input == '1*2'
|
||||
assert ussd_session.state == 'initial_pin_entry'
|
||||
|
||||
|
||||
def test_process_current_menu(create_activated_user, create_in_db_ussd_session):
|
||||
ussd_session = create_in_db_ussd_session
|
||||
current_menu = process_current_menu(ussd_session=ussd_session, user=create_activated_user, user_input="")
|
||||
assert current_menu == UssdMenu.find_by_name(name='exit_invalid_input')
|
||||
current_menu = process_current_menu(ussd_session=None, user=create_activated_user, user_input="1*0000")
|
||||
assert current_menu == UssdMenu.find_by_name(name='start')
|
||||
|
||||
|
||||
def test_cache_account_creation_task_id(init_redis_cache):
|
||||
phone_number = '+25412345678'
|
||||
task_id = str(uuid.uuid4())
|
||||
cache_account_creation_task_id(phone_number=phone_number, task_id=task_id)
|
||||
|
||||
redis_cache = init_redis_cache
|
||||
action_data = redis_cache.get(task_id)
|
||||
action_data = json.loads(action_data)
|
||||
|
||||
assert action_data.get('phone_number') == phone_number
|
||||
assert action_data.get('sms_notification_sent') is False
|
||||
assert action_data.get('status') == 'PENDING'
|
||||
assert action_data.get('task_id') == task_id
|
||||
|
||||
|
||||
def test_save_to_in_memory_ussd_session_data(celery_session_worker,
|
||||
create_in_db_ussd_session,
|
||||
create_in_redis_ussd_session,
|
||||
init_database):
|
||||
|
||||
in_memory_ussd_session = InMemoryStore.cache.get('AT974186')
|
||||
in_memory_ussd_session = json.loads(in_memory_ussd_session)
|
||||
|
||||
assert in_memory_ussd_session.get('session_data') == {}
|
||||
|
||||
session_data = {
|
||||
'some_test_key': 'some_test_value'
|
||||
}
|
||||
save_to_in_memory_ussd_session_data(
|
||||
queue='cic-ussd',
|
||||
session_data=session_data,
|
||||
ussd_session=create_in_db_ussd_session.to_json()
|
||||
)
|
||||
|
||||
in_memory_ussd_session = InMemoryStore.cache.get('AT974186')
|
||||
in_memory_ussd_session = json.loads(in_memory_ussd_session)
|
||||
|
||||
assert in_memory_ussd_session.get('session_data') == session_data
|
||||
|
||||
|
||||
@pytest.mark.parametrize("external_session_id, phone_number, expected_response", [
|
||||
("AT123456789", "+254700000000", "END Your account is being created. You will receive an SMS when your account is ready.\nAkaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.\n"),
|
||||
("AT974186", "+25498765432", "CON Please enter a PIN to manage your account.\n0. Back")
|
||||
])
|
||||
def test_process_menu_interaction_requests(external_session_id,
|
||||
phone_number,
|
||||
expected_response,
|
||||
load_ussd_menu,
|
||||
load_data_into_state_machine,
|
||||
load_config,
|
||||
setup_chain_spec,
|
||||
celery_session_worker,
|
||||
create_activated_user,
|
||||
create_in_db_ussd_session):
|
||||
chain_str = Chain.spec.__str__()
|
||||
response = process_menu_interaction_requests(
|
||||
chain_str=chain_str,
|
||||
external_session_id=external_session_id,
|
||||
phone_number=phone_number,
|
||||
queue='cic-ussd',
|
||||
service_code=load_config.get('APP_SERVICE_CODE'),
|
||||
user_input='1'
|
||||
)
|
||||
|
||||
assert response == expected_response
|
||||
|
||||
|
||||
@pytest.mark.parametrize("phone_number, region, expected_result", [
|
||||
("0712345678", "KE", "+254712345678"),
|
||||
("+254787654321", "KE", "+254787654321")
|
||||
])
|
||||
def test_process_phone_number(expected_result, phone_number, region):
|
||||
processed_phone_number = process_phone_number(phone_number=phone_number, region=region)
|
||||
assert processed_phone_number == expected_result
|
||||
|
||||
|
||||
def test_get_user_by_phone_number(create_activated_user):
|
||||
known_phone_number = create_activated_user.phone_number
|
||||
user = get_user_by_phone_number(phone_number=known_phone_number)
|
||||
assert user is not None
|
||||
assert create_activated_user.blockchain_address == user.blockchain_address
|
||||
|
||||
unknown_phone_number = '+254700000000'
|
||||
user = get_user_by_phone_number(phone_number=unknown_phone_number)
|
||||
assert user is None
|
||||
18
apps/cic-ussd/tests/cic_ussd/test_phone_number.py
Normal file
18
apps/cic-ussd/tests/cic_ussd/test_phone_number.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# standard imports
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.phone_number import process_phone_number
|
||||
|
||||
# tests imports
|
||||
|
||||
|
||||
@pytest.mark.parametrize("phone_number, region, expected_result", [
|
||||
("0712345678", "KE", "+254712345678"),
|
||||
("+254787654321", "KE", "+254787654321")
|
||||
])
|
||||
def test_process_phone_number(expected_result, phone_number, region):
|
||||
processed_phone_number = process_phone_number(phone_number=phone_number, region=region)
|
||||
assert processed_phone_number == expected_result
|
||||
@@ -1,130 +0,0 @@
|
||||
# local imports
|
||||
from cic_ussd.menu.ussd_menu import UssdMenu
|
||||
from cic_ussd.processor import (custom_display_text,
|
||||
next_state,
|
||||
process_request,
|
||||
process_pin_authorization,
|
||||
process_transaction_pin_authorization,
|
||||
process_exit_insufficient_balance,
|
||||
process_exit_successful_transaction)
|
||||
|
||||
|
||||
def test_process_pin_authorization(create_activated_user,
|
||||
load_ussd_menu,
|
||||
set_locale_files):
|
||||
ussd_menu = UssdMenu.find_by_name(name='standard_pin_authorization')
|
||||
response = process_pin_authorization(
|
||||
display_key=ussd_menu.get('display_key'),
|
||||
user=create_activated_user
|
||||
)
|
||||
assert response == 'CON Please enter your PIN.\n0. Back'
|
||||
|
||||
user_with_one_failed_pin_attempt = create_activated_user
|
||||
user_with_one_failed_pin_attempt.failed_pin_attempts = 1
|
||||
alt_response = process_pin_authorization(
|
||||
display_key=ussd_menu.get('display_key'),
|
||||
user=user_with_one_failed_pin_attempt,
|
||||
)
|
||||
assert alt_response == 'CON Please enter your PIN. You have 2 attempts remaining.\n0. Back'
|
||||
|
||||
|
||||
def test_process_transaction_pin_authorization(create_activated_user,
|
||||
create_in_db_ussd_session,
|
||||
load_ussd_menu,
|
||||
set_locale_files):
|
||||
session_data = {
|
||||
'recipient_phone_number': '+254700000000',
|
||||
}
|
||||
ussd_session = create_in_db_ussd_session.to_json()
|
||||
ussd_session['session_data'] = session_data
|
||||
ussd_session['user_input'] = '1*0700000000*120'
|
||||
ussd_menu = UssdMenu.find_by_name(name='transaction_pin_authorization')
|
||||
response = process_transaction_pin_authorization(
|
||||
display_key=ussd_menu.get('display_key'),
|
||||
user=create_activated_user,
|
||||
ussd_session=ussd_session
|
||||
)
|
||||
assert response == 'CON +254700000000 will receive 120.00 SRF from +25498765432.\nPlease enter your PIN to confirm.\n0. Back'
|
||||
|
||||
|
||||
def test_process_request_for_pending_user(load_ussd_menu, create_pending_user):
|
||||
expected_menu = process_request(user_input="", user=create_pending_user)
|
||||
assert expected_menu == UssdMenu.find_by_name(name='initial_language_selection')
|
||||
|
||||
|
||||
def test_processor_request_for_activated_user(load_ussd_menu, create_activated_user):
|
||||
expected_menu = process_request(user_input="", user=create_activated_user)
|
||||
assert expected_menu == UssdMenu.find_by_name(name="start")
|
||||
|
||||
|
||||
def test_next_state(load_data_into_state_machine, load_ussd_menu, create_in_db_ussd_session, create_pending_user):
|
||||
assert create_in_db_ussd_session.state == "initial_language_selection"
|
||||
successive_state = next_state(
|
||||
ussd_session=create_in_db_ussd_session.to_json(),
|
||||
user=create_pending_user,
|
||||
user_input="1"
|
||||
)
|
||||
assert successive_state == "initial_pin_entry"
|
||||
|
||||
|
||||
def test_custom_display_text(create_activated_user,
|
||||
get_in_redis_ussd_session,
|
||||
load_ussd_menu,
|
||||
set_locale_files):
|
||||
ussd_session = get_in_redis_ussd_session
|
||||
user = create_activated_user
|
||||
ussd_menu = UssdMenu.find_by_name(name='exit_invalid_request')
|
||||
english_translation = custom_display_text(
|
||||
display_key=ussd_menu.get('display_key'),
|
||||
menu_name=ussd_menu.get('name'),
|
||||
user=user,
|
||||
ussd_session=ussd_session
|
||||
)
|
||||
user.preferred_language = 'sw'
|
||||
swahili_translation = custom_display_text(
|
||||
display_key=ussd_menu.get('display_key'),
|
||||
menu_name=ussd_menu.get('name'),
|
||||
user=user,
|
||||
ussd_session=ussd_session
|
||||
)
|
||||
assert swahili_translation == 'END Chaguo si sahihi.'
|
||||
assert english_translation == 'END Invalid request.'
|
||||
|
||||
|
||||
def test_process_exit_insufficient_balance(
|
||||
create_valid_tx_recipient,
|
||||
load_ussd_menu,
|
||||
mock_balance,
|
||||
set_locale_files,
|
||||
ussd_session_data):
|
||||
mock_balance(50)
|
||||
ussd_session_data['user_input'] = f'1*{create_valid_tx_recipient.phone_number}*75'
|
||||
ussd_session_data['session_data'] = {'recipient_phone_number': create_valid_tx_recipient.phone_number}
|
||||
ussd_session_data['display_key'] = 'exit_insufficient_balance'
|
||||
ussd_menu = UssdMenu.find_by_name(name='exit_insufficient_balance')
|
||||
response = process_exit_insufficient_balance(
|
||||
display_key=ussd_menu.get('display_key'),
|
||||
user=create_valid_tx_recipient,
|
||||
ussd_session=ussd_session_data
|
||||
)
|
||||
assert response == 'CON Payment of 75.00 SRF to +25498765432 has failed due to insufficent balance.\nYour Sarafu-Network balances is: 50.00\n00. Back\n99. Exit'
|
||||
|
||||
|
||||
def test_process_exit_successful_transaction(
|
||||
create_valid_tx_recipient,
|
||||
create_valid_tx_sender,
|
||||
load_ussd_menu,
|
||||
set_locale_files,
|
||||
ussd_session_data):
|
||||
ussd_session_data['session_data'] = {
|
||||
'recipient_phone_number': create_valid_tx_recipient.phone_number,
|
||||
'transaction_amount': 75
|
||||
}
|
||||
ussd_session_data['display_key'] = 'exit_successful_transaction'
|
||||
ussd_menu = UssdMenu.find_by_name(name='exit_successful_transaction')
|
||||
response = process_exit_successful_transaction(
|
||||
display_key=ussd_menu.get('display_key'),
|
||||
user=create_valid_tx_sender,
|
||||
ussd_session=ussd_session_data
|
||||
)
|
||||
assert response == 'CON Your request has been sent. +25498765432 will receive 75.00 SRF from +25498765433.\n00. Back\n99. Exit'
|
||||
@@ -1,65 +0,0 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.requests import (get_query_parameters,
|
||||
get_request_endpoint,
|
||||
get_request_method,
|
||||
process_pin_reset_requests,
|
||||
process_locked_accounts_requests)
|
||||
|
||||
|
||||
def test_get_query_parameters(get_request_with_params_env):
|
||||
param = get_query_parameters(env=get_request_with_params_env, query_name='phone')
|
||||
assert param == '0700000000'
|
||||
|
||||
|
||||
def test_get_request_endpoint(valid_locked_accounts_env):
|
||||
param = get_request_endpoint(env=valid_locked_accounts_env)
|
||||
assert param == '/accounts/locked/10/10'
|
||||
|
||||
|
||||
def test_get_request_method(valid_locked_accounts_env):
|
||||
param = get_request_method(env=valid_locked_accounts_env)
|
||||
assert param == 'GET'
|
||||
|
||||
|
||||
def test_process_pin_reset_requests(uwsgi_env, create_pin_blocked_user):
|
||||
env = uwsgi_env
|
||||
env['REQUEST_METHOD'] = 'GET'
|
||||
message, status = process_pin_reset_requests(env=env, phone_number='070000000')
|
||||
assert message == 'No user matching 070000000 was found.'
|
||||
assert status == '404 Not Found'
|
||||
|
||||
env['REQUEST_METHOD'] = 'GET'
|
||||
message, status = process_pin_reset_requests(env=env, phone_number=create_pin_blocked_user.phone_number)
|
||||
assert message == '{"status": "LOCKED"}'
|
||||
assert status == '200 OK'
|
||||
|
||||
env['REQUEST_METHOD'] = 'GET'
|
||||
message, status = process_pin_reset_requests(env=env, phone_number=create_pin_blocked_user.phone_number)
|
||||
assert message == '{"status": "LOCKED"}'
|
||||
assert status == '200 OK'
|
||||
|
||||
env['REQUEST_METHOD'] = 'PUT'
|
||||
message, status = process_pin_reset_requests(env=env, phone_number=create_pin_blocked_user.phone_number)
|
||||
assert message == f'Pin reset for user {create_pin_blocked_user.phone_number} is successful!'
|
||||
assert status == '200 OK'
|
||||
assert create_pin_blocked_user.get_account_status() == 'RESET'
|
||||
|
||||
|
||||
def test_process_locked_accounts_requests(create_locked_accounts, valid_locked_accounts_env):
|
||||
|
||||
response, message = process_locked_accounts_requests(env=valid_locked_accounts_env)
|
||||
|
||||
assert message == '200 OK'
|
||||
locked_account_addresses = json.loads(response)
|
||||
assert len(locked_account_addresses) == 10
|
||||
|
||||
# check that blockchain addresses are ordered by most recently accessed
|
||||
user_1 = Account.session.query(Account).filter_by(blockchain_address=locked_account_addresses[2]).first()
|
||||
user_2 = Account.session.query(Account).filter_by(blockchain_address=locked_account_addresses[7]).first()
|
||||
|
||||
assert user_1.updated > user_2.updated
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
# standard imports
|
||||
|
||||
# third-party imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.chain import Chain
|
||||
from cic_ussd.transactions import OutgoingTransactionProcessor, truncate
|
||||
|
||||
|
||||
def test_outgoing_transaction_processor(load_config,
|
||||
create_valid_tx_recipient,
|
||||
create_valid_tx_sender,
|
||||
mock_outgoing_transactions):
|
||||
chain_str = Chain.spec.__str__()
|
||||
outgoing_tx_processor = OutgoingTransactionProcessor(
|
||||
chain_str=chain_str,
|
||||
from_address=create_valid_tx_sender.blockchain_address,
|
||||
to_address=create_valid_tx_recipient.blockchain_address
|
||||
)
|
||||
|
||||
outgoing_tx_processor.process_outgoing_transfer_transaction(
|
||||
amount=120,
|
||||
token_symbol='SRF'
|
||||
)
|
||||
assert mock_outgoing_transactions[0].get('amount') == 120.0
|
||||
assert mock_outgoing_transactions[0].get('token_symbol') == 'SRF'
|
||||
|
||||
|
||||
@pytest.mark.parametrize("decimals, value, expected_result",[
|
||||
(3, 1234.32875, 1234.328),
|
||||
(2, 98.998, 98.99)
|
||||
])
|
||||
def test_truncate(decimals, value, expected_result):
|
||||
assert truncate(value=value, decimals=decimals).__float__() == expected_result
|
||||
@@ -1,6 +1,12 @@
|
||||
# standard imports
|
||||
|
||||
# external imports
|
||||
|
||||
# local imports
|
||||
from cic_ussd.translation import translation_for
|
||||
|
||||
# tests imports
|
||||
|
||||
|
||||
def test_translation_for(set_locale_files):
|
||||
english_translation = translation_for(
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# third party imports
|
||||
# standard imports
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.validator import (check_ip,
|
||||
check_request_content_length,
|
||||
check_service_code,
|
||||
check_known_user,
|
||||
check_request_method,
|
||||
validate_phone_number,
|
||||
validate_response_type)
|
||||
is_valid_response,
|
||||
validate_phone_number)
|
||||
|
||||
|
||||
def test_check_ip(load_config, uwsgi_env):
|
||||
@@ -19,15 +19,6 @@ def test_check_request_content_length(load_config, uwsgi_env):
|
||||
assert check_request_content_length(config=load_config, env=uwsgi_env) is True
|
||||
|
||||
|
||||
def test_check_service_code(load_config):
|
||||
assert check_service_code(code='*483*46#', config=load_config) is True
|
||||
|
||||
|
||||
def test_check_known_user(create_pending_user):
|
||||
user = create_pending_user
|
||||
assert check_known_user(phone=user.phone_number) is True
|
||||
|
||||
|
||||
def test_check_request_method(uwsgi_env):
|
||||
assert check_request_method(env=uwsgi_env) is True
|
||||
|
||||
@@ -49,5 +40,5 @@ def test_validate_phone_number(phone, expected_value):
|
||||
('BIO testing', False)
|
||||
])
|
||||
def test_validate_response_type(response, expected_value):
|
||||
assert validate_response_type(response) is expected_value
|
||||
assert is_valid_response(response) is expected_value
|
||||
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
# third-party imports
|
||||
# standard imports
|
||||
from logging import config
|
||||
|
||||
# external imports
|
||||
from cic_types.pytest import *
|
||||
|
||||
|
||||
# local imports
|
||||
from tests.fixtures.config import *
|
||||
from tests.fixtures.db import *
|
||||
from tests.fixtures.celery import *
|
||||
from tests.fixtures.integration import *
|
||||
from tests.fixtures.user import *
|
||||
from tests.fixtures.ussd_session import *
|
||||
from tests.fixtures.redis import *
|
||||
from tests.fixtures.callback import *
|
||||
from tests.fixtures.requests import *
|
||||
from tests.fixtures.mocks import *
|
||||
# test imports
|
||||
from .fixtures.account import *
|
||||
from .fixtures.config import *
|
||||
from .fixtures.db import *
|
||||
from .fixtures.cache import *
|
||||
from .fixtures.integration import *
|
||||
from .fixtures.metadata import *
|
||||
from .fixtures.patches.account import *
|
||||
from .fixtures.tasker import *
|
||||
from .fixtures.transaction import *
|
||||
from .fixtures.ussd_session import *
|
||||
from .fixtures.util import *
|
||||
|
||||
241
apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc
Normal file
241
apps/cic-ussd/tests/data/pgp/privatekeys_meta.asc
Normal file
@@ -0,0 +1,241 @@
|
||||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
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
|
||||
1VrzRbOS5ymseDJcjejtMxuCOuSTN9R5KuQ=
|
||||
=VqO+
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
||||
169
apps/cic-ussd/tests/fixtures/account.py
vendored
Normal file
169
apps/cic-ussd/tests/fixtures/account.py
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
# standard imports
|
||||
import json
|
||||
import random
|
||||
|
||||
# external accounts
|
||||
import pytest
|
||||
from chainlib.hash import strip_0x
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.chain import Chain
|
||||
from cic_ussd.cache import cache_data, cache_data_key
|
||||
from cic_ussd.db.enum import AccountStatus
|
||||
from cic_ussd.db.models.account import Account
|
||||
|
||||
# test imports
|
||||
from tests.helpers.accounts import blockchain_address, phone_number
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def account_creation_data(task_uuid):
|
||||
return {
|
||||
'phone_number': phone_number(),
|
||||
'sms_notification_sent': False,
|
||||
'status': 'PENDING',
|
||||
'task_uuid': task_uuid
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def activated_account(init_database, set_fernet_key):
|
||||
account = Account(blockchain_address(), phone_number())
|
||||
account.create_password('0000')
|
||||
account.activate_account()
|
||||
init_database.add(account)
|
||||
init_database.commit()
|
||||
return account
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def balances():
|
||||
return [{
|
||||
'address': blockchain_address(),
|
||||
'converters': [],
|
||||
'balance_network': 50000000,
|
||||
'balance_outgoing': 0,
|
||||
'balance_incoming': 0
|
||||
}]
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def cache_account_creation_data(init_cache, account_creation_data):
|
||||
cache_data(account_creation_data.get('task_uuid'), json.dumps(account_creation_data))
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def cache_balances(activated_account, balances, init_cache):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
balances = json.dumps(balances[0])
|
||||
key = cache_data_key(identifier, ':cic.balances')
|
||||
cache_data(key, balances)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def cache_default_token_data(default_token_data, init_cache, load_chain_spec):
|
||||
chain_str = Chain.spec.__str__()
|
||||
data = json.dumps(default_token_data)
|
||||
key = cache_data_key(chain_str.encode('utf-8'), ':cic.default_token_data')
|
||||
cache_data(key, data)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def cache_person_metadata(activated_account, init_cache, person_metadata):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
person = json.dumps(person_metadata)
|
||||
key = cache_data_key(identifier, ':cic.person')
|
||||
cache_data(key, person)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def cache_preferences(activated_account, init_cache, preferences):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
preferences = json.dumps(preferences)
|
||||
key = cache_data_key(identifier, ':cic.preferences')
|
||||
cache_data(key, preferences)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def cache_statement(activated_account, init_cache, statement):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
preferences = json.dumps(statement)
|
||||
key = cache_data_key(identifier, ':cic.statement')
|
||||
cache_data(key, preferences)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def custom_metadata():
|
||||
return {"tags": ["ussd", "individual"]}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def default_token_data(token_symbol):
|
||||
return {
|
||||
'symbol': token_symbol,
|
||||
'address': blockchain_address(),
|
||||
'name': 'Giftable',
|
||||
'decimals': 6
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def locked_accounts_traffic(init_database, set_fernet_key):
|
||||
for _ in range(20):
|
||||
address = blockchain_address()
|
||||
phone = phone_number()
|
||||
account = Account(address, phone)
|
||||
account.create_password(str(random.randint(1000, 9999)))
|
||||
account.failed_pin_attempts = 3
|
||||
account.status = AccountStatus.LOCKED.value
|
||||
init_database.add(account)
|
||||
init_database.commit()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def pending_account(init_database, set_fernet_key):
|
||||
account = Account(blockchain_address(), phone_number())
|
||||
init_database.add(account)
|
||||
init_database.commit()
|
||||
return account
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def pin_blocked_account(init_database, set_fernet_key):
|
||||
account = Account(blockchain_address(), phone_number())
|
||||
account.create_password('3333')
|
||||
account.failed_pin_attempts = 3
|
||||
init_database.add(account)
|
||||
init_database.commit()
|
||||
return account
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def preferences():
|
||||
return {
|
||||
'preferred_language': random.sample(['en', 'sw'], 1)[0]
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def raw_person_metadata():
|
||||
return {
|
||||
"date_of_birth": {
|
||||
'year': 1998
|
||||
},
|
||||
"family_name": "Snow",
|
||||
"given_name": "Name",
|
||||
"gender": 'Male',
|
||||
"location": "Kangemi",
|
||||
"products": "Mandazi"
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def valid_recipient(init_database, set_fernet_key):
|
||||
account = Account(blockchain_address(), phone_number())
|
||||
account.create_password('2222')
|
||||
account.activate_account()
|
||||
init_database.add(account)
|
||||
init_database.commit()
|
||||
return account
|
||||
15
apps/cic-ussd/tests/fixtures/cache.py
vendored
Normal file
15
apps/cic-ussd/tests/fixtures/cache.py
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# standard imports
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.cache import Cache
|
||||
from cic_ussd.session.ussd_session import UssdSession
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def init_cache(redisdb):
|
||||
Cache.store = redisdb
|
||||
UssdSession.store = redisdb
|
||||
return redisdb
|
||||
115
apps/cic-ussd/tests/fixtures/callback.py
vendored
115
apps/cic-ussd/tests/fixtures/callback.py
vendored
@@ -1,115 +0,0 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# third party imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.redis import InMemoryStore
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def account_creation_action_data():
|
||||
return {
|
||||
'phone_number': '+254712345678',
|
||||
'sms_notification_sent': False,
|
||||
'status': 'PENDING',
|
||||
'task_id': '31e85315-feee-4b6d-995e-223569082cc4'
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def set_account_creation_action_data(init_redis_cache, account_creation_action_data):
|
||||
redis_cache = init_redis_cache
|
||||
action_data = account_creation_action_data
|
||||
task_id = action_data.get('task_id')
|
||||
redis_cache.set(task_id, json.dumps(action_data))
|
||||
redis_cache.persist(task_id)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def successful_incoming_token_gift_callback():
|
||||
return {
|
||||
'RESULT': {
|
||||
'hash': '0xb469fb2ebacc9574afb7b51d44e174fba7129fde71bf757fd39784363270832b',
|
||||
'sender': '0xd6204101012270Bf2558EDcFEd595938d1847bf0',
|
||||
'recipient': '0xFD9c5aD15C72C6F60f1a119A608931226674243f',
|
||||
'source_value': 1048576,
|
||||
'destination_value': 1048576,
|
||||
'source_token': '0xa75B519dc9b0A50D267E03D8B6808f85A66932dd',
|
||||
'destination_token': '0xa75B519dc9b0A50D267E03D8B6808f85A66932dd',
|
||||
'source_token_symbol': 'SRF',
|
||||
'destination_token_symbol': 'SRF',
|
||||
'source_token_decimals': 18,
|
||||
'destination_token_decimals': 18,
|
||||
'chain': 'Bloxberg:8996'
|
||||
},
|
||||
'PARAM': 'tokengift',
|
||||
'STATUS_CODE': 0,
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def successful_incoming_transfer_callback():
|
||||
return {
|
||||
'RESULT': {
|
||||
'hash': '0x8b0ed32533164d010afc46c0011fbcb58b0198e03c05b96e2791555746bd3606',
|
||||
'sender': '0xd6204101012270Bf2558EDcFEd595938d1847bf1',
|
||||
'recipient': '0xd6204101012270Bf2558EDcFEd595938d1847bf0',
|
||||
'source_value': 10000000000000000000000,
|
||||
'destination_value': 10000000000000000000000,
|
||||
'source_token': '0xa75B519dc9b0A50D267E03D8B6808f85A66932dd',
|
||||
'destination_token': '0xa75B519dc9b0A50D267E03D8B6808f85A66932dd',
|
||||
'source_token_symbol': 'SRF',
|
||||
'destination_token_symbol': 'SRF',
|
||||
'source_token_decimals': 18,
|
||||
'destination_token_decimals': 18,
|
||||
'chain': 'Bloxberg:8996'
|
||||
},
|
||||
'PARAM': 'transfer',
|
||||
'STATUS_CODE': 0
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def incoming_transfer_callback_invalid_tx_status_code():
|
||||
return {
|
||||
'RESULT': {
|
||||
'hash': '0x8b0ed32533164d010afc46c0011fbcb58b0198e03c05b96e2791555746bd3606',
|
||||
'sender': '0xd6204101012270Bf2558EDcFEd595938d1847bf1',
|
||||
'recipient': '0xd6204101012270Bf2558EDcFEd595938d1847bf0',
|
||||
'source_value': 10000000000000000000000,
|
||||
'destination_value': 10000000000000000000000,
|
||||
'source_token': '0xa75B519dc9b0A50D267E03D8B6808f85A66932dd',
|
||||
'destination_token': '0xa75B519dc9b0A50D267E03D8B6808f85A66932dd',
|
||||
'source_token_symbol': 'SRF',
|
||||
'destination_token_symbol': 'SRF',
|
||||
'source_token_decimals': 18,
|
||||
'destination_token_decimals': 18,
|
||||
'chain': 'Bloxberg:8996'
|
||||
},
|
||||
'PARAM': 'transfer',
|
||||
'STATUS_CODE': 1
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def incoming_transfer_callback_invalid_tx_param():
|
||||
return {
|
||||
'RESULT': {
|
||||
'hash': '0x8b0ed32533164d010afc46c0011fbcb58b0198e03c05b96e2791555746bd3606',
|
||||
'sender': '0xd6204101012270Bf2558EDcFEd595938d1847bf1',
|
||||
'recipient': '0xd6204101012270Bf2558EDcFEd595938d1847bf0',
|
||||
'source_value': 10000000000000000000000,
|
||||
'destination_value': 10000000000000000000000,
|
||||
'source_token': '0xa75B519dc9b0A50D267E03D8B6808f85A66932dd',
|
||||
'destination_token': '0xa75B519dc9b0A50D267E03D8B6808f85A66932dd',
|
||||
'source_token_symbol': 'SRF',
|
||||
'destination_token_symbol': 'SRF',
|
||||
'source_token_decimals': 18,
|
||||
'destination_token_decimals': 18,
|
||||
'chain': 'Bloxberg:8996'
|
||||
},
|
||||
'PARAM': 'erroneousparam',
|
||||
'STATUS_CODE': 0
|
||||
}
|
||||
130
apps/cic-ussd/tests/fixtures/config.py
vendored
130
apps/cic-ussd/tests/fixtures/config.py
vendored
@@ -1,42 +1,28 @@
|
||||
# standard imports
|
||||
import i18n
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
# third party imports
|
||||
import i18n
|
||||
import pytest
|
||||
from chainlib.chain import ChainSpec
|
||||
from confini import Config
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
# local imports
|
||||
from cic_ussd.chain import Chain
|
||||
from cic_ussd.db import dsn_from_config
|
||||
from cic_ussd.account.chain import Chain
|
||||
from cic_ussd.encoder import PasswordEncoder
|
||||
from cic_ussd.files.local_files import create_local_file_data_stores, json_file_parser
|
||||
from cic_ussd.menu.ussd_menu import UssdMenu
|
||||
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
||||
from cic_ussd.metadata.signer import Signer
|
||||
from cic_ussd.metadata.person import PersonMetadata
|
||||
from cic_ussd.phone_number import E164Format, Support
|
||||
from cic_ussd.state_machine import UssdStateMachine
|
||||
from cic_ussd.validator import validate_presence
|
||||
|
||||
|
||||
logg = logging.getLogger()
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
fixtures_dir = os.path.dirname(__file__)
|
||||
root_directory = os.path.dirname(os.path.dirname(fixtures_dir))
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def load_config():
|
||||
config_directory = os.path.join(root_directory, '.config/test')
|
||||
config = Config(config_dir=config_directory)
|
||||
config.process(set_as_current=True)
|
||||
logg.debug('config loaded\n{}'.format(config))
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def alembic_config():
|
||||
migrations_directory = os.path.join(root_directory, 'cic_ussd', 'db', 'migrations', 'default')
|
||||
@@ -47,22 +33,39 @@ def alembic_config():
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def alembic_engine(load_config):
|
||||
data_source_name = dsn_from_config(load_config)
|
||||
database_engine = create_engine(data_source_name)
|
||||
return database_engine
|
||||
@pytest.fixture(scope='function')
|
||||
def init_state_machine(load_config):
|
||||
UssdStateMachine.states = json_file_parser(filepath=load_config.get('MACHINE_STATES'))
|
||||
UssdStateMachine.transitions = json_file_parser(filepath=load_config.get('MACHINE_TRANSITIONS'))
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def set_fernet_key(load_config):
|
||||
PasswordEncoder.set_key(load_config.get('APP_PASSWORD_PEPPER'))
|
||||
def load_chain_spec(load_config):
|
||||
chain_spec = ChainSpec(
|
||||
common_name=load_config.get('CIC_COMMON_NAME'),
|
||||
engine=load_config.get('CIC_ENGINE'),
|
||||
network_id=load_config.get('CIC_NETWORK_ID')
|
||||
)
|
||||
Chain.spec = chain_spec
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def set_locale_files(load_config):
|
||||
i18n.load_path.append(load_config.get('APP_LOCALE_PATH'))
|
||||
i18n.set('fallback', load_config.get('APP_LOCALE_FALLBACK'))
|
||||
@pytest.fixture(scope='session')
|
||||
def load_config():
|
||||
config_directory = os.path.join(root_directory, 'config/test')
|
||||
config = Config(default_dir=config_directory)
|
||||
config.process()
|
||||
logg.debug('config loaded\n{}'.format(config))
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def load_e164_region(load_config):
|
||||
E164Format.region = load_config.get('E164_REGION')
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def load_support_phone(load_config):
|
||||
Support.phone_number = load_config.get('OFFICE_SUPPORT_PHONE')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -72,65 +75,12 @@ def load_ussd_menu(load_config):
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def load_data_into_state_machine(load_config):
|
||||
UssdStateMachine.states = json_file_parser(filepath=load_config.get('STATEMACHINE_STATES'))
|
||||
UssdStateMachine.transitions = json_file_parser(filepath=load_config.get('STATEMACHINE_TRANSITIONS'))
|
||||
def set_fernet_key(load_config):
|
||||
PasswordEncoder.set_key(load_config.get('APP_PASSWORD_PEPPER'))
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def uwsgi_env():
|
||||
return {
|
||||
'REQUEST_METHOD': 'POST',
|
||||
'REQUEST_URI': '/',
|
||||
'PATH_INFO': '/',
|
||||
'QUERY_STRING': '',
|
||||
'SERVER_PROTOCOL': 'HTTP/1.1',
|
||||
'SCRIPT_NAME': '',
|
||||
'SERVER_NAME': 'mango-habanero',
|
||||
'SERVER_PORT': '9091',
|
||||
'UWSGI_ROUTER': 'http',
|
||||
'REMOTE_ADDR': '127.0.0.1',
|
||||
'REMOTE_PORT': '33515',
|
||||
'CONTENT_TYPE': 'application/json',
|
||||
'HTTP_USER_AGENT': 'PostmanRuntime/7.26.8',
|
||||
'HTTP_ACCEPT': '*/*',
|
||||
'HTTP_POSTMAN_TOKEN': 'c1f6eb29-8160-497f-a5a1-935d175e2eb7',
|
||||
'HTTP_HOST': '127.0.0.1:9091',
|
||||
'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br',
|
||||
'HTTP_CONNECTION': 'keep-alive',
|
||||
'CONTENT_LENGTH': '102',
|
||||
'wsgi.version': (1, 0),
|
||||
'wsgi.run_once': False,
|
||||
'wsgi.multithread': False,
|
||||
'wsgi.multiprocess': False,
|
||||
'wsgi.url_scheme': 'http',
|
||||
'uwsgi.version': b'2.0.19.1',
|
||||
'uwsgi.node': b'mango-habanero'
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def setup_metadata_signer(load_config):
|
||||
temp_dir = tempfile.mkdtemp(dir='/tmp')
|
||||
logg.debug(f'Created temp dir: {temp_dir}')
|
||||
Signer.gpg_path = temp_dir
|
||||
Signer.gpg_passphrase = load_config.get('PGP_PASSPHRASE')
|
||||
Signer.key_file_path = f"{load_config.get('PGP_KEYS_PATH')}{load_config.get('PGP_PRIVATE_KEYS')}"
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def define_metadata_pointer_url(load_config, create_activated_user):
|
||||
identifier = blockchain_address_to_metadata_pointer(blockchain_address=create_activated_user.blockchain_address)
|
||||
PersonMetadata.base_url = load_config.get('CIC_META_URL')
|
||||
person_metadata_client = PersonMetadata(identifier=identifier)
|
||||
return person_metadata_client.url
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def setup_chain_spec(load_config):
|
||||
chain_spec = ChainSpec(
|
||||
common_name=load_config.get('CIC_COMMON_NAME'),
|
||||
engine=load_config.get('CIC_ENGINE'),
|
||||
network_id=load_config.get('CIC_NETWORK_ID')
|
||||
)
|
||||
Chain.spec = chain_spec
|
||||
@pytest.fixture
|
||||
def set_locale_files(load_config):
|
||||
validate_presence(load_config.get('LOCALE_PATH'))
|
||||
i18n.load_path.append(load_config.get('LOCALE_PATH'))
|
||||
i18n.set('fallback', load_config.get('LOCALE_FALLBACK'))
|
||||
|
||||
37
apps/cic-ussd/tests/fixtures/db.py
vendored
37
apps/cic-ussd/tests/fixtures/db.py
vendored
@@ -1,4 +1,5 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import os
|
||||
|
||||
# third party imports
|
||||
@@ -8,17 +9,30 @@ from alembic.config import Config as AlembicConfig
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db import dsn_from_config
|
||||
from cic_ussd.db.models.base import SessionBase
|
||||
from tests.fixtures.config import root_directory
|
||||
from cic_ussd.db.models.base import SessionBase, create_engine
|
||||
from .config import root_directory
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def alembic_engine(load_config):
|
||||
data_source_name = dsn_from_config(load_config)
|
||||
return create_engine(data_source_name)
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def database_engine(load_config):
|
||||
data_source_name = dsn_from_config(load_config)
|
||||
SessionBase.connect(data_source_name)
|
||||
yield data_source_name
|
||||
if load_config.get('DATABASE_ENGINE') == 'sqlite':
|
||||
os.unlink(load_config.get('DATABASE_NAME'))
|
||||
try:
|
||||
os.unlink(load_config.get('DATABASE_NAME'))
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
SessionBase.transactional = False
|
||||
SessionBase.poolable = False
|
||||
dsn = dsn_from_config(load_config)
|
||||
SessionBase.connect(dsn, debug=load_config.get('DATABASE_DEBUG') is not None)
|
||||
return dsn
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
@@ -27,8 +41,9 @@ def init_database(load_config, database_engine):
|
||||
migrations_directory = os.path.join(db_directory, 'migrations', load_config.get('DATABASE_ENGINE'))
|
||||
if not os.path.isdir(migrations_directory):
|
||||
migrations_directory = os.path.join(db_directory, 'migrations', 'default')
|
||||
logg.info(f'using migrations directory {migrations_directory}')
|
||||
|
||||
SessionBase.session = SessionBase.create_session()
|
||||
session = SessionBase.create_session()
|
||||
|
||||
alembic_config = AlembicConfig(os.path.join(migrations_directory, 'alembic.ini'))
|
||||
alembic_config.set_main_option('sqlalchemy.url', database_engine)
|
||||
@@ -37,8 +52,6 @@ def init_database(load_config, database_engine):
|
||||
alembic.command.downgrade(alembic_config, 'base')
|
||||
alembic.command.upgrade(alembic_config, 'head')
|
||||
|
||||
yield SessionBase.session
|
||||
|
||||
SessionBase.session.commit()
|
||||
SessionBase.session.close()
|
||||
|
||||
yield session
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
53
apps/cic-ussd/tests/fixtures/metadata.py
vendored
Normal file
53
apps/cic-ussd/tests/fixtures/metadata.py
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
from chainlib.hash import strip_0x
|
||||
from cic_types.processor import generate_metadata_pointer
|
||||
|
||||
# local imports
|
||||
from cic_ussd.metadata import Metadata, PersonMetadata, PhonePointerMetadata, PreferencesMetadata
|
||||
from cic_ussd.metadata.signer import Signer
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def setup_metadata_signer(load_config):
|
||||
temp_dir = tempfile.mkdtemp(dir='/tmp')
|
||||
logg.debug(f'Created temp dir: {temp_dir}')
|
||||
Signer.gpg_path = temp_dir
|
||||
Signer.gpg_passphrase = load_config.get('PGP_PASSPHRASE')
|
||||
Signer.key_file_path = os.path.join(load_config.get('PGP_KEYS_PATH'), load_config.get('PGP_PRIVATE_KEYS'))
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def setup_metadata_request_handler(load_config):
|
||||
Metadata.base_url = load_config.get('CIC_META_URL')
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def account_phone_pointer(activated_account):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
return generate_metadata_pointer(identifier, ':cic.phone')
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def person_metadata_url(activated_account, setup_metadata_request_handler):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
return PersonMetadata(identifier).url
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def phone_pointer_url(activated_account, setup_metadata_request_handler):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
return PhonePointerMetadata(identifier).url
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def preferences_metadata_url(activated_account, setup_metadata_request_handler):
|
||||
identifier = bytes.fromhex(strip_0x(activated_account.blockchain_address))
|
||||
return PreferencesMetadata(identifier).url
|
||||
95
apps/cic-ussd/tests/fixtures/mocks.py
vendored
95
apps/cic-ussd/tests/fixtures/mocks.py
vendored
@@ -1,95 +0,0 @@
|
||||
# standard imports
|
||||
import json
|
||||
from io import StringIO
|
||||
|
||||
# third-party imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.translation import translation_for
|
||||
from cic_ussd.transactions import truncate
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_meta_post_response():
|
||||
return {
|
||||
'name': 'cic',
|
||||
'version': '1',
|
||||
'ext': {
|
||||
'network': {
|
||||
'name': 'pgp',
|
||||
'version': '2'
|
||||
},
|
||||
'engine': {
|
||||
'name': 'automerge',
|
||||
'version': '0.14.1'
|
||||
}
|
||||
},
|
||||
'payload': '["~#iL",[["~#iM",["ops",["^0",[["^1",["action","set","obj","00000000-0000-0000-0000-000000000000",'
|
||||
'"key","id","value","7e2f58335a69ac82f9a965a8fc35403c8585ea601946d858ee97684a285bf857"]],["^1",'
|
||||
'["action","set","obj","00000000-0000-0000-0000-000000000000","key","timestamp","value",'
|
||||
'1613487781]], '
|
||||
'["^1",["action","set","obj","00000000-0000-0000-0000-000000000000","key","data","value",'
|
||||
'"{\\"foo\\": '
|
||||
'\\"bar\\", \\"xyzzy\\": 42}"]]]],"actor","2b738a75-2aad-4ac8-ae8d-294a5ea4afad","seq",1,"deps",'
|
||||
'["^1", '
|
||||
'[]],"message","Initialization","undoable",false]],["^1",["ops",["^0",[["^1",["action","makeMap",'
|
||||
'"obj","a921a5ae-0554-497a-ac2e-4e829d8a12b6"]],["^1",["action","set","obj",'
|
||||
'"a921a5ae-0554-497a-ac2e-4e829d8a12b6","key","digest","value","W10="]],["^1",["action","link",'
|
||||
'"obj", '
|
||||
'"00000000-0000-0000-0000-000000000000","key","signature","value",'
|
||||
'"a921a5ae-0554-497a-ac2e-4e829d8a12b6"]]]],"actor","2b738a75-2aad-4ac8-ae8d-294a5ea4afad","seq",2,'
|
||||
'"deps",["^1",[]],"message","sign"]]]]',
|
||||
'digest': 'W10='
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_meta_get_response():
|
||||
return {
|
||||
"foo": "bar",
|
||||
"xyzzy": 42
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_notifier_api(mocker):
|
||||
messages = []
|
||||
|
||||
def mock_sms_api(self, message: str, recipient: str):
|
||||
pass
|
||||
|
||||
def mock_send_sms_notification(self, key: str, phone_number: str, preferred_language: str, **kwargs):
|
||||
message = translation_for(key=key, preferred_language=preferred_language, **kwargs)
|
||||
messages.append({'message': message, 'recipient': phone_number})
|
||||
|
||||
mocker.patch('cic_notify.api.Api.sms', mock_sms_api)
|
||||
mocker.patch('cic_ussd.notifications.Notifier.send_sms_notification', mock_send_sms_notification)
|
||||
return messages
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_outgoing_transactions(mocker):
|
||||
transactions = []
|
||||
|
||||
def mock_process_outgoing_transfer_transaction(self, amount: int, token_symbol: str = 'SRF'):
|
||||
transactions.append({
|
||||
'amount': amount,
|
||||
'token_symbol': token_symbol
|
||||
})
|
||||
|
||||
mocker.patch(
|
||||
'cic_ussd.transactions.OutgoingTransactionProcessor.process_outgoing_transfer_transaction',
|
||||
mock_process_outgoing_transfer_transaction
|
||||
)
|
||||
return transactions
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_balance(mocker):
|
||||
mocked_operational_balance = mocker.patch('cic_ussd.accounts.BalanceManager.get_balances')
|
||||
|
||||
def _mock_operational_balance(balance: int):
|
||||
mocked_operational_balance.return_value = truncate(value=balance, decimals=2)
|
||||
|
||||
return _mock_operational_balance
|
||||
0
apps/cic-ussd/tests/fixtures/patches/__init__.py
vendored
Normal file
0
apps/cic-ussd/tests/fixtures/patches/__init__.py
vendored
Normal file
104
apps/cic-ussd/tests/fixtures/patches/account.py
vendored
Normal file
104
apps/cic-ussd/tests/fixtures/patches/account.py
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
# standard imports
|
||||
import os
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.translation import translation_for
|
||||
|
||||
# test imports
|
||||
from tests.helpers.accounts import blockchain_address
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_account_creation_task_request(mocker, task_uuid):
|
||||
def mock_request(self):
|
||||
mocked_task_request = mocker.patch('celery.app.task.Task.request')
|
||||
mocked_task_request.id = task_uuid
|
||||
return mocked_task_request
|
||||
mocker.patch('cic_eth.api.api_task.Api.create_account', mock_request)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_account_creation_task_result(mocker, task_uuid):
|
||||
def task_result(self):
|
||||
sync_res = mocker.patch('celery.result.AsyncResult')
|
||||
sync_res.id = task_uuid
|
||||
sync_res.get.return_value = blockchain_address()
|
||||
return sync_res
|
||||
mocker.patch('cic_eth.api.api_task.Api.create_account', task_result)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_async_balance_api_query(mocker):
|
||||
query_args = {}
|
||||
|
||||
def async_api_query(self, address: str, token_symbol: str):
|
||||
query_args['address'] = address
|
||||
query_args['token_symbol'] = token_symbol
|
||||
mocker.patch('cic_eth.api.api_task.Api.balance', async_api_query)
|
||||
return query_args
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_notifier_api(mocker):
|
||||
sms = {}
|
||||
|
||||
def mock_sms_api(self, message: str, recipient: str):
|
||||
pass
|
||||
|
||||
def send_sms_notification(self, key: str, phone_number: str, preferred_language: str, **kwargs):
|
||||
message = translation_for(key=key, preferred_language=preferred_language, **kwargs)
|
||||
sms['message'] = message
|
||||
sms['recipient'] = phone_number
|
||||
|
||||
mocker.patch('cic_notify.api.Api.sms', mock_sms_api)
|
||||
mocker.patch('cic_ussd.notifications.Notifier.send_sms_notification', send_sms_notification)
|
||||
return sms
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_sync_balance_api_query(balances, mocker, task_uuid):
|
||||
def sync_api_query(self, address: str, token_symbol: str):
|
||||
sync_res = mocker.patch('celery.result.AsyncResult')
|
||||
sync_res.id = task_uuid
|
||||
sync_res.get.return_value = balances
|
||||
return sync_res
|
||||
mocker.patch('cic_eth.api.api_task.Api.balance', sync_api_query)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_sync_default_token_api_query(default_token_data, mocker, task_uuid):
|
||||
def mock_query(self):
|
||||
sync_res = mocker.patch('celery.result.AsyncResult')
|
||||
sync_res.id = task_uuid
|
||||
sync_res.get.return_value = default_token_data
|
||||
return sync_res
|
||||
mocker.patch('cic_eth.api.api_task.Api.default_token', mock_query)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_transaction_list_query(mocker):
|
||||
query_args = {}
|
||||
|
||||
def mock_query(self, address: str, limit: int):
|
||||
query_args['address'] = address
|
||||
query_args['limit'] = limit
|
||||
|
||||
mocker.patch('cic_eth.api.api_task.Api.list', mock_query)
|
||||
return query_args
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_transfer_api(mocker):
|
||||
transfer_args = {}
|
||||
|
||||
def mock_transfer(self, from_address: str, to_address: str, value: int, token_symbol: str):
|
||||
transfer_args['from_address'] = from_address
|
||||
transfer_args['to_address'] = to_address
|
||||
transfer_args['value'] = value
|
||||
transfer_args['token_symbol'] = token_symbol
|
||||
|
||||
mocker.patch('cic_eth.api.api_task.Api.transfer', mock_transfer)
|
||||
return transfer_args
|
||||
11
apps/cic-ussd/tests/fixtures/redis.py
vendored
11
apps/cic-ussd/tests/fixtures/redis.py
vendored
@@ -1,11 +0,0 @@
|
||||
# third party imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.redis import InMemoryStore
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def init_redis_cache(redisdb):
|
||||
InMemoryStore.cache = redisdb
|
||||
return redisdb
|
||||
20
apps/cic-ussd/tests/fixtures/requests.py
vendored
20
apps/cic-ussd/tests/fixtures/requests.py
vendored
@@ -1,20 +0,0 @@
|
||||
# third party imports
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def valid_locked_accounts_env(uwsgi_env):
|
||||
env = uwsgi_env
|
||||
env['REQUEST_METHOD'] = 'GET'
|
||||
env['PATH_INFO'] = '/accounts/locked/10/10'
|
||||
|
||||
return env
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def get_request_with_params_env(uwsgi_env):
|
||||
env = uwsgi_env
|
||||
env['REQUEST_METHOD'] = 'GET'
|
||||
env['REQUEST_URI'] = '/?phone=0700000000'
|
||||
|
||||
return env
|
||||
@@ -1,19 +1,27 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import os
|
||||
import pytest
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
# external imports
|
||||
from celery import uuid
|
||||
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def celery_includes():
|
||||
return [
|
||||
'cic_ussd.tasks.ussd',
|
||||
'cic_ussd.tasks.callback_handler',
|
||||
'cic_ussd.tasks.metadata',
|
||||
'cic_ussd.tasks.notifications',
|
||||
'cic_ussd.tasks.processor',
|
||||
'cic_ussd.tasks.ussd_session',
|
||||
'cic_eth.queue.balance',
|
||||
'cic_notify.tasks.sms',
|
||||
'cic_ussd.tasks.metadata'
|
||||
]
|
||||
|
||||
|
||||
@@ -42,3 +50,8 @@ def celery_config():
|
||||
@pytest.fixture(scope='session')
|
||||
def celery_enable_logging():
|
||||
return True
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def task_uuid():
|
||||
return uuid()
|
||||
148
apps/cic-ussd/tests/fixtures/transaction.py
vendored
Normal file
148
apps/cic-ussd/tests/fixtures/transaction.py
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
# standard import
|
||||
import random
|
||||
|
||||
# external import
|
||||
import pytest
|
||||
|
||||
# local import
|
||||
|
||||
# tests imports
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def notification_data(activated_account, cache_person_metadata, cache_preferences, preferences):
|
||||
return {
|
||||
'blockchain_address': activated_account.blockchain_address,
|
||||
'token_symbol': 'GFT',
|
||||
'token_value': 25000000,
|
||||
'role': 'sender',
|
||||
'action_tag': 'Sent',
|
||||
'direction_tag': 'To',
|
||||
'metadata_id': activated_account.standard_metadata_id(),
|
||||
'phone_number': activated_account.phone_number,
|
||||
'available_balance': 50.0,
|
||||
'preferred_language': preferences.get('preferred_language')
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def statement(activated_account):
|
||||
return [
|
||||
{
|
||||
'blockchain_address': activated_account.blockchain_address,
|
||||
'token_symbol': 'GFT',
|
||||
'token_value': 25000000,
|
||||
'role': 'sender',
|
||||
'action_tag': 'Sent',
|
||||
'direction_tag': 'To',
|
||||
'metadata_id': activated_account.standard_metadata_id(),
|
||||
'phone_number': activated_account.phone_number,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def transaction_result(activated_account, load_config, valid_recipient):
|
||||
return {
|
||||
'hash': '0xb469fb2ebacc9574afb7b51d44e174fba7129fde71bf757fd39784363270832b',
|
||||
'sender': activated_account.blockchain_address,
|
||||
'recipient': valid_recipient.blockchain_address,
|
||||
'source_token_value': 25000000,
|
||||
'destination_token_value': 25000000,
|
||||
'source_token': '0xa75B519dc9b0A50D267E03D8B6808f85A66932dd',
|
||||
'destination_token': '0xa75B519dc9b0A50D267E03D8B6808f85A66932dd',
|
||||
'source_token_symbol': load_config.get('TEST_TOKEN_SYMBOL'),
|
||||
'destination_token_symbol': load_config.get('TEST_TOKEN_SYMBOL'),
|
||||
'source_token_decimals': 6,
|
||||
'destination_token_decimals': 6,
|
||||
'chain': 'evm:bloxberg:8996'
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def transactions_list(activated_account, valid_recipient):
|
||||
return [
|
||||
{
|
||||
'tx_hash': '0x7cdca277861665fa56c4c32930101ff41316c61af3683be12b4879e3d9990125',
|
||||
'signed_tx': '0xf8a70201837a120094b708175e3f6cd850643aaf7b32212afad50e254980b844a9059cbb000000000000000000000000367cb0f65137b0a845c1db4b7ca47d3def32dde800000000000000000000000000000000000000000000000000000000017d784082466ba030a75acff9081e57e0a9daa6858d7473fc10348bf95a6da4dd1dc6a602883c8da005358742612001ad44fc142c30bcc23b452af48c90f9c6c80433ae2a93b2e96e',
|
||||
'nonce': 2,
|
||||
'status': 'SUCCESS',
|
||||
'status_code': 4104,
|
||||
'source_token': '0xb708175e3f6Cd850643aAF7B32212AFad50e2549',
|
||||
'destination_token': '0xb708175e3f6Cd850643aAF7B32212AFad50e2549',
|
||||
'block_number': 94,
|
||||
'tx_index': 0,
|
||||
'sender': activated_account.blockchain_address,
|
||||
'recipient': valid_recipient.blockchain_address,
|
||||
'from_value': 25000000,
|
||||
'to_value': 25000000,
|
||||
'date_created': '2021-07-14T14:14:58.117017',
|
||||
'date_updated': '2021-07-14T14:14:58.117017',
|
||||
'date_checked': '2021-07-14T14:14:58.603124',
|
||||
'timestamp': 1626272098,
|
||||
'hash': '0x7cdca277861665fa56c4c32930101ff41316c61af3683be12b4879e3d9990125',
|
||||
'source_token_symbol': 'GFT',
|
||||
'source_token_decimals': 6,
|
||||
'destination_token_symbol': 'GFT',
|
||||
'destination_token_decimals': 6
|
||||
},
|
||||
{
|
||||
'tx_hash': '0x5bd3b72f07ceb55199e759e8e82006b1c70bd5b87a3d37e3327515ea27872290',
|
||||
'signed_tx': '0xf88601018323186094103d1ed6e370dba6267045c70d4999384c18a04a80a463e4bff4000000000000000000000000367cb0f65137b0a845c1db4b7ca47d3def32dde882466ca00beb6913cdd0b9b63469fbca53e2fb48dceeedf73d31d54c23c85392f01419a8a02352fff9187ba3dd6409ef6e473369dc4c3459a8baaa9bc1d68a541ca8a8f923',
|
||||
'nonce': 1,
|
||||
'status': 'REVERTED',
|
||||
'status_code': 5128,
|
||||
'source_token': '0x0000000000000000000000000000000000000000',
|
||||
'destination_token': '0x0000000000000000000000000000000000000000',
|
||||
'block_number': 80,
|
||||
'tx_index': 0,
|
||||
'sender': '0x367cB0F65137b0A845c1DB4B7Ca47D3DEF32dDe8',
|
||||
'recipient': '0x103d1ed6e370dBa6267045c70d4999384c18a04A',
|
||||
'from_value': 0,
|
||||
'to_value': 0,
|
||||
'date_created': '2021-07-14T14:13:46.036198',
|
||||
'date_updated': '2021-07-14T14:13:46.036198',
|
||||
'date_checked': '2021-07-14T14:13:46.450050',
|
||||
'timestamp': 1626272026,
|
||||
'hash': '0x5bd3b72f07ceb55199e759e8e82006b1c70bd5b87a3d37e3327515ea27872290'},
|
||||
{
|
||||
'tx_hash': '0x9d586562e1e40ae80fd506161e59825bc316293b5c522b8f243cf6c804c7843b',
|
||||
'signed_tx': '0xf868800182520894367cb0f65137b0a845c1db4b7ca47d3def32dde887066517289880008082466ca0c75083ea13d4fa9dfd408073cd0a8234199b78e79afe441fb71d7c79aa282ca6a00a7dd29e3ec1102817236d85af365fce7593b337ee609d02efdb86d298cf11ab',
|
||||
'nonce': 0,
|
||||
'status': 'SUCCESS',
|
||||
'status_code': 4104,
|
||||
'source_token': '0x0000000000000000000000000000000000000000',
|
||||
'destination_token': '0x0000000000000000000000000000000000000000',
|
||||
'block_number': 78,
|
||||
'tx_index': 0,
|
||||
'sender': '0xb41BfEE260693A473254D62b81aE1ADCC9E51AFb',
|
||||
'recipient': '0x367cB0F65137b0A845c1DB4B7Ca47D3DEF32dDe8',
|
||||
'from_value': 1800000000000000,
|
||||
'to_value': 1800000000000000,
|
||||
'date_created': '2021-07-14T14:13:35.839638',
|
||||
'date_updated': '2021-07-14T14:13:35.839638',
|
||||
'date_checked': '2021-07-14T14:13:36.333426',
|
||||
'timestamp': 1626272015,
|
||||
'hash': '0x9d586562e1e40ae80fd506161e59825bc316293b5c522b8f243cf6c804c7843b'
|
||||
},
|
||||
{
|
||||
'tx_hash': '0x32ca3dd3bef06463b452f4d32f5f563d083cb4759219eed90f3d2a9c1791c5fc',
|
||||
'signed_tx': '0xf88680018323186094103d1ed6e370dba6267045c70d4999384c18a04a80a463e4bff4000000000000000000000000367cb0f65137b0a845c1db4b7ca47d3def32dde882466ca0ab9ec1c6affb80f54bb6c2a25e64f38b3da840404180fb189bd6e191266f3c63a03cc53e59f8528da04aeec36ab8ae099553fca366bd067feffed6362ccb28d8f0',
|
||||
'nonce': 0,
|
||||
'status': 'SUCCESS',
|
||||
'status_code': 4104,
|
||||
'source_token': '0x0000000000000000000000000000000000000000',
|
||||
'destination_token': '0x0000000000000000000000000000000000000000',
|
||||
'block_number': 79,
|
||||
'tx_index': 0,
|
||||
'sender': '0x367cB0F65137b0A845c1DB4B7Ca47D3DEF32dDe8',
|
||||
'recipient': '0x103d1ed6e370dBa6267045c70d4999384c18a04A',
|
||||
'from_value': 0,
|
||||
'to_value': 0,
|
||||
'date_created': '2021-07-14T14:13:35.638355',
|
||||
'date_updated': '2021-07-14T14:13:35.638355',
|
||||
'date_checked': '2021-07-14T14:13:40.927113',
|
||||
'timestamp': 1626272015,
|
||||
'hash': '0x32ca3dd3bef06463b452f4d32f5f563d083cb4759219eed90f3d2a9c1791c5fc'}
|
||||
]
|
||||
|
||||
120
apps/cic-ussd/tests/fixtures/user.py
vendored
120
apps/cic-ussd/tests/fixtures/user.py
vendored
@@ -1,120 +0,0 @@
|
||||
# standard imports
|
||||
import json
|
||||
import uuid
|
||||
from random import randint
|
||||
|
||||
# third party imports
|
||||
import pytest
|
||||
from cic_types.models.person import generate_metadata_pointer
|
||||
from faker import Faker
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import AccountStatus, Account
|
||||
from cic_ussd.redis import cache_data
|
||||
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
|
||||
|
||||
|
||||
fake = Faker()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_activated_user(init_database, set_fernet_key):
|
||||
user = Account(
|
||||
blockchain_address='0xFD9c5aD15C72C6F60f1a119A608931226674243f',
|
||||
phone_number='+25498765432'
|
||||
)
|
||||
user.preferred_language = 'en'
|
||||
user.create_password('0000')
|
||||
user.activate_account()
|
||||
init_database.add(user)
|
||||
init_database.commit()
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_valid_tx_recipient(init_database, set_fernet_key):
|
||||
user = Account(
|
||||
blockchain_address='0xd6204101012270Bf2558EDcFEd595938d1847bf0',
|
||||
phone_number='+25498765432'
|
||||
)
|
||||
user.preferred_language = 'en'
|
||||
user.create_password('0000')
|
||||
user.activate_account()
|
||||
init_database.add(user)
|
||||
init_database.commit()
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_valid_tx_sender(init_database, set_fernet_key):
|
||||
user = Account(
|
||||
blockchain_address='0xd6204101012270Bf2558EDcFEd595938d1847bf1',
|
||||
phone_number='+25498765433'
|
||||
)
|
||||
user.preferred_language = 'en'
|
||||
user.create_password('0000')
|
||||
user.activate_account()
|
||||
init_database.add(user)
|
||||
init_database.commit()
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_pending_user(init_database, set_fernet_key):
|
||||
user = Account(
|
||||
blockchain_address='0x0ebdea8612c1b05d952c036859266c7f2cfcd6a29842d9c6cce3b9f1ba427588',
|
||||
phone_number='+25498765432'
|
||||
)
|
||||
init_database.add(user)
|
||||
init_database.commit()
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_pin_blocked_user(init_database, set_fernet_key):
|
||||
user = Account(
|
||||
blockchain_address='0x0ebdea8612c1b05d952c036859266c7f2cfcd6a29842d9c6cce3b9f1ba427588',
|
||||
phone_number='+25498765432'
|
||||
)
|
||||
user.create_password('0000')
|
||||
user.failed_pin_attempts = 3
|
||||
user.account_status = 3
|
||||
init_database.add(user)
|
||||
init_database.commit()
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_locked_accounts(init_database, set_fernet_key):
|
||||
for i in range(20):
|
||||
blockchain_address = str(uuid.uuid4())
|
||||
phone_number = fake.phone_number()
|
||||
pin = f'{randint(1000, 9999)}'
|
||||
user = Account(phone_number=phone_number, blockchain_address=blockchain_address)
|
||||
user.create_password(password=pin)
|
||||
user.failed_pin_attempts = 3
|
||||
user.account_status = AccountStatus.LOCKED.value
|
||||
user.session.add(user)
|
||||
user.session.commit()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def complete_user_metadata(create_activated_user):
|
||||
return {
|
||||
"date_registered": create_activated_user.created,
|
||||
"family_name": "Snow",
|
||||
"given_name": "Name",
|
||||
"gender": 'Male',
|
||||
"location": "Kangemi",
|
||||
"products": "Mandazi"
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def cached_user_metadata(create_activated_user, init_redis_cache, person_metadata):
|
||||
user_metadata = json.dumps(person_metadata)
|
||||
key = generate_metadata_pointer(
|
||||
identifier=blockchain_address_to_metadata_pointer(blockchain_address=create_activated_user.blockchain_address),
|
||||
cic_type=':cic.person'
|
||||
)
|
||||
cache_data(key=key, data=user_metadata)
|
||||
75
apps/cic-ussd/tests/fixtures/ussd_session.py
vendored
75
apps/cic-ussd/tests/fixtures/ussd_session.py
vendored
@@ -1,52 +1,69 @@
|
||||
# standard imports
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
|
||||
# third-party imports
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.ussd_session import UssdSession
|
||||
from cic_ussd.redis import InMemoryStore
|
||||
from cic_ussd.session.ussd_session import UssdSession as InMemoryUssdSession
|
||||
from cic_ussd.session.ussd_session import create_ussd_session
|
||||
|
||||
# test imports
|
||||
from tests.helpers.accounts import phone_number
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def ussd_session_data():
|
||||
def activated_account_ussd_session(load_config, activated_account):
|
||||
valid_service_codes = load_config.get('USSD_SERVICE_CODE').split(",")
|
||||
return {
|
||||
'external_session_id': 'AT974186',
|
||||
'service_code': '*483*46#',
|
||||
'msisdn': '+25498765432',
|
||||
'user_input': '1',
|
||||
'data': {},
|
||||
'external_session_id': os.urandom(20).hex(),
|
||||
'msisdn': activated_account.phone_number,
|
||||
'service_code': valid_service_codes[0],
|
||||
'state': 'initial_language_selection',
|
||||
'session_data': {},
|
||||
'user_input': '1',
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def generic_ussd_session(load_config, activated_account):
|
||||
valid_service_codes = load_config.get('USSD_SERVICE_CODE').split(",")
|
||||
return {
|
||||
'data': {},
|
||||
'service_code': valid_service_codes[0],
|
||||
'state': 'initial_language_selection',
|
||||
'user_input': '1',
|
||||
'version': 2
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_in_redis_ussd_session(ussd_session_data, init_redis_cache):
|
||||
external_session_id = ussd_session_data.get('external_session_id')
|
||||
InMemoryUssdSession.redis_cache = InMemoryStore.cache
|
||||
InMemoryUssdSession.redis_cache.set(external_session_id, json.dumps(ussd_session_data))
|
||||
return InMemoryUssdSession.redis_cache
|
||||
def ussd_session_traffic(generic_ussd_session, init_database, persisted_ussd_session):
|
||||
for _ in range((random.randint(5, 15))):
|
||||
generic_ussd_session['external_session_id'] = os.urandom(20).hex()
|
||||
generic_ussd_session['msisdn'] = phone_number()
|
||||
ussd = UssdSession(**{key: value for key, value in generic_ussd_session.items()})
|
||||
init_database.add(ussd)
|
||||
init_database.commit()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def get_in_redis_ussd_session(ussd_session_data, create_in_redis_ussd_session):
|
||||
external_session_id = ussd_session_data.get('external_session_id')
|
||||
ussd_session_data = create_in_redis_ussd_session.get(external_session_id)
|
||||
ussd_session_data = json.loads(ussd_session_data)
|
||||
# remove version from ussd_session data because the ussd_session object does not expect a version at initialization
|
||||
del ussd_session_data['version']
|
||||
ussd_session = InMemoryUssdSession(**{key: value for key, value in ussd_session_data.items()})
|
||||
ussd_session.version = ussd_session_data.get('version')
|
||||
return ussd_session
|
||||
def ussd_session_data(load_config):
|
||||
return {
|
||||
'recipient': phone_number()
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def create_in_db_ussd_session(init_database, ussd_session_data):
|
||||
ussd_session_data['session_data'] = {}
|
||||
ussd_session = UssdSession(**{key: value for key, value in ussd_session_data.items()})
|
||||
init_database.add(ussd_session)
|
||||
def cached_ussd_session(init_cache, activated_account_ussd_session):
|
||||
return create_ussd_session(**{key: value for key, value in activated_account_ussd_session.items()})
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def persisted_ussd_session(init_cache, init_database, activated_account_ussd_session):
|
||||
activated_account_ussd_session['version'] = 2
|
||||
ussd = UssdSession(**{key: value for key, value in activated_account_ussd_session.items()})
|
||||
init_database.add(ussd)
|
||||
init_database.commit()
|
||||
return ussd_session
|
||||
return ussd
|
||||
|
||||
63
apps/cic-ussd/tests/fixtures/util.py
vendored
Normal file
63
apps/cic-ussd/tests/fixtures/util.py
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
# standard imports
|
||||
|
||||
# external imports
|
||||
import pytest
|
||||
|
||||
# local imports
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def uwsgi_env():
|
||||
return {
|
||||
'REQUEST_METHOD': 'POST',
|
||||
'REQUEST_URI': '/',
|
||||
'PATH_INFO': '/',
|
||||
'QUERY_STRING': '',
|
||||
'SERVER_PROTOCOL': 'HTTP/1.1',
|
||||
'SCRIPT_NAME': '',
|
||||
'SERVER_NAME': 'mango-habanero',
|
||||
'SERVER_PORT': '9091',
|
||||
'UWSGI_ROUTER': 'http',
|
||||
'REMOTE_ADDR': '127.0.0.1',
|
||||
'REMOTE_PORT': '33515',
|
||||
'CONTENT_TYPE': 'application/json',
|
||||
'HTTP_USER_AGENT': 'PostmanRuntime/7.26.8',
|
||||
'HTTP_ACCEPT': '*/*',
|
||||
'HTTP_POSTMAN_TOKEN': 'c1f6eb29-8160-497f-a5a1-935d175e2eb7',
|
||||
'HTTP_HOST': '127.0.0.1:9091',
|
||||
'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br',
|
||||
'HTTP_CONNECTION': 'keep-alive',
|
||||
'CONTENT_LENGTH': '102',
|
||||
'wsgi.version': (1, 0),
|
||||
'wsgi.run_once': False,
|
||||
'wsgi.multithread': False,
|
||||
'wsgi.multiprocess': False,
|
||||
'wsgi.url_scheme': 'http',
|
||||
'uwsgi.version': b'2.0.19.1',
|
||||
'uwsgi.node': b'mango-habanero'
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def locked_accounts_env(with_params_env):
|
||||
with_params_env['PATH_INFO'] = '/accounts/locked/10/10'
|
||||
return with_params_env
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def with_params_env(uwsgi_env):
|
||||
uwsgi_env['REQUEST_METHOD'] = 'GET'
|
||||
uwsgi_env['REQUEST_URI'] = '/?phone=0700000000'
|
||||
return uwsgi_env
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_url():
|
||||
return 'https://testing.io'
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_response():
|
||||
return {
|
||||
'Looking': 'Good'
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
# standard imports
|
||||
import os
|
||||
import random
|
||||
import uuid
|
||||
|
||||
# external imports
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
from faker import Faker
|
||||
from faker_e164.providers import E164Provider
|
||||
|
||||
@@ -18,6 +20,10 @@ def phone_number() -> str:
|
||||
return fake.e164('KE')
|
||||
|
||||
|
||||
def blockchain_address() -> str:
|
||||
return to_checksum_address('0x' + os.urandom(20).hex())
|
||||
|
||||
|
||||
def session_id() -> str:
|
||||
return uuid.uuid4().hex
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ There are four files defining the integration tests.
|
||||
## REQUIREMENTS
|
||||
|
||||
In order to run the transaction tests, please ensure that the faucet amount is set to a non-zero value, ideally `50000000`
|
||||
which is the value set in the config file `.config/test/integration.ini`.
|
||||
which is the value set in the config file `config/test/integration.ini`.
|
||||
|
||||
This implies setting the `DEV_FAUCET_AMOUNT` to a non-zero value before bringing up the contract-migration image:
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
PYTHONPATH=. py.test --debug -vv --log-level debug -s --log-cli-level debug
|
||||
PYTHONPATH=. pytest --log-level debug -x .
|
||||
|
||||
@@ -37,12 +37,13 @@ stages:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '175'
|
||||
Content-Length: '91'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "END Your account is being created. You will receive an SMS when your account is ready.\nAkaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.\n"
|
||||
expected_response: "END Akaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari."
|
||||
delay_after: 5
|
||||
|
||||
- name: Initiate account creation process [second account].
|
||||
request:
|
||||
@@ -59,12 +60,12 @@ stages:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '175'
|
||||
Content-Length: '91'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "END Your account is being created. You will receive an SMS when your account is ready.\nAkaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.\n"
|
||||
expected_response: "END Akaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari."
|
||||
delay_after: 5
|
||||
|
||||
- name: Initaite account metadata entry [first account]
|
||||
@@ -82,12 +83,12 @@ stages:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '61'
|
||||
Content-Length: '57'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Welcome to Sarafu Network\n1. English\n2. Kiswahili\n3. Help"
|
||||
expected_response: "CON Karibu Sarafu Network\n1. English\n2. Kiswahili\n3. Help"
|
||||
|
||||
- name: Initaite account metadata entry [second account]
|
||||
request:
|
||||
@@ -104,12 +105,12 @@ stages:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '61'
|
||||
Content-Length: '57'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Welcome to Sarafu Network\n1. English\n2. Kiswahili\n3. Help"
|
||||
expected_response: "CON Karibu Sarafu Network\n1. English\n2. Kiswahili\n3. Help"
|
||||
|
||||
- name: Select preferred language [English]
|
||||
request:
|
||||
@@ -126,12 +127,13 @@ stages:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '64'
|
||||
Content-Length: '71'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Please enter a new four number PIN for your account.\n0. Back"
|
||||
expected_response: "CON Tafadhali weka pin mpya yenye nambari nne kwa akaunti yako\n0. Nyuma"
|
||||
delay_after: 5
|
||||
|
||||
- name: Select preferred language [Kiswahili]
|
||||
request:
|
||||
@@ -154,6 +156,7 @@ stages:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Tafadhali weka pin mpya yenye nambari nne kwa akaunti yako\n0. Nyuma"
|
||||
delay_after: 5
|
||||
|
||||
- name: Enter pin number [{first_account_pin_number} - first account]
|
||||
request:
|
||||
@@ -176,6 +179,7 @@ stages:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Enter your four number PIN again\n0. Back"
|
||||
delay_after: 5
|
||||
|
||||
- name: Enter pin number [second_account_pin_number - second account]
|
||||
request:
|
||||
@@ -198,6 +202,7 @@ stages:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka PIN yako tena\n0. Nyuma"
|
||||
delay_after: 5
|
||||
|
||||
- name: Pin number confirmation [first_account_pin_number - first account]
|
||||
request:
|
||||
|
||||
@@ -162,12 +162,13 @@ stages:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '30'
|
||||
Content-Length: '36'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "END Asante kwa kutumia huduma."
|
||||
expected_response: "END Thank you for using the service."
|
||||
delay_after: 5
|
||||
|
||||
- name: Select language [second account]
|
||||
request:
|
||||
@@ -184,12 +185,13 @@ stages:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '36'
|
||||
Content-Length: '30'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "END Thank you for using the service."
|
||||
expected_response: "END Asante kwa kutumia huduma."
|
||||
delay_after: 5
|
||||
|
||||
- name: Second account management start menu [first account]
|
||||
request:
|
||||
@@ -512,12 +514,12 @@ stages:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '31'
|
||||
Content-Length: '43'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka PIN yako tena\n0. Nyuma"
|
||||
expected_response: "CON Weka nambari yako ya siri tena\n0. Nyuma"
|
||||
|
||||
- name: Enter new pin [second account]
|
||||
request:
|
||||
|
||||
Reference in New Issue
Block a user