180 lines
6.0 KiB
Python
180 lines
6.0 KiB
Python
import json
|
|
from unittest.mock import patch, MagicMock
|
|
from graphene_django.utils.testing import GraphQLTestCase
|
|
from .models import Account
|
|
import uuid
|
|
|
|
class BillingGraphQLTests(GraphQLTestCase):
|
|
GRAPHQL_URL = '/graphql/public/'
|
|
|
|
@patch('billing_app.schemas.tigerbeetle_client')
|
|
def test_create_transfer_new_accounts(self, mock_tb_client):
|
|
"""
|
|
Tests that a transfer is created successfully and that new accounts
|
|
are created lazily in both the local DB and in TigerBeetle.
|
|
"""
|
|
# Configure the mock to return no errors
|
|
mock_tb_client.create_accounts.return_value = []
|
|
mock_tb_client.create_transfers.return_value = []
|
|
|
|
debit_account_id = str(uuid.uuid4())
|
|
credit_account_id = str(uuid.uuid4())
|
|
amount = 100
|
|
ledger = 1
|
|
code = 200
|
|
|
|
# Ensure accounts do not exist locally
|
|
self.assertEqual(Account.objects.count(), 0)
|
|
|
|
response = self.query(
|
|
'''
|
|
mutation createTransfer(
|
|
$debitAccountId: String!,
|
|
$creditAccountId: String!,
|
|
$amount: Int!,
|
|
$ledger: Int!,
|
|
$code: Int!
|
|
) {
|
|
createTransfer(
|
|
debitAccountId: $debitAccountId,
|
|
creditAccountId: $creditAccountId,
|
|
amount: $amount,
|
|
ledger: $ledger,
|
|
code: $code
|
|
) {
|
|
success
|
|
message
|
|
transferId
|
|
}
|
|
}
|
|
''',
|
|
variables={
|
|
'debitAccountId': debit_account_id,
|
|
'creditAccountId': credit_account_id,
|
|
'amount': amount,
|
|
'ledger': ledger,
|
|
'code': code
|
|
}
|
|
)
|
|
|
|
self.assertResponseNoErrors(response)
|
|
content = json.loads(response.content)
|
|
result = content['data']['createTransfer']
|
|
|
|
self.assertTrue(result['success'])
|
|
self.assertIsNotNone(result['transferId'])
|
|
self.assertEqual(result['message'], 'Transfer completed successfully.')
|
|
|
|
# Verify local accounts were created
|
|
self.assertEqual(Account.objects.count(), 2)
|
|
self.assertTrue(Account.objects.filter(uuid=debit_account_id).exists())
|
|
self.assertTrue(Account.objects.filter(uuid=credit_account_id).exists())
|
|
|
|
# Verify TigerBeetle client was called correctly
|
|
mock_tb_client.create_accounts.assert_called_once()
|
|
self.assertEqual(len(mock_tb_client.create_accounts.call_args[0][0]), 2) # Called with 2 accounts
|
|
|
|
mock_tb_client.create_transfers.assert_called_once()
|
|
self.assertEqual(len(mock_tb_client.create_transfers.call_args[0][0]), 1) # Called with 1 transfer
|
|
transfer_arg = mock_tb_client.create_transfers.call_args[0][0][0]
|
|
self.assertEqual(transfer_arg.amount, amount)
|
|
|
|
@patch('billing_app.schemas.tigerbeetle_client')
|
|
def test_create_transfer_existing_accounts(self, mock_tb_client):
|
|
"""
|
|
Tests that a transfer is created successfully with existing accounts
|
|
and that `create_accounts` is not called.
|
|
"""
|
|
mock_tb_client.create_transfers.return_value = []
|
|
|
|
debit_account_id = uuid.uuid4()
|
|
credit_account_id = uuid.uuid4()
|
|
|
|
# Pre-populate the local database
|
|
Account.objects.create(uuid=debit_account_id)
|
|
Account.objects.create(uuid=credit_account_id)
|
|
|
|
self.assertEqual(Account.objects.count(), 2)
|
|
|
|
response = self.query(
|
|
'''
|
|
mutation createTransfer(
|
|
$debitAccountId: String!,
|
|
$creditAccountId: String!,
|
|
$amount: Int!,
|
|
$ledger: Int!,
|
|
$code: Int!
|
|
) {
|
|
createTransfer(
|
|
debitAccountId: $debitAccountId,
|
|
creditAccountId: $creditAccountId,
|
|
amount: $amount,
|
|
ledger: $ledger,
|
|
code: $code
|
|
) {
|
|
success
|
|
message
|
|
transferId
|
|
}
|
|
}
|
|
''',
|
|
variables={
|
|
'debitAccountId': str(debit_account_id),
|
|
'creditAccountId': str(credit_account_id),
|
|
'amount': 50,
|
|
'ledger': 1,
|
|
'code': 201
|
|
}
|
|
)
|
|
|
|
self.assertResponseNoErrors(response)
|
|
|
|
# Verify that create_accounts was NOT called
|
|
mock_tb_client.create_accounts.assert_not_called()
|
|
|
|
# Verify that create_transfers WAS called
|
|
mock_tb_client.create_transfers.assert_called_once()
|
|
|
|
def test_create_transfer_invalid_uuid(self):
|
|
"""
|
|
Tests that the mutation fails gracefully with an invalid UUID.
|
|
"""
|
|
response = self.query(
|
|
'''
|
|
mutation createTransfer(
|
|
$debitAccountId: String!,
|
|
$creditAccountId: String!,
|
|
$amount: Int!,
|
|
$ledger: Int!,
|
|
$code: Int!
|
|
) {
|
|
createTransfer(
|
|
debitAccountId: $debitAccountId,
|
|
creditAccountId: $creditAccountId,
|
|
amount: $amount,
|
|
ledger: $ledger,
|
|
code: $code
|
|
) {
|
|
success
|
|
message
|
|
transferId
|
|
}
|
|
}
|
|
''',
|
|
variables={
|
|
'debitAccountId': 'not-a-uuid',
|
|
'creditAccountId': str(uuid.uuid4()),
|
|
'amount': 50,
|
|
'ledger': 1,
|
|
'code': 202
|
|
}
|
|
)
|
|
|
|
self.assertResponseNoErrors(response)
|
|
content = json.loads(response.content)
|
|
result = content['data']['createTransfer']
|
|
|
|
self.assertFalse(result['success'])
|
|
self.assertEqual(result['message'], 'Invalid account ID format. Must be a valid UUID.')
|
|
|