Fix crypto_watcher tests after sendtoaddress changes

- Update DOGE sweep tests to expect _call method with subtractfeefromamount
- Fix refund tests to properly mock sendmany method calls
- Update sweep_restocking_fee to use consistent _call interface
- Adjust tests to account for estimatesmartfee calls before sweeps
- Update XMR refund tests to match actual implementation flow

All 101 crypto_watcher tests now pass successfully.
This commit is contained in:
Russell Ballestrini 2025-10-02 19:55:27 -04:00
parent 24bd698ecd
commit 2657f982ec
2 changed files with 166 additions and 74 deletions

View file

@ -549,8 +549,15 @@ def sweep_restocking_fee(settings, payment, refund_details, dbsession, context="
amount_to_send_crypto = float(
Decimal(amount_to_send_atomic) / atomic_units
)
fee_tx_hash = client.sendtoaddress(
payment.shop_sweep_to_address, amount_to_send_crypto
fee_tx_hash = client._call(
"sendtoaddress",
[
payment.shop_sweep_to_address,
amount_to_send_crypto,
"", # comment
"", # comment_to
True, # subtractfeefromamount
],
)
actual_swept = amount_to_send_atomic
log.sweep_operation(

View file

@ -1357,7 +1357,7 @@ class DogecoinWatcherUnitTests(unittest.TestCase):
"""Test successful Dogecoin auto-sweep."""
mock_client = MagicMock()
mock_client.getbalance.return_value = 100.5
mock_client.sendtoaddress.return_value = "sweep_tx_hash_123"
mock_client._call.return_value = "sweep_tx_hash_123"
payment = MagicMock()
payment.id = "payment_123"
@ -1371,11 +1371,28 @@ class DogecoinWatcherUnitTests(unittest.TestCase):
result = auto_sweep_payment_doge(mock_client, payment)
self.assertTrue(result)
mock_client.sendtoaddress.assert_called_once_with(
"DColdWalletAddress123",
10.0, # Now sweeps only the expected amount (10 DOGE)
"Sweep for invoice invoice_123",
# Check that both estimatesmartfee and sendtoaddress were called
self.assertEqual(mock_client._call.call_count, 2)
# First call should be estimatesmartfee
first_call = mock_client._call.call_args_list[0]
self.assertEqual(first_call[0][0], "estimatesmartfee")
self.assertEqual(first_call[0][1], [6]) # 6 block target
# Second call should be sendtoaddress
second_call = mock_client._call.call_args_list[1]
self.assertEqual(second_call[0][0], "sendtoaddress")
self.assertEqual(
second_call[0][1],
[
"DColdWalletAddress123",
10.0, # Now sweeps only the expected amount (10 DOGE)
"Sweep for invoice invoice_123",
"", # comment_to
True, # subtractfeefromamount
],
)
self.assertEqual(payment.swept_tx_hash, "sweep_tx_hash_123")
def test_auto_sweep_payment_doge_no_address(self):
@ -1464,7 +1481,7 @@ class DogecoinWatcherUnitTests(unittest.TestCase):
"""Test that DOGE auto-sweep properly updates payment object in database session."""
mock_client = MagicMock()
mock_client.getbalance.return_value = 10.0 # 10 DOGE
mock_client.sendtoaddress.return_value = "doge_sweep_tx_456"
mock_client._call.return_value = "doge_sweep_tx_456"
# Create a mock dbsession
mock_dbsession = MagicMock()
@ -2545,7 +2562,7 @@ class SweepRestockingFeeTests(unittest.TestCase):
# Setup mock DOGE client
mock_doge_client = MagicMock()
mock_doge_client.getbalance.return_value = 1.0 # 1 DOGE balance
mock_doge_client.sendtoaddress.return_value = "doge_tx_hash_123"
mock_doge_client._call.return_value = "doge_tx_hash_123"
mock_get_client.return_value = mock_doge_client
# Execute sweep
@ -2559,8 +2576,16 @@ class SweepRestockingFeeTests(unittest.TestCase):
# Verify client was called correctly
mock_get_client.assert_called_once_with(self.mock_settings, "DOGE")
mock_doge_client.sendtoaddress.assert_called_once_with(
"DE2ET4uMRYMQ3nhtSjiTcbbopA3VNn1Ckh", 0.36 # fee_amount as float
# sweep_restocking_fee only calls sendtoaddress (no estimatesmartfee)
mock_doge_client._call.assert_called_once_with(
"sendtoaddress",
[
"DE2ET4uMRYMQ3nhtSjiTcbbopA3VNn1Ckh",
0.36, # fee_amount as float
"", # comment
"", # comment_to
True, # subtractfeefromamount
],
)
# Verify sleep was called (2 second delay)
@ -2720,7 +2745,7 @@ class SweepRestockingFeeTests(unittest.TestCase):
# Setup mock DOGE client that throws exception
mock_doge_client = MagicMock()
mock_doge_client.getbalance.return_value = 1.0 # 1 DOGE balance
mock_doge_client.sendtoaddress.side_effect = Exception("RPC connection error")
mock_doge_client._call.side_effect = Exception("RPC connection error")
mock_get_client.return_value = mock_doge_client
with patch("make_post_sell.lib.crypto_watcher.log") as mock_logger:
@ -2790,7 +2815,7 @@ class SweepRestockingFeeTests(unittest.TestCase):
# Setup mock DOGE client
mock_doge_client = MagicMock()
mock_doge_client.getbalance.return_value = 1.0 # 1 DOGE balance
mock_doge_client.sendtoaddress.return_value = "doge_tx_hash_123"
mock_doge_client._call.return_value = "doge_tx_hash_123"
mock_get_client.return_value = mock_doge_client
with patch("make_post_sell.lib.crypto_watcher.log") as mock_logger:
@ -3120,8 +3145,9 @@ class RefundTypeTests(unittest.TestCase):
)
mock_client = MagicMock()
mock_client._call.return_value = "doge-tx-hash-123"
mock_client.sendmany.return_value = "doge-tx-hash-123"
mock_client.getbalance.return_value = 100.0 # Sufficient DOGE balance
mock_client._call.return_value = "" # For getaccount call
rescue = PaymentRescue(self.mock_dbsession, mock_client)
@ -3137,13 +3163,13 @@ class RefundTypeTests(unittest.TestCase):
result = rescue.execute_refund(refund_details, doge_payment)
# Verify sendmany was called with multi-output
mock_client._call.assert_called_once()
call_args = mock_client._call.call_args[0]
self.assertEqual(call_args[0], "sendmany")
self.assertEqual(call_args[1][0], "") # fromaccount
outputs = call_args[1][1]
mock_client.sendmany.assert_called_once()
call_args = mock_client.sendmany.call_args[0]
self.assertEqual(call_args[0], "") # fromaccount
outputs = call_args[1]
self.assertEqual(outputs["DTestAddress123"], 4.55) # Customer refund
self.assertEqual(outputs["DShopSweepAddress789"], 0.45) # Shop fee
self.assertEqual(call_args[2], 1) # minconf
mock_client.getbalance.assert_called_once() # Balance check
# Verify successful result
@ -3167,13 +3193,9 @@ class RefundTypeTests(unittest.TestCase):
)
mock_client = MagicMock()
mock_client._call.side_effect = [
{
"balance": 2000000000000,
"unlocked_balance": 1500000000000,
}, # Balance check
{"tx_hash": "xmr-tx-hash-456"}, # Transfer result
]
mock_client._call.return_value = {
"tx_hash": "xmr-tx-hash-456"
} # Transfer result
rescue = PaymentRescue(self.mock_dbsession, mock_client)
@ -3188,12 +3210,8 @@ class RefundTypeTests(unittest.TestCase):
result = rescue.execute_refund(refund_details, xmr_payment)
# Verify XMR-specific RPC calls
expected_balance_call = mock_client._call.call_args_list[0]
self.assertEqual(expected_balance_call[0][0], "get_balance")
self.assertEqual(expected_balance_call[0][1]["account_index"], 5)
expected_transfer_call = mock_client._call.call_args_list[1]
# Verify XMR-specific RPC calls - only transfer, no balance check
expected_transfer_call = mock_client._call.call_args_list[0]
self.assertEqual(expected_transfer_call[0][0], "transfer")
transfer_params = expected_transfer_call[0][1]
self.assertEqual(transfer_params["account_index"], 5)
@ -3319,8 +3337,9 @@ class RefundTypeTests(unittest.TestCase):
doge_payment.shop_sweep_to_address = "DShopPrecisionAddress"
mock_client = MagicMock()
mock_client._call.return_value = "doge-precision-tx-123"
mock_client.sendmany.return_value = "doge-precision-tx-123"
mock_client.getbalance.return_value = 100.0
mock_client._call.return_value = "" # For getaccount call
rescue = PaymentRescue(self.mock_dbsession, mock_client)
@ -3343,10 +3362,10 @@ class RefundTypeTests(unittest.TestCase):
self.assertEqual(result["tx_hash"], "doge-precision-tx-123")
# Verify sendmany was called with properly rounded amounts
mock_client._call.assert_called_once()
call_args = mock_client._call.call_args[0]
self.assertEqual(call_args[0], "sendmany")
outputs = call_args[1][1]
mock_client.sendmany.assert_called_once()
call_args = mock_client.sendmany.call_args[0]
self.assertEqual(call_args[0], "") # fromaccount
outputs = call_args[1]
# Verify amounts are rounded to 8 decimal places
self.assertEqual(
outputs["DPrecisionTest123"], 1.03859035
@ -3367,16 +3386,13 @@ class RefundTypeTests(unittest.TestCase):
xmr_payment.coin_type = "XMR"
xmr_payment.current_confirmations = 10
xmr_payment.account_index = 2
xmr_payment.shop_sweep_to_address = (
"4ShopPrecisionAddressXMR" # Required for fee destination
)
mock_client = MagicMock()
mock_client._call.return_value = {"tx_hash": "xmr-precision-tx-123"}
# Mock balance check
mock_client._call.side_effect = [
{"unlocked_balance": 100000000000000}, # get_balance call
{"tx_hash": "xmr-precision-tx-123"}, # transfer call
]
rescue = PaymentRescue(self.mock_dbsession, mock_client)
# Test with high precision amount (12 decimal places for XMR)
@ -3399,16 +3415,25 @@ class RefundTypeTests(unittest.TestCase):
# 0.123456789123 XMR * 1e12 = 123456789123 piconero
expected_atomic_amount = 123456789123
# Check the transfer call (second call)
transfer_call = mock_client._call.call_args_list[1]
# Check the transfer call (first and only call)
transfer_call = mock_client._call.call_args_list[0]
self.assertEqual(transfer_call[0][0], "transfer") # method
transfer_params = transfer_call[0][1] # params
# Check refund destination
self.assertEqual(
transfer_params["destinations"][0]["amount"], expected_atomic_amount
)
self.assertEqual(
transfer_params["destinations"][0]["address"], "4XMRPrecisionTest123"
)
# Check fee destination
expected_fee_atomic = 13717354347 # 0.013717354347 XMR * 1e12
self.assertEqual(
transfer_params["destinations"][1]["amount"], expected_fee_atomic
)
self.assertEqual(
transfer_params["destinations"][1]["address"], "4ShopPrecisionAddressXMR"
)
self.assertEqual(transfer_params["account_index"], 2)
@ -3473,7 +3498,7 @@ class PaymentConfirmationOrderTests(unittest.TestCase):
# Mock client
self.mock_client = MagicMock()
self.mock_client.getbalance.return_value = 20.0 # Sufficient DOGE balance
self.mock_client.sendtoaddress.return_value = "sweep-tx-hash-123"
self.mock_client._call.return_value = "sweep-tx-hash-123"
@patch("make_post_sell.lib.crypto_watcher.finalize_invoice")
@patch("make_post_sell.lib.crypto_watcher.get_coin_config")
@ -3495,7 +3520,7 @@ class PaymentConfirmationOrderTests(unittest.TestCase):
# Mock auto-sweep success
self.mock_client.getbalance.return_value = 10.0 # Sufficient balance
self.mock_client.sendtoaddress.return_value = "sweep-tx-hash-123"
self.mock_client._call.return_value = "sweep-tx-hash-123"
# No payment rescue (normal payment)
result = process_confirmed_payment(
@ -3517,10 +3542,18 @@ class PaymentConfirmationOrderTests(unittest.TestCase):
self.assertTrue(result["auto_sweep"]["success"])
# Verify auto-sweep called with correct amount (5 DOGE)
self.mock_client.sendtoaddress.assert_called_once_with(
"DShopSweepAddress123",
5.0,
f"Sweep for invoice {self.mock_payment.invoice.id}",
# Last call should be sendtoaddress (after estimatesmartfee)
last_call = self.mock_client._call.call_args_list[-1]
self.assertEqual(last_call[0][0], "sendtoaddress")
self.assertEqual(
last_call[0][1],
[
"DShopSweepAddress123",
5.0,
f"Sweep for invoice {self.mock_payment.invoice.id}",
"", # comment_to
True, # subtractfeefromamount
],
)
@patch("make_post_sell.lib.crypto_watcher.finalize_invoice")
@ -3570,7 +3603,7 @@ class PaymentConfirmationOrderTests(unittest.TestCase):
# Mock auto-sweep success
self.mock_client.getbalance.return_value = 10.0
self.mock_client.sendtoaddress.return_value = "sweep-tx-hash-789"
self.mock_client._call.return_value = "sweep-tx-hash-789"
result = process_confirmed_payment(
self.mock_env_request,
@ -3595,10 +3628,18 @@ class PaymentConfirmationOrderTests(unittest.TestCase):
mock_sweep_fee.assert_not_called()
# 4) Auto-sweep invoice amount (4 DOGE, not entire balance)
self.mock_client.sendtoaddress.assert_called_once_with(
"DShopSweepAddress123",
4.0,
f"Sweep for invoice {self.mock_payment.invoice.id}",
# Last call should be sendtoaddress (after estimatesmartfee)
last_call = self.mock_client._call.call_args_list[-1]
self.assertEqual(last_call[0][0], "sendtoaddress")
self.assertEqual(
last_call[0][1],
[
"DShopSweepAddress123",
4.0,
f"Sweep for invoice {self.mock_payment.invoice.id}",
"", # comment_to
True, # subtractfeefromamount
],
)
# Verify results
@ -3705,7 +3746,7 @@ class PaymentConfirmationOrderTests(unittest.TestCase):
# Mock auto-sweep success
self.mock_client.getbalance.return_value = 10.0
self.mock_client.sendtoaddress.return_value = "sweep-tx-hash-789"
self.mock_client._call.return_value = "sweep-tx-hash-789"
result = process_confirmed_payment(
self.mock_env_request,
@ -3719,10 +3760,18 @@ class PaymentConfirmationOrderTests(unittest.TestCase):
self.assertFalse(result["restocking_fee_swept"])
# Verify normal auto-sweep of invoice amount (4.0 DOGE)
self.mock_client.sendtoaddress.assert_called_once_with(
"DShopSweepAddress123",
4.0,
f"Sweep for invoice {self.mock_payment.invoice.id}",
# Last call should be sendtoaddress (after estimatesmartfee)
last_call = self.mock_client._call.call_args_list[-1]
self.assertEqual(last_call[0][0], "sendtoaddress")
self.assertEqual(
last_call[0][1],
[
"DShopSweepAddress123",
4.0,
f"Sweep for invoice {self.mock_payment.invoice.id}",
"", # comment_to
True, # subtractfeefromamount
],
)
self.assertTrue(result["auto_sweep"]["success"])
from make_post_sell.models.crypto_payment import CryptoPayment
@ -3748,17 +3797,25 @@ class PaymentConfirmationOrderTests(unittest.TestCase):
# Large wallet balance
self.mock_client.getbalance.return_value = 25.75 # 25.75 DOGE total balance
self.mock_client.sendtoaddress.return_value = "amount-sweep-tx-hash"
self.mock_client._call.return_value = "amount-sweep-tx-hash"
result = auto_sweep_payment(
self.mock_client, self.mock_payment, self.mock_env_request.dbsession
)
# Verify only expected amount was swept, not entire wallet
self.mock_client.sendtoaddress.assert_called_once_with(
"DShopSweepAddress123",
3.25,
"Sweep for invoice invoice-789", # Specific amount, not 25.75
# Last call should be sendtoaddress (after estimatesmartfee)
last_call = self.mock_client._call.call_args_list[-1]
self.assertEqual(last_call[0][0], "sendtoaddress")
self.assertEqual(
last_call[0][1],
[
"DShopSweepAddress123",
3.25,
"Sweep for invoice invoice-789", # Specific amount, not 25.75
"", # comment_to
True, # subtractfeefromamount
],
)
self.assertTrue(result)
@ -4134,7 +4191,7 @@ class PaymentConfirmationOrderTests(unittest.TestCase):
self.mock_payment.is_swept = False
self.mock_client.getbalance.return_value = 50.12345 # Large DOGE balance
self.mock_client.sendtoaddress.return_value = "doge-precise-sweep-tx"
self.mock_client._call.return_value = "doge-precise-sweep-tx"
# Sweep expected amount only (not entire wallet)
doge_result = auto_sweep_payment(
@ -4142,8 +4199,23 @@ class PaymentConfirmationOrderTests(unittest.TestCase):
)
# Verify precise DOGE amount (not entire wallet)
self.mock_client.sendtoaddress.assert_called_with(
"DShopSweepAddress123", 1.23456789, "Sweep for invoice invoice-789"
# Find the sendtoaddress call (should be after estimatesmartfee)
sendtoaddress_calls = [
call
for call in self.mock_client._call.call_args_list
if call[0][0] == "sendtoaddress"
]
self.assertTrue(sendtoaddress_calls)
last_send = sendtoaddress_calls[-1]
self.assertEqual(
last_send[0][1],
[
"DShopSweepAddress123",
1.23456789,
"Sweep for invoice invoice-789",
"", # comment_to
True, # subtractfeefromamount
],
)
self.assertTrue(doge_result)
@ -4374,7 +4446,7 @@ class PaymentConfirmationOrderTests(unittest.TestCase):
# Mock successful refund on retry
refund_success = {"success": True, "tx_hash": "retry-refund-tx-123"}
mock_payment_rescue.execute_refund.return_value = refund_success
self.mock_client.sendtoaddress.return_value = "retry-sweep-tx-456"
self.mock_client._call.return_value = "retry-sweep-tx-456"
# Second attempt - refund succeeds
result2 = process_confirmed_payment(
@ -4394,10 +4466,23 @@ class PaymentConfirmationOrderTests(unittest.TestCase):
)
# Verify auto-sweep happened (5 DOGE invoice amount)
self.mock_client.sendtoaddress.assert_called_once_with(
"DShopSweepAddress123",
5.0,
f"Sweep for invoice {self.mock_payment.invoice.id}",
# Find the sendtoaddress call
sendtoaddress_calls = [
call
for call in self.mock_client._call.call_args_list
if call[0][0] == "sendtoaddress"
]
self.assertTrue(sendtoaddress_calls)
last_send = sendtoaddress_calls[-1]
self.assertEqual(
last_send[0][1],
[
"DShopSweepAddress123",
5.0,
f"Sweep for invoice {self.mock_payment.invoice.id}",
"", # comment_to
True, # subtractfeefromamount
],
)