aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstuppie2026-10-22 13:03:27 -0600
committerstuppie2026-10-22 13:03:27 -0600
commit64c251ceaf7ae2915f58b7f98e8ee7bbd27ed4e2 (patch)
tree11d58fe8a27e62542a61147534bb7caaec66facc
parented4cf78c782d669aa554422f3cbbaf24361e1c8a (diff)
downloadgeneralresearch-64c251ceaf7ae2915f58b7f98e8ee7bbd27ed4e2.tar.gz
generalresearch-64c251ceaf7ae2915f58b7f98e8ee7bbd27ed4e2.zip
VI tests and fix a little bug
-rw-r--r--generalresearch/managers/thl/ledger_manager/thl_ledger.py8
-rw-r--r--generalresearch/managers/thl/virtual_incentives_api.py2
-rw-r--r--test_utils/managers/conftest.py32
-rw-r--r--tests/managers/thl/test_wallet/test_user_payout.py220
4 files changed, 256 insertions, 6 deletions
diff --git a/generalresearch/managers/thl/ledger_manager/thl_ledger.py b/generalresearch/managers/thl/ledger_manager/thl_ledger.py
index 13a79ec..85e60d1 100644
--- a/generalresearch/managers/thl/ledger_manager/thl_ledger.py
+++ b/generalresearch/managers/thl/ledger_manager/thl_ledger.py
@@ -1151,6 +1151,7 @@ class ThlLedgerManager(LedgerManager):
PayoutType.PAYPAL: f"User Payout Paypal Complete {amt_str}",
PayoutType.CASH_IN_MAIL: f"User Payout Cash Complete {amt_str}",
PayoutType.TANGO: f"User Payout Tango Complete {amt_str}",
+ PayoutType.VIRTUAL_INCENTIVES: f"User Payout VI Complete {amt_str}",
}
description = descriptions[payout_event.payout_type]
bp_wallet_account = self.get_account_or_create_bp_wallet(user.product)
@@ -1189,6 +1190,13 @@ class ThlLedgerManager(LedgerManager):
)
if fee_amount is None:
fee_amount = (amount_usd * Decimal("0.035")).quantize(Decimal("0.01"))
+ elif payout_event.payout_type == PayoutType.VIRTUAL_INCENTIVES:
+ bp_expense_account = self.get_account_or_create_bp_expense(
+ product=user.product, expense_name="virtual_incentives"
+ )
+ if fee_amount is None:
+ # todo: What is the fee????? On the website it says 0, which I don't really believe.
+ fee_amount = (amount_usd * Decimal("0.029")).quantize(Decimal("0.01"))
else:
raise NotImplementedError()
diff --git a/generalresearch/managers/thl/virtual_incentives_api.py b/generalresearch/managers/thl/virtual_incentives_api.py
index 25d9d5f..56127a1 100644
--- a/generalresearch/managers/thl/virtual_incentives_api.py
+++ b/generalresearch/managers/thl/virtual_incentives_api.py
@@ -262,10 +262,8 @@ class VirtualIncentivesManager(PostgresManagerWithRedis):
order_data=order,
)
- # todo: What is the fee??
ledger_manager.create_tx_user_payout_complete(
user, payout_event=payout_event,
- fee_amount=Decimal('0.10'),
)
return payout_event
diff --git a/test_utils/managers/conftest.py b/test_utils/managers/conftest.py
index a9f92a2..d78332e 100644
--- a/test_utils/managers/conftest.py
+++ b/test_utils/managers/conftest.py
@@ -31,6 +31,7 @@ from generalresearch.models.definitions import Source
from generalresearch.models.thl.wallet.cashout_method import (
CashoutMethod,
TangoCashoutMethodData,
+ VICashoutMethodData,
)
from generalresearch.models.thl.wallet.definitions import Currency, PayoutType
@@ -177,12 +178,13 @@ def setup_cashoutmethod_db(
cashout_method_manager: CashoutMethodManager,
delete_cashoutmethod_db: Callable[..., None],
example_tango_cashout_methods: list[CashoutMethod],
+ example_vi_cashout_methods: list[CashoutMethod],
) -> Callable[..., None]:
def _inner():
delete_cashoutmethod_db()
- for x in example_tango_cashout_methods:
+ for x in example_tango_cashout_methods + example_vi_cashout_methods:
cashout_method_manager.create(x)
return _inner
@@ -199,8 +201,7 @@ def random_ext_id_factory(base: str = "U02") -> Callable[..., str]:
@pytest.fixture(scope="session")
-def example_tango_cashout_methods(
-) -> list[CashoutMethod]:
+def example_tango_cashout_methods() -> list[CashoutMethod]:
return [
CashoutMethod(
id=uuid4().hex,
@@ -237,6 +238,31 @@ def example_tango_cashout_methods(
]
+@pytest.fixture(scope="session")
+def example_vi_cashout_methods() -> list[CashoutMethod]:
+ return [
+ CashoutMethod(
+ id=uuid4().hex,
+ type=PayoutType.VIRTUAL_INCENTIVES,
+ ext_id="AMAZON-US",
+ name="Amazon.com Gift Card",
+ description="Amazon.com Gift Card",
+ data=VICashoutMethodData(
+ sku="AMAZON-US",
+ value_type="variable",
+ marketing="Amazon marketing copy",
+ terms="Amazon terms",
+ ),
+ user=None,
+ image_url="https://example.com/amazon.png",
+ currency=Currency.USD,
+ original_currency=Currency.USD,
+ min_value=500,
+ max_value=25_000,
+ )
+ ]
+
+
# === THL: Marketplaces ===
diff --git a/tests/managers/thl/test_wallet/test_user_payout.py b/tests/managers/thl/test_wallet/test_user_payout.py
index 2a89c97..1ef080a 100644
--- a/tests/managers/thl/test_wallet/test_user_payout.py
+++ b/tests/managers/thl/test_wallet/test_user_payout.py
@@ -12,6 +12,11 @@ from generalresearch.managers.thl.cashout_method import CashoutMethodManager
from generalresearch.managers.thl.ipinfo import GeoIpInfoManager
from generalresearch.managers.thl.ledger_manager.thl_ledger import ThlLedgerManager
from generalresearch.managers.thl.userhealth import UserIpHistoryManager
+from generalresearch.managers.thl.virtual_incentives_api import (
+ VirtualIncentivesClient,
+ VirtualIncentivesManager,
+)
+from generalresearch.managers.thl.wallet import manage_pending_cashout
from generalresearch.managers.thl.wallet.tango import TangoManager
from generalresearch.managers.thl.wallet.user_payout import (
UserPayoutEventManager,
@@ -19,6 +24,7 @@ from generalresearch.managers.thl.wallet.user_payout import (
)
from generalresearch.models.thl.definitions import PayoutStatus
from generalresearch.models.thl.payout import UserPayoutEvent
+from generalresearch.models.thl.product import UserWalletConfig
from generalresearch.models.thl.user import User
from generalresearch.models.thl.wallet.cashout_method import (
CashMailCashoutMethodData,
@@ -318,7 +324,7 @@ class TestUserRequestRedeem:
user_ip_history_manager=user_iphistory_manager,
redis_client=thl_redis_client,
)
- assert payout.request_data["amount"] == '1.74'
+ assert payout.request_data["amount"] == "1.74"
assert payout.request_data["externalRefID"] == payout.uuid
user_payout_event_manager.update(
@@ -365,6 +371,218 @@ class TestUserRequestRedeem:
)
assert thl_ledger_manager.get_account_balance(bp_expense) == -7
+ def test_user_request_redeem_virtual_incentives(
+ self,
+ product_factory,
+ payout_config,
+ user_factory,
+ user_payout_event_manager: UserPayoutEventManager,
+ cashout_method_manager: CashoutMethodManager,
+ thl_ledger_manager: ThlLedgerManager,
+ user_iphistory_manager: UserIpHistoryManager,
+ ip_record_factory,
+ thl_redis_client,
+ setup_cashoutmethod_db,
+ example_vi_cashout_methods,
+ ):
+ setup_cashoutmethod_db()
+ cm = example_vi_cashout_methods[0]
+ product = product_factory(
+ payout_config=payout_config,
+ user_wallet_config=UserWalletConfig(
+ enabled=True, supported_payout_types={PayoutType.VIRTUAL_INCENTIVES}
+ ),
+ )
+ user = user_factory(product=product)
+
+ ip_record_factory(user=user, ip="8.8.8.8")
+ bonus_ref = uuid4().hex
+ thl_ledger_manager.create_tx_user_bonus(
+ user=user,
+ amount=Decimal("10.00"),
+ ref_uuid=bonus_ref,
+ description="Fund Virtual Incentives redemption test",
+ )
+ tango_manager = Mock(spec=TangoManager)
+ tango_manager.get_exchange_rates.return_value = {}
+ virtual_incentives_manager = VirtualIncentivesManager.__new__(
+ VirtualIncentivesManager
+ )
+
+ payout = user_payout_event_manager.user_request_redeem(
+ user=user,
+ cashout_method_id=cm.id,
+ amount=USDCent(600),
+ tango_manager=tango_manager,
+ virtual_incentives_manager=virtual_incentives_manager,
+ cashout_method_manager=cashout_method_manager,
+ ledger_manager=thl_ledger_manager,
+ user_ip_history_manager=user_iphistory_manager,
+ redis_client=thl_redis_client,
+ )
+
+ assert payout.status == PayoutStatus.PENDING
+ assert payout.payout_type == PayoutType.VIRTUAL_INCENTIVES
+ assert payout.amount == 600
+ assert payout.request_data == {
+ "sku": "AMAZON-US",
+ "amount": "6.00",
+ "cashout_request_id": payout.uuid,
+ }
+ assert payout.cashout_method_uuid == cm.id
+ assert thl_ledger_manager.get_user_wallet_balance(user) == 400
+ assert user_payout_event_manager.get_by_uuid(payout.uuid) == payout
+
+ bonus_tag = f"test:user_bonus:{bonus_ref}"
+ request_tag = f"test:user_payout:{payout.uuid}:request"
+ assert len(thl_ledger_manager.get_tx_ids_by_tag(bonus_tag)) == 1
+ assert len(thl_ledger_manager.get_tx_ids_by_tag(request_tag)) == 1
+
+ user_wallet = thl_ledger_manager.get_account_or_create_user_wallet(user)
+ bp_wallet = thl_ledger_manager.get_account_or_create_bp_wallet(product)
+ pending_payout = thl_ledger_manager.get_or_create_bp_pending_payout_account(
+ product
+ )
+ assert thl_ledger_manager.get_account_balance(user_wallet) == 400
+ assert thl_ledger_manager.get_account_balance(bp_wallet) == -1_000
+ assert thl_ledger_manager.get_account_balance(pending_payout) == 600
+ assert (
+ thl_ledger_manager.get_account_filtered_balance(
+ account=user_wallet,
+ metadata_key="payoutevent",
+ metadata_value=payout.uuid,
+ )
+ == -600
+ )
+ assert (
+ thl_ledger_manager.get_account_filtered_balance(
+ account=pending_payout,
+ metadata_key="payoutevent",
+ metadata_value=payout.uuid,
+ )
+ == 600
+ )
+
+ detail = user_payout_event_manager.get_payout_detail(payout.uuid)
+ assert detail.status == PayoutStatus.PENDING
+ assert detail.description == "Amazon.com Gift Card"
+ assert not detail.transaction_info
+
+
+class TestManagePendingCashout:
+ def test_approve_virtual_incentives(
+ self,
+ product_factory,
+ payout_config,
+ user_factory,
+ user_manager,
+ user_payout_event_manager: UserPayoutEventManager,
+ cashout_method_manager: CashoutMethodManager,
+ thl_ledger_manager: ThlLedgerManager,
+ user_iphistory_manager: UserIpHistoryManager,
+ ip_record_factory,
+ thl_redis_client,
+ setup_cashoutmethod_db,
+ example_vi_cashout_methods,
+ create_main_accounts,
+ ):
+ create_main_accounts()
+ setup_cashoutmethod_db()
+ cashout_method = example_vi_cashout_methods[0]
+ product = product_factory(
+ payout_config=payout_config,
+ user_wallet_config=UserWalletConfig(
+ enabled=True,
+ supported_payout_types={PayoutType.VIRTUAL_INCENTIVES},
+ ),
+ )
+ user = user_factory(product=product)
+ ip_record_factory(user=user, ip="8.8.8.8")
+ thl_ledger_manager.create_tx_user_bonus(
+ user=user,
+ amount=Decimal("10.00"),
+ ref_uuid=uuid4().hex,
+ description="Fund Virtual Incentives approval test",
+ )
+ tango_manager = Mock(spec=TangoManager)
+ tango_manager.get_exchange_rates.return_value = {}
+ virtual_incentives_manager = VirtualIncentivesManager.__new__(
+ VirtualIncentivesManager
+ )
+ virtual_incentives_manager.client = Mock(spec=VirtualIncentivesClient)
+
+ payout = user_payout_event_manager.user_request_redeem(
+ user=user,
+ cashout_method_id=cashout_method.id,
+ amount=USDCent(600),
+ tango_manager=tango_manager,
+ virtual_incentives_manager=virtual_incentives_manager,
+ cashout_method_manager=cashout_method_manager,
+ ledger_manager=thl_ledger_manager,
+ user_ip_history_manager=user_iphistory_manager,
+ redis_client=thl_redis_client,
+ )
+
+ pending_payout = thl_ledger_manager.get_or_create_bp_pending_payout_account(
+ product
+ )
+ user_wallet = thl_ledger_manager.get_account_or_create_user_wallet(user)
+
+ assert thl_ledger_manager.get_account_balance(user_wallet) == 400
+ assert thl_ledger_manager.get_account_balance(pending_payout) == 600
+
+ order = {
+ "status": PayoutStatus.COMPLETE,
+ "number": "vi-order-1",
+ "accounts": [{"sku": "AMAZON-US", "amount": "6.00"}],
+ }
+ virtual_incentives_manager.client.create_order.return_value = order
+
+ result = manage_pending_cashout(
+ payout_id=payout.uuid,
+ new_status=PayoutStatus.APPROVED,
+ user_payout_event_manager=user_payout_event_manager,
+ user_ip_history_manager=user_iphistory_manager,
+ user_manager=user_manager,
+ ledger_manager=thl_ledger_manager,
+ virtual_incentives_manager=virtual_incentives_manager,
+ )
+
+ assert result.uuid == payout.uuid
+ assert result.status == PayoutStatus.COMPLETE
+ assert result.ext_ref_id == "vi-order-1"
+ assert result.order_data == order
+ virtual_incentives_manager.client.create_order.assert_called_once_with(
+ cashout_request_id=payout.uuid,
+ sku="AMAZON-US",
+ amount=Decimal("6.00"),
+ )
+
+ stored_payout = user_payout_event_manager.get_by_uuid(payout.uuid)
+ assert stored_payout.status == PayoutStatus.COMPLETE
+ assert stored_payout.ext_ref_id == "vi-order-1"
+ assert stored_payout.order_data == order
+
+ request_tag = f"test:user_payout:{payout.uuid}:request"
+ complete_tag = f"test:user_payout:{payout.uuid}:complete"
+ assert len(thl_ledger_manager.get_tx_ids_by_tag(request_tag)) == 1
+ assert len(thl_ledger_manager.get_tx_ids_by_tag(complete_tag)) == 1
+
+ bp_wallet = thl_ledger_manager.get_account_or_create_bp_wallet(product)
+
+ cash = thl_ledger_manager.get_account_cash()
+ vi_expense = thl_ledger_manager.get_account_or_create_bp_expense_by_uuid(
+ product_uuid=product.uuid,
+ expense_name="virtual_incentives",
+ )
+ assert thl_ledger_manager.get_account_balance(user_wallet) == 400
+ assert thl_ledger_manager.get_account_balance(pending_payout) == 0
+ assert thl_ledger_manager.get_account_balance(cash) == -600
+ # $10 bribe, 17c expense (2.9% x $6)
+ assert thl_ledger_manager.get_account_balance(bp_wallet) == -10_17
+ assert thl_ledger_manager.get_account_balance(vi_expense) == -17
+ assert thl_ledger_manager.check_ledger_balanced()
+
def test_user_request_redeem_paypal_insufficient_balance(
self,
user_with_wallet: User,