def test_transaction_filtering_no_result( client, acc1_usd_deposit_transaction_factory, acc2_eth_withdrawal_transaction_factory, ): """Succeeds with expected response if invalid combo of ids provided.""" deposit = acc1_usd_deposit_transaction_factory(client_address) withdrawal = acc2_eth_withdrawal_transaction_factory(client_address) encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get( (f"{sep24_endpoint}?id={deposit.id}" f"&external_transaction_id={withdrawal.external_transaction_id}" f"&stellar_transaction_id={withdrawal.stellar_transaction_id}"), follow=True, **header, ) content = json.loads(response.content) assert response.status_code == 404 assert content.get("error") is not None
def test_transaction_claimable_balance_id_result( client, acc2_eth_CB_deposit_transaction_factory, ): """Succeeds with expected response if claimable_balance_id provided.""" deposit = acc2_eth_CB_deposit_transaction_factory(client_address) encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get( (f"{sep24_endpoint}?id={deposit.id}"), follow=True, **header, ) content = json.loads(response.content) assert response.status_code == 200 deposit_claimable_balance_transaction = content.get("transaction") # Verifying the deposit claimable balance transaction data: assert deposit.claimable_balance_supported assert isinstance(deposit_claimable_balance_transaction["id"], str) assert deposit_claimable_balance_transaction["kind"] == "deposit" assert (deposit_claimable_balance_transaction["status"] == Transaction.STATUS.completed) assert deposit_claimable_balance_transaction["claimable_balance_id"]
def test_transactions_authenticated_success( client, acc2_eth_withdrawal_transaction_factory, acc2_eth_deposit_transaction_factory, ): """ Response has correct length and status code, if the SEP 10 authentication token is required. """ withdrawal = acc2_eth_withdrawal_transaction_factory(client_address) acc2_eth_deposit_transaction_factory(client_address) encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get( f"/transactions?asset_code={withdrawal.asset.code}", follow=True, **header, ) content = json.loads(response.content) assert len(content.get("transactions")) == 2 assert response.status_code == 200
def test_transaction_stellar_filter( client, acc1_usd_deposit_transaction_factory, acc2_eth_withdrawal_transaction_factory, ): """Succeeds with expected response if `stellar_transaction_id` provided.""" acc1_usd_deposit_transaction_factory(client_address) withdrawal = acc2_eth_withdrawal_transaction_factory(client_address) encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get( f"{sep24_endpoint}?stellar_transaction_id={withdrawal.stellar_transaction_id}", follow=True, **header, ) content = json.loads(response.content) assert response.status_code == 200 withdrawal_transaction = content.get("transaction") assert withdrawal_transaction["kind"] == "withdrawal" assert withdrawal_transaction["status"] == Transaction.STATUS.completed
def test_transaction_external_filter( client, acc1_usd_deposit_transaction_factory, acc2_eth_withdrawal_transaction_factory, ): """Succeeds with expected response if `external_transaction_id` provided.""" deposit = acc1_usd_deposit_transaction_factory(client_address) acc2_eth_withdrawal_transaction_factory(client_address) encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get( f"/transaction?external_transaction_id={deposit.external_transaction_id}", follow=True, **header, ) content = json.loads(response.content) assert response.status_code == 200 withdrawal_transaction = content.get("transaction") assert withdrawal_transaction["kind"] == "deposit" assert (withdrawal_transaction["status"] == Transaction.STATUS.pending_user_transfer_start)
def test_no_older_than_filter( client, acc2_eth_deposit_transaction_factory, acc2_eth_withdrawal_transaction_factory, ): """Valid `no_older_than` succeeds.""" withdrawal_transaction = acc2_eth_withdrawal_transaction_factory( client_address ) # older transaction deposit_transaction = acc2_eth_deposit_transaction_factory( client_address ) # newer transaction encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} urlencoded_datetime = urllib.parse.quote(deposit_transaction.started_at.isoformat()) response = client.get( ( f"{endpoint}?asset_code={withdrawal_transaction.asset.code}" f"&no_older_than={urlencoded_datetime}" ), follow=True, **header, ) content = json.loads(response.content) assert response.status_code == 200 assert len(content.get("transactions")) == 1 assert content.get("transactions")[0]["kind"] == "deposit"
def test_transactions_order( client, acc2_eth_withdrawal_transaction_factory, acc2_eth_deposit_transaction_factory, ): """Transactions are serialized in expected order.""" acc2_eth_deposit_transaction_factory(client_address) # older transaction withdrawal = acc2_eth_withdrawal_transaction_factory( client_address ) # newer transaction encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get( f"{endpoint}?asset_code={withdrawal.asset.code}", follow=True, **header ) content = json.loads(response.content) # Withdrawal comes first, since transactions are ordered by -id withdrawal_transaction = content.get("transactions")[0] deposit_transaction = content.get("transactions")[1] assert withdrawal_transaction["kind"] == "withdrawal" assert deposit_transaction["kind"] == "deposit"
def test_limit( client, acc2_eth_deposit_transaction_factory, acc2_eth_withdrawal_transaction_factory, ): """Valid `limit` succeeds.""" acc2_eth_deposit_transaction_factory(client_address) withdrawal = acc2_eth_withdrawal_transaction_factory(client_address) # newest encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get( f"{endpoint}?asset_code={withdrawal.asset.code}" "&limit=1", follow=True, **header, ) content = json.loads(response.content) # By providing the paging_id = w.id, we're looking for entries older than `w` # which only leaves us with the deposit transaction. assert len(content.get("transactions")) == 1 assert content.get("transactions")[0]["kind"] == "withdrawal"
def test_paging_id( client, acc2_eth_deposit_transaction_factory, acc2_eth_withdrawal_transaction_factory, ): """Only return transactions chronologically after a `paging_id`, if provided.""" acc2_eth_deposit_transaction_factory(client_address) withdrawal = acc2_eth_withdrawal_transaction_factory(client_address) encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get( ( f"{endpoint}?asset_code={withdrawal.asset.code}" f"&paging_id={withdrawal.id}" ), follow=True, **header, ) content = json.loads(response.content) # By providing the paging_id = w.id, we're looking for entries older than `w` # which only leaves us with the deposit transaction. assert len(content.get("transactions")) == 1 assert content.get("transactions")[0]["kind"] == "deposit"
def test_transaction_authenticated_success( client, acc1_usd_deposit_transaction_factory, acc2_eth_withdrawal_transaction_factory, ): """ Succeeds with expected response if authentication required. Though it filters using the stellar transaction ID, the logic should apply in any case. """ acc1_usd_deposit_transaction_factory(client_address) withdrawal = acc2_eth_withdrawal_transaction_factory(client_address) withdrawal.stellar_address = client_address withdrawal.save() encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get( f"{sep24_endpoint}?stellar_transaction_id={withdrawal.stellar_transaction_id}", follow=True, **header, ) content = json.loads(response.content) assert response.status_code == 200 withdrawal_transaction = content.get("transaction") assert withdrawal_transaction["kind"] == "withdrawal" assert withdrawal_transaction["status"] == Transaction.STATUS.completed
def test_transaction_multiple_filters( client, acc1_usd_deposit_transaction_factory, acc2_eth_withdrawal_transaction_factory, ): """Succeeds with expected response if multiple valid ids provided.""" acc1_usd_deposit_transaction_factory(client_address) withdrawal = acc2_eth_withdrawal_transaction_factory(client_address) encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get( (f"/transaction?id={withdrawal.id}" f"&external_transaction_id={withdrawal.external_transaction_id}" f"&stellar_transaction_id={withdrawal.stellar_transaction_id}"), follow=True, **header, ) content = json.loads(response.content) assert response.status_code == 200 withdrawal_transaction = content.get("transaction") # Verifying the withdrawal transaction data: assert isinstance(withdrawal_transaction["id"], str) assert withdrawal_transaction["kind"] == "withdrawal" assert withdrawal_transaction["status"] == Transaction.STATUS.completed
def test_transaction_bad_uuid(client): encoded_jwt = sep10(client, client_address, client_seed) header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get(f"/transaction?id=NOTAREALID", follow=True, **header) content = json.loads(response.content) assert response.status_code == 400 assert content == {"error": "[\"'NOTAREALID' is not a valid UUID.\"]"}
def test_more_info_required_fields(client, acc1_usd_deposit_transaction_factory): """Fails if no required fields are provided.""" acc1_usd_deposit_transaction_factory(client_address) encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get(f"/transaction/more_info", follow=True, **header) assert response.status_code == 400
def test_transaction_id_filter_and_format( client, acc1_usd_deposit_transaction_factory, acc2_eth_withdrawal_transaction_factory, ): """Succeeds with expected response if `id` provided.""" acc1_usd_deposit_transaction_factory(client_address) withdrawal = acc2_eth_withdrawal_transaction_factory(client_address) encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} w_started_at = withdrawal.started_at.isoformat().replace("+00:00", "Z") w_completed_at = withdrawal.completed_at.isoformat().replace("+00:00", "Z") response = client.get(f"/transaction?id={withdrawal.id}", follow=True, **header) content = json.loads(response.content) assert response.status_code == 200 withdrawal_transaction = content.get("transaction") # Verifying the withdrawal transaction data: assert isinstance(withdrawal_transaction["id"], str) assert withdrawal_transaction["kind"] == "withdrawal" assert withdrawal_transaction["status"] == "completed" assert withdrawal_transaction["status_eta"] == 3600 assert withdrawal_transaction["amount_in"] == "500.0" assert withdrawal_transaction["amount_out"] == "495.0" assert withdrawal_transaction["amount_fee"] == "3.0" assert withdrawal_transaction["started_at"] == w_started_at assert withdrawal_transaction["completed_at"] == w_completed_at assert ( withdrawal_transaction["stellar_transaction_id"] == "17a670bc424ff5ce3b386dbfaae9990b66a2a37b4fbe51547e8794962a3f9e6a" ) assert ( withdrawal_transaction["external_transaction_id"] == "2dd16cb409513026fbe7defc0c6f826c2d2c65c3da993f747d09bf7dafd31094" ) assert withdrawal_transaction["from"] is None assert withdrawal_transaction["to"] is None assert withdrawal_transaction["external_extra"] is None assert withdrawal_transaction["external_extra_text"] is None assert withdrawal_transaction["deposit_memo"] is None assert withdrawal_transaction["deposit_memo_type"] == withdrawal.deposit_memo_type assert ( withdrawal_transaction["withdraw_anchor_account"] == withdrawal.withdraw_anchor_account ) assert withdrawal_transaction["withdraw_memo"] == withdrawal.withdraw_memo assert withdrawal_transaction["withdraw_memo_type"] == withdrawal.withdraw_memo_type
def test_more_info_id_filter(client, acc1_usd_deposit_transaction_factory): """Succeeds if a valid transaction ID is provided.""" deposit = acc1_usd_deposit_transaction_factory(client_address) encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get(f"/transaction/more_info?id={deposit.id}", follow=True, **header) assert response.status_code == 200
def test_deposit_interactive_confirm_success( mock_check_middleware, mock_submit, mock_base_fee, client, acc1_usd_deposit_transaction_factory, ): """ `GET /deposit` and `GET /transactions/deposit/webapp` succeed with valid `account` and `asset_code`. """ del mock_submit, mock_base_fee, mock_check_middleware deposit = acc1_usd_deposit_transaction_factory() encoded_jwt = sep10(client, deposit.stellar_account, STELLAR_ACCOUNT_1_SEED) header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.post( DEPOSIT_PATH, { "asset_code": "USD", "account": deposit.stellar_account }, follow=True, **header, ) content = json.loads(response.content) assert response.status_code == 200 assert content["type"] == "interactive_customer_info_needed" transaction_id = content["id"] url = content["url"] # Authenticate session response = client.get(url) assert response.status_code == 200 assert client.session["authenticated"] is True amount = 20 url, args_str = url.split("?") response = client.post(url + "/submit?" + args_str, {"amount": amount}) assert response.status_code == 302 assert (Transaction.objects.get(id=transaction_id).status == Transaction.STATUS.pending_user_transfer_start) transaction = Transaction.objects.get(id=transaction_id) execute_deposit(transaction) # We've mocked submit_transaction, but the status should be marked as # completed after executing the function above. transaction.refresh_from_db() assert float(transaction.amount_in) == amount assert transaction.status == Transaction.STATUS.completed
def test_more_info_multiple_filters(client, acc1_usd_deposit_transaction_factory): """Succeeds if a valid combination of IDs is provided.""" deposit = acc1_usd_deposit_transaction_factory(client_address) deposit.stellar_transaction_id = "test_stellar_id" deposit.save() encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get( f"/transaction/more_info?id={deposit.id}" f"&external_transaction_id={deposit.external_transaction_id}" f"&stellar_transaction_id={deposit.stellar_transaction_id}", follow=True, **header, ) assert response.status_code == 200
def test_more_info_no_result( client, acc1_usd_deposit_transaction_factory, acc2_eth_withdrawal_transaction_factory, ): """Fails if an invalid combination of IDs is provided.""" deposit = acc1_usd_deposit_transaction_factory(client_address) withdrawal = acc2_eth_withdrawal_transaction_factory(client_address) encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} response = client.get( f"/transaction/more_info?id={deposit.id}" f"&external_transaction_id={withdrawal.external_transaction_id}" f"&stellar_transaction_id={withdrawal.stellar_transaction_id}", follow=True, **header, ) assert response.status_code == 404
def test_transactions_content( client, acc2_eth_deposit_transaction_factory, acc2_eth_withdrawal_transaction_factory, ): """ This expected response was adapted from the example SEP-0024 response on https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0024.md#transaction-history Some changes have been applied, to ensure the data we provide is in a consistent format and in accordance with design decisions from this reference implementation: - amounts are floats, so values like "500" are displayed as "500.0" - nullable fields are displayed, but with a null value """ deposit = acc2_eth_deposit_transaction_factory(client_address) withdrawal = acc2_eth_withdrawal_transaction_factory(client_address) encoded_jwt = sep10(client, client_address, client_seed) # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that # we expect due to the middleware. header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"} d_started_at = deposit.started_at.isoformat().replace("+00:00", "Z") w_started_at = withdrawal.started_at.isoformat().replace("+00:00", "Z") w_completed_at = withdrawal.completed_at.isoformat().replace("+00:00", "Z") response = client.get( f"{endpoint}?asset_code={withdrawal.asset.code}", follow=True, **header ) content = json.loads(response.content) withdrawal_transaction = content.get("transactions")[0] deposit_transaction = content.get("transactions")[1] withdrawal_asset = withdrawal.asset deposit_asset = deposit.asset # update amount_* fields to the correct number of decimals withdrawal.refresh_from_db() deposit.refresh_from_db() # Verifying the withdrawal transaction data: assert withdrawal_transaction["id"] == str(withdrawal.id) assert withdrawal_transaction["kind"] == withdrawal.kind assert withdrawal_transaction["status"] == withdrawal.status assert not withdrawal_transaction["status_eta"] assert withdrawal_transaction["amount_in"] == str( round(withdrawal.amount_in, withdrawal_asset.significant_decimals) ) assert withdrawal_transaction["amount_out"] == str( round(withdrawal.amount_out, withdrawal_asset.significant_decimals) ) assert withdrawal_transaction["amount_fee"] == str( round(withdrawal.amount_fee, withdrawal_asset.significant_decimals) ) assert withdrawal_transaction["started_at"] == w_started_at assert withdrawal_transaction["completed_at"] == w_completed_at assert ( withdrawal_transaction["stellar_transaction_id"] == withdrawal.stellar_transaction_id ) assert ( withdrawal_transaction["external_transaction_id"] == withdrawal.external_transaction_id ) assert withdrawal_transaction["from"] is None assert withdrawal_transaction["to"] is None assert ( withdrawal_transaction["withdraw_anchor_account"] == withdrawal.receiving_anchor_account ) assert withdrawal_transaction["withdraw_memo"] == withdrawal.memo assert withdrawal_transaction["withdraw_memo_type"] == withdrawal.memo_type # Verifying the deposit transaction data: assert deposit_transaction["id"] == str(deposit.id) assert deposit_transaction["kind"] == deposit.kind assert deposit_transaction["status"] == deposit.status assert deposit_transaction["status_eta"] == deposit.status_eta assert deposit_transaction["amount_in"] == str( round(deposit.amount_in, deposit_asset.significant_decimals) ) assert deposit_transaction["amount_out"] == str( round(deposit.amount_out, deposit_asset.significant_decimals) ) assert deposit_transaction["amount_fee"] == str( round(deposit.amount_fee, deposit_asset.significant_decimals) ) assert deposit_transaction["started_at"] == d_started_at assert deposit_transaction["completed_at"] is None assert deposit_transaction["stellar_transaction_id"] is None assert ( deposit_transaction["external_transaction_id"] == deposit.external_transaction_id ) assert deposit_transaction["from"] is None assert deposit_transaction["to"] is None assert deposit_transaction["deposit_memo"] == deposit.memo assert deposit_transaction["deposit_memo_type"] == deposit.memo_type # stellar_account and asset should not be exposed: with pytest.raises(KeyError): assert withdrawal_transaction["stellar_account"] with pytest.raises(KeyError): assert withdrawal_transaction["asset"] with pytest.raises(KeyError): assert deposit_transaction["stellar_account"] with pytest.raises(KeyError): assert deposit_transaction["asset"]