From 203066cb5228ce3bcadb940b9aca1b6a36dceb4e Mon Sep 17 00:00:00 2001 From: stuppie Date: Fri, 18 Sep 2026 11:16:22 -0600 Subject: user ip history and geo ip information fixes, fixtures, and tests --- test_utils/managers/conftest.py | 107 ++++++++++++++--------- test_utils/models/thl/conftest.py | 174 ++++++++------------------------------ 2 files changed, 102 insertions(+), 179 deletions(-) (limited to 'test_utils') diff --git a/test_utils/managers/conftest.py b/test_utils/managers/conftest.py index 391e6bf..72be79e 100644 --- a/test_utils/managers/conftest.py +++ b/test_utils/managers/conftest.py @@ -1,19 +1,33 @@ from __future__ import annotations import random +from collections import defaultdict from collections.abc import Callable from datetime import datetime from typing import TYPE_CHECKING +from unittest.mock import Mock from uuid import uuid4 import pytest +from grip_client.mmdb.models import ( + GRIPAnonymousRecord, + GRIPAsnRecord, + GRIPCountryRecord, + GRIPMMDBLookupResult, +) from generalresearch.managers.thl.cashout_method import ( CashoutMethodManager, ) +from generalresearch.managers.thl.ipinfo import GeoIpInfoManager from generalresearch.managers.thl.user_streak import ( UserStreakManager, ) +from generalresearch.managers.thl.userhealth import ( + AuditLogManager, + IPRecordManager, + UserIpHistoryManager, +) from generalresearch.models.definitions import Source from generalresearch.models.thl.wallet.cashout_method import ( CashoutMethod, @@ -24,15 +38,6 @@ from generalresearch.models.thl.wallet.definitions import Currency, PayoutType if TYPE_CHECKING: from generalresearch.managers.spectrum.survey import SpectrumSurveyManager from generalresearch.managers.thl.buyer import BuyerManager - from generalresearch.managers.thl.ipinfo import ( - GeoIpInfoManager, - IPGeonameManager, - ) - from generalresearch.managers.thl.userhealth import ( - AuditLogManager, - IPRecordManager, - UserIpHistoryManager, - ) from generalresearch.models.thl.user import User from generalresearch.pg_helper import PostgresConfig from generalresearch.redis_helper import RedisConfig @@ -51,41 +56,39 @@ def audit_log_manager(thl_web_rw: PostgresConfig) -> AuditLogManager: return AuditLogManager(pg_config=thl_web_rw) -@pytest.fixture(scope="session") -def ip_geoname_manager(thl_web_rw: PostgresConfig) -> IPGeonameManager: - assert thl_web_rw.dsn.path - assert "/unittest-" in thl_web_rw.dsn.path - - from generalresearch.managers.thl.ipinfo import IPGeonameManager - - return IPGeonameManager(pg_config=thl_web_rw) - - -@pytest.fixture(scope="session") +@pytest.fixture def ip_record_manager( - thl_web_rw: PostgresConfig, thl_redis_config: RedisConfig + thl_web_rw: PostgresConfig, + thl_redis_config: RedisConfig, + geoip_info_manager: GeoIpInfoManager, ) -> IPRecordManager: assert thl_web_rw.dsn.path assert "/unittest-" in thl_web_rw.dsn.path from generalresearch.managers.thl.userhealth import IPRecordManager - return IPRecordManager(pg_config=thl_web_rw, redis_config=thl_redis_config) + return IPRecordManager( + pg_config=thl_web_rw, + redis_config=thl_redis_config, + geoip_info_manager=geoip_info_manager, + ) -@pytest.fixture(scope="session") +@pytest.fixture def user_iphistory_manager( - thl_web_rw: PostgresConfig, thl_redis_config: RedisConfig + thl_web_rw: PostgresConfig, + thl_redis_config: RedisConfig, + geoip_info_manager: GeoIpInfoManager, ) -> UserIpHistoryManager: assert thl_web_rw.dsn.path assert "/unittest-" in thl_web_rw.dsn.path - from generalresearch.managers.thl.userhealth import ( - UserIpHistoryManager, + return UserIpHistoryManager( + pg_config=thl_web_rw, + redis_config=thl_redis_config, + geoip_info_manager=geoip_info_manager, ) - return UserIpHistoryManager(pg_config=thl_web_rw, redis_config=thl_redis_config) - @pytest.fixture(scope="function") def user_iphistory_manager_clear_cache(user_iphistory_manager, user: User): @@ -96,16 +99,42 @@ def user_iphistory_manager_clear_cache(user_iphistory_manager, user: User): user_iphistory_manager.delete_user_ip_history_cache(user_id=user.user_id) -@pytest.fixture(scope="session") -def geoipinfo_manager( - thl_web_rw: PostgresConfig, thl_redis_config: RedisConfig -) -> GeoIpInfoManager: - assert thl_web_rw.dsn.path - assert "/unittest-" in thl_web_rw.dsn.path +@pytest.fixture +def grip_lookup_results() -> dict[str, GRIPMMDBLookupResult]: + return defaultdict( + lambda: GRIPMMDBLookupResult( + country=GRIPCountryRecord(), + anonymous=GRIPAnonymousRecord(), + asn=GRIPAsnRecord(), + ), + { + "8.8.8.8": GRIPMMDBLookupResult( + country=GRIPCountryRecord(country_iso="US"), + anonymous=GRIPAnonymousRecord(is_anonymous=False), + asn=GRIPAsnRecord( + asn=15169, + network_operator="Google", + ), + ), + "1.1.1.1": GRIPMMDBLookupResult( + country=GRIPCountryRecord(country_iso="AU"), + anonymous=GRIPAnonymousRecord(is_anonymous=True), + asn=GRIPAsnRecord( + asn=13335, + network_operator="Cloudflare", + ), + ), + }, + ) - from generalresearch.managers.thl.ipinfo import GeoIpInfoManager - return GeoIpInfoManager(pg_config=thl_web_rw, redis_config=thl_redis_config) +@pytest.fixture(scope="function") +def geoip_info_manager( + grip_lookup_results: dict[str, GRIPMMDBLookupResult], +) -> GeoIpInfoManager: + manager = GeoIpInfoManager(grip_token="test-token") + manager.grip_mmdb.lookup = Mock(side_effect=grip_lookup_results.__getitem__) + return manager @pytest.fixture(scope="session") @@ -193,10 +222,10 @@ def example_tango_cashout_methods( last_updated=datetime.fromisoformat("2021-06-23T20:45:38.239182Z"), is_live=True, type=PayoutType.TANGO, - ext_id='U025035', + ext_id="U025035", name="Safeway eGift Card $25", data=TangoCashoutMethodData( - value_type="fixed", countries=["US"], utid='U025035' + value_type="fixed", countries=["US"], utid="U025035" ), user=None, image_url="https://d30s7yzk2az89n.cloudfront.net/images/brands/b694446-1200w-326ppi.png", @@ -209,7 +238,7 @@ def example_tango_cashout_methods( last_updated=datetime.fromisoformat("2021-06-23T20:45:38.239182Z"), is_live=True, type=PayoutType.TANGO, - ext_id='U006961', + ext_id="U006961", name="Amazon.it Gift Certificate", data=TangoCashoutMethodData( value_type="variable", countries=["IT"], utid="U006961" diff --git a/test_utils/models/thl/conftest.py b/test_utils/models/thl/conftest.py index d20fdc4..433003c 100644 --- a/test_utils/models/thl/conftest.py +++ b/test_utils/models/thl/conftest.py @@ -14,10 +14,9 @@ from grip_client.enums import AccessType from pydantic import PositiveInt from generalresearch.currency import USDCent -from generalresearch.managers.thl.payout import ( - BusinessPayoutEventManager, - UserPayoutEventManager, -) +from generalresearch.managers.thl.ipinfo import GeoIpInfoManager +from generalresearch.managers.thl.payout import BusinessPayoutEventManager +from generalresearch.managers.thl.wallet.user_payout import UserPayoutEventManager from generalresearch.models.custom_types import ( AwareDatetimeISO, IPvAnyAddressStr, @@ -29,6 +28,7 @@ from generalresearch.models.thl.definitions import ( PayoutStatus, Status, ) +from generalresearch.models.thl.ipinfo import GeoIPInformation from generalresearch.models.thl.payout import UserPayoutEvent from generalresearch.models.thl.user import User from generalresearch.models.thl.userhealth import AuditLogLevel @@ -36,10 +36,6 @@ from generalresearch.models.thl.wallet.definitions import PayoutType from generalresearch.pg_helper import PostgresConfig if TYPE_CHECKING: - from generalresearch.managers.thl.ipinfo import ( - IPGeonameManager, - IPInformationManager, - ) from generalresearch.managers.thl.ledger_manager.thl_ledger import ThlLedgerManager from generalresearch.managers.thl.product import ProductManager from generalresearch.managers.thl.session import SessionManager @@ -50,7 +46,6 @@ if TYPE_CHECKING: from generalresearch.models.gr.business import Business from generalresearch.models.gr.team import Team from generalresearch.models.legacy.bucket import Bucket - from generalresearch.models.thl.ipinfo import IPGeoname, IPInformation from generalresearch.models.thl.payout import BrokerageProductPayoutEvent from generalresearch.models.thl.product import ( PayoutConfig, @@ -438,143 +433,44 @@ def unsaved_product(product_factory: Callable[..., Product]) -> Product: return product_factory(save=False) -# --- IP Geoname --- - - -@pytest.fixture -def ip_geoname_factory( - ip_geoname_manager: IPGeonameManager, -) -> Callable[..., IPGeoname]: - - def _inner( - save: bool = True, - geoname_id: PositiveInt | None = None, - continent_code: str | None = None, - continent_name: str | None = None, - country_iso: str | None = None, - country_name: str | None = None, - subdivision_1_iso: str | None = None, - subdivision_1_name: str | None = None, - subdivision_2_iso: str | None = None, - subdivision_2_name: str | None = None, - city_name: str | None = None, - metro_code: int | None = None, - time_zone: str | None = None, - is_in_european_union: bool | None = None, - ) -> IPGeoname: - if save: - return ip_geoname_manager.create( - geoname_id=geoname_id or randint(1, 999_999_999), - continent_code=continent_code or "na", - continent_name=continent_name or "North America", - country_iso=country_iso or "us", - country_name=country_name or "United States", - subdivision_1_iso=subdivision_1_iso or "fl", - subdivision_1_name=subdivision_1_name or "Florida", - subdivision_2_iso=subdivision_2_iso, - subdivision_2_name=subdivision_2_name, - city_name=city_name, - metro_code=metro_code, - time_zone=time_zone, - is_in_european_union=is_in_european_union, - ) - else: - raise ValueError("Unsaved IPGeoname not yet supported") - - return _inner - - -@pytest.fixture() -def ip_geoname(ip_geoname_factory: Callable[..., IPGeoname]) -> IPGeoname: - return ip_geoname_factory(save=True) - - -@pytest.fixture() -def unsaved_ip_geoname(ip_geoname_factory: Callable[..., IPGeoname]) -> IPGeoname: - return ip_geoname_factory(save=True) - - -# --- IP Information --- +# --- GeoIP Information --- @pytest.fixture -def ip_information_factory( - ip_information_manager: IPInformationManager, -) -> Callable[..., IPInformation]: +def geoip_information_factory( + geoip_info_manager: GeoIpInfoManager, +) -> Callable[..., GeoIPInformation]: def _inner( - save: bool = True, ip: IPvAnyAddressStr | None = None, - geoname_id: PositiveInt | None = None, country_iso: str | None = None, - registered_country_iso: str | None = None, + country_name: str | None = None, is_anonymous: bool | None = None, - is_anonymous_vpn: bool | None = None, - is_hosting_provider: bool | None = None, - is_public_proxy: bool | None = None, - is_tor_exit_node: bool | None = None, - is_residential_proxy: bool | None = None, autonomous_system_number: PositiveInt | None = None, autonomous_system_organization: str | None = None, - domain: str | None = None, - isp: str | None = None, - mobile_country_code: str | None = None, - mobile_network_code: str | None = None, - network: str | None = None, - organization: str | None = None, - static_ip_score: float | None = None, - user_type: AccessType | None = None, - postal_code: str | None = None, - latitude: Decimal | None = None, - longitude: Decimal | None = None, - accuracy_radius: int | None = None, - ) -> IPInformation: - - if save: - return ip_information_manager.create( - ip=ip or fake.ipv4_public(), - geoname_id=geoname_id, - country_iso=country_iso or fake.country_code(), - registered_country_iso=registered_country_iso, - is_anonymous=is_anonymous, - is_anonymous_vpn=is_anonymous_vpn, - is_hosting_provider=is_hosting_provider, - is_public_proxy=is_public_proxy, - is_tor_exit_node=is_tor_exit_node, - is_residential_proxy=is_residential_proxy, - autonomous_system_number=autonomous_system_number, - autonomous_system_organization=autonomous_system_organization, - domain=domain, - isp=isp, - mobile_country_code=mobile_country_code, - mobile_network_code=mobile_network_code, - network=network, - organization=organization, - static_ip_score=static_ip_score, - user_type=user_type, - postal_code=postal_code, - latitude=latitude, - longitude=longitude, - accuracy_radius=accuracy_radius, - ) - else: - raise ValueError("Unsaved IP Information not supported yet") + access_type: AccessType | None = None, + ) -> GeoIPInformation: + + return GeoIPInformation( + country_iso=country_iso or fake.country_code("alpha-2"), + access_type=access_type, + is_anonymous=is_anonymous, + autonomous_system_number=autonomous_system_number, + autonomous_system_organization=autonomous_system_organization, + country_name=country_name, + ip=ip or fake.ipv4_public(), + subdivision_1_iso=None, + subdivision_1_name=None, + ) return _inner @pytest.fixture -def ip_information( - ip_information_factory: Callable[..., IPInformation], -) -> IPInformation: - return ip_information_factory(save=True) - - -@pytest.fixture() -def unsaved_ip_information( - ip_information_factory: Callable[..., IPInformation], -) -> IPInformation: - return ip_information_factory(save=False) +def geoip_information( + geoip_information_factory: Callable[..., GeoIPInformation], +) -> GeoIPInformation: + return geoip_information_factory() # --- IP Record --- @@ -584,7 +480,7 @@ def unsaved_ip_information( def ip_record_factory(ip_record_manager: IPRecordManager) -> Callable[..., IPRecord]: def _inner( - user_id: PositiveInt, + user: User, save: bool = True, ip: IPvAnyAddressStr | None = None, forwarded_ip1: IPvAnyAddressStr | None = None, @@ -597,15 +493,13 @@ def ip_record_factory(ip_record_manager: IPRecordManager) -> Callable[..., IPRec if save: return ip_record_manager.create( - user_id=user_id, + user_id=user.to_user_ref().user_id, ip=ip or fake.ipv4_public(), - forwarded_ip1=(forwarded_ip1 or fake.ipv4_public()), - forwarded_ip2=( - forwarded_ip2 or fake.ipv6() if random() < 0.5 else None - ), - forwarded_ip3=( - forwarded_ip3 or fake.ipv4_public() if random() < 0.25 else None - ), + forwarded_ip1=forwarded_ip1 or fake.ipv4_public(), + forwarded_ip2=forwarded_ip2 or fake.ipv6() if random() < 0.5 else None, + forwarded_ip3=forwarded_ip3 or fake.ipv4_public() + if random() < 0.25 + else None, forwarded_ip4=forwarded_ip4, forwarded_ip5=forwarded_ip5, forwarded_ip6=forwarded_ip6, -- cgit v1.2.3 From 1a98418317c115b92739639cf0b837bd974fc5d6 Mon Sep 17 00:00:00 2001 From: stuppie Date: Fri, 18 Sep 2026 11:34:27 -0600 Subject: fix more tests --- generalresearch/managers/thl/payout.py | 2 +- .../managers/thl/wallet/cashout_tasks.py | 3 --- generalresearch/managers/thl/wallet/user_payout.py | 4 ++- test_utils/managers/thl/conftest.py | 31 ++-------------------- test_utils/models/ledger/conftest.py | 16 +++++------ tests/managers/thl/test_cashout_method.py | 4 --- tests/managers/thl/test_ipinfo.py | 6 ++--- tests/managers/thl/test_ledger/test_thl_lm_tx.py | 27 +++++++++++++++++++ tests/managers/thl/test_ledger/test_thl_pem.py | 5 ++-- tests/managers/thl/test_payout.py | 19 +++++++------ 10 files changed, 56 insertions(+), 61 deletions(-) delete mode 100644 generalresearch/managers/thl/wallet/cashout_tasks.py (limited to 'test_utils') diff --git a/generalresearch/managers/thl/payout.py b/generalresearch/managers/thl/payout.py index 4014cf8..3968ea7 100644 --- a/generalresearch/managers/thl/payout.py +++ b/generalresearch/managers/thl/payout.py @@ -879,7 +879,7 @@ class BusinessPayoutEventManager(PostgresManagerWithRedis): amount: USDCent, ext_ref_id: str, created: datetime | None = None, - ): + ) -> BusinessPayoutEvent: """ This should NOT be called directly normally. It is just a shortcut for tests. However, instead of just making a naked BP payout, diff --git a/generalresearch/managers/thl/wallet/cashout_tasks.py b/generalresearch/managers/thl/wallet/cashout_tasks.py deleted file mode 100644 index b28b04f..0000000 --- a/generalresearch/managers/thl/wallet/cashout_tasks.py +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/generalresearch/managers/thl/wallet/user_payout.py b/generalresearch/managers/thl/wallet/user_payout.py index ca47611..a1c628b 100644 --- a/generalresearch/managers/thl/wallet/user_payout.py +++ b/generalresearch/managers/thl/wallet/user_payout.py @@ -11,7 +11,9 @@ from redis import Redis from generalresearch.currency import USDCent from generalresearch.managers.thl.cashout_method import CashoutMethodManager from generalresearch.managers.thl.ipinfo import GeoIpInfoManager -from generalresearch.managers.thl.ledger_manager.exceptions import LedgerTransactionCreateError +from generalresearch.managers.thl.ledger_manager.exceptions import ( + LedgerTransactionCreateError, +) from generalresearch.managers.thl.ledger_manager.thl_ledger import ThlLedgerManager from generalresearch.managers.thl.payout import PayoutEventManager from generalresearch.managers.thl.userhealth import UserIpHistoryManager diff --git a/test_utils/managers/thl/conftest.py b/test_utils/managers/thl/conftest.py index 3392559..5b96680 100644 --- a/test_utils/managers/thl/conftest.py +++ b/test_utils/managers/thl/conftest.py @@ -23,15 +23,10 @@ if TYPE_CHECKING: from generalresearch.config import GRLBaseSettings from generalresearch.managers.thl.buyer import BuyerManager from generalresearch.managers.thl.category import CategoryManager - from generalresearch.managers.thl.ipinfo import ( - IPGeonameManager, - IPInformationManager, - ) from generalresearch.managers.thl.payout import ( BrokerageProductPayoutEventManager, BusinessPayoutEventManager, PayoutEventManager, - UserPayoutEventManager, ) from generalresearch.managers.thl.product import ProductManager from generalresearch.managers.thl.session import SessionManager @@ -52,6 +47,7 @@ if TYPE_CHECKING: WallCacheManager, WallManager, ) + from generalresearch.managers.thl.wallet.user_payout import UserPayoutEventManager # === Msc === @@ -139,7 +135,7 @@ def user_payout_event_manager( assert thl_web_rw.dsn.path assert "/unittest-" in thl_web_rw.dsn.path - from generalresearch.managers.thl.payout import UserPayoutEventManager + from generalresearch.managers.thl.wallet.user_payout import UserPayoutEventManager return UserPayoutEventManager( pg_config=thl_web_rw, @@ -342,29 +338,6 @@ def surveypenalty_manager(thl_redis_config: RedisConfig): return SurveyPenaltyManager(redis_config=thl_redis_config) -# --- IP Geolocation --- - - -@pytest.fixture -def ip_geoname_manager(thl_web_rw: PostgresConfig) -> IPGeonameManager: - from generalresearch.managers.thl.ipinfo import IPGeonameManager - - return IPGeonameManager(pg_config=thl_web_rw) - - -# --- IP Information --- - - -@pytest.fixture(scope="session") -def ip_information_manager(thl_web_rw: PostgresConfig) -> IPInformationManager: - assert thl_web_rw.dsn.path - assert "/unittest-" in thl_web_rw.dsn.path - - from generalresearch.managers.thl.ipinfo import IPInformationManager - - return IPInformationManager(pg_config=thl_web_rw) - - # --- IP Record --- diff --git a/test_utils/models/ledger/conftest.py b/test_utils/models/ledger/conftest.py index 9ee0df2..e9ddbc9 100644 --- a/test_utils/models/ledger/conftest.py +++ b/test_utils/models/ledger/conftest.py @@ -39,7 +39,6 @@ if TYPE_CHECKING: ThlLedgerManager, ) from generalresearch.managers.thl.payout import ( - BrokerageProductPayoutEventManager, BusinessPayoutEventManager, ) from generalresearch.managers.thl.session import SessionManager @@ -202,7 +201,7 @@ def bp_payout_event( @pytest.fixture def bp_payout_event_factory( - brokerage_product_payout_event_manager: BrokerageProductPayoutEventManager, + business_payout_event_manager: BusinessPayoutEventManager, thl_ledger_manager: ThlLedgerManager, ) -> Callable[..., BrokerageProductPayoutEvent]: @@ -210,14 +209,13 @@ def bp_payout_event_factory( product: Product, usd_cent: USDCent, ext_ref_id: str | None = None ) -> BrokerageProductPayoutEvent: - return brokerage_product_payout_event_manager.create_bp_payout_event( + bus_pe = business_payout_event_manager.create_bp_payout_event( thl_ledger_manager=thl_ledger_manager, product=product, amount=usd_cent, - ext_ref_id=ext_ref_id, - skip_wallet_balance_check=True, - skip_one_per_day_check=True, + ext_ref_id=ext_ref_id or uuid4().hex, ) + return bus_pe.bp_payouts[0] return _inner @@ -225,9 +223,9 @@ def bp_payout_event_factory( @pytest.fixture def currency(ledger_manager: LedgerManager) -> LedgerCurrency: # return request.param if hasattr(request, "currency") else LedgerCurrency.TEST - assert ( - ledger_manager.currency - ), "LedgerManager must have a currency specified for these tests" + assert ledger_manager.currency, ( + "LedgerManager must have a currency specified for these tests" + ) return ledger_manager.currency diff --git a/tests/managers/thl/test_cashout_method.py b/tests/managers/thl/test_cashout_method.py index fc364f2..bb978f8 100644 --- a/tests/managers/thl/test_cashout_method.py +++ b/tests/managers/thl/test_cashout_method.py @@ -13,7 +13,6 @@ from generalresearch.models.thl.wallet.cashout_method import ( from generalresearch.models.thl.wallet.definitions import PayoutType if TYPE_CHECKING: - from generalresearch.config import GRLBaseSettings from generalresearch.managers.thl.cashout_method import ( CashoutMethodManager, ) @@ -24,7 +23,6 @@ if TYPE_CHECKING: class TestTangoCashoutMethods: - def test_create_and_get( self, cashout_method_manager: CashoutMethodManager, @@ -51,9 +49,7 @@ class TestTangoCashoutMethods: assert len(res) == 2 - class TestUserCashoutMethods: - def test( self, cashout_method_manager: CashoutMethodManager, diff --git a/tests/managers/thl/test_ipinfo.py b/tests/managers/thl/test_ipinfo.py index f39d50e..d84cbf7 100644 --- a/tests/managers/thl/test_ipinfo.py +++ b/tests/managers/thl/test_ipinfo.py @@ -3,7 +3,7 @@ from generalresearch.models.thl.ipinfo import GeoIPInformation class TestGeoIpInfoManager: - def test_get(self, geoipinfo_manager: GeoIpInfoManager): + def test_get(self, geoip_info_manager: GeoIpInfoManager): result = geoip_info_manager.get("8.8.8.8") assert result == GeoIPInformation( @@ -16,7 +16,7 @@ class TestGeoIpInfoManager: ) geoip_info_manager.grip_mmdb.lookup.assert_called_once_with("8.8.8.8") - def test_get_multi(self, geoipinfo_manager: GeoIpInfoManager): + def test_get_multi(self, geoip_info_manager: GeoIpInfoManager): result = geoip_info_manager.get_multi(["8.8.8.8", "1.1.1.1", "8.8.8.8"]) assert result == { @@ -42,6 +42,6 @@ class TestGeoIpInfoManager: call.args[0] for call in geoip_info_manager.grip_mmdb.lookup.call_args_list } == {"8.8.8.8", "1.1.1.1"} - def test_get_multi_empty(self, geoipinfo_manager: GeoIpInfoManager): + def test_get_multi_empty(self, geoip_info_manager: GeoIpInfoManager): assert geoip_info_manager.get_multi([]) == {} geoip_info_manager.grip_mmdb.lookup.assert_not_called() diff --git a/tests/managers/thl/test_ledger/test_thl_lm_tx.py b/tests/managers/thl/test_ledger/test_thl_lm_tx.py index 96ea0d6..22a7b73 100644 --- a/tests/managers/thl/test_ledger/test_thl_lm_tx.py +++ b/tests/managers/thl/test_ledger/test_thl_lm_tx.py @@ -806,6 +806,24 @@ class TestThlLedgerTxManager: assert thl_ledger_manager.get_session_attempt_credit(session.uuid) == 5 assert thl_ledger_manager.get_user_attempt_credit_balance(user) == 5 + attempt_credit_account = ( + thl_ledger_manager.get_account_or_create_user_attempt_credit(user) + ) + user_txs = thl_ledger_manager.get_user_txs( + user, account_uuid=attempt_credit_account.uuid + ) + assert user_txs.total == 1 + assert user_txs.transactions[0].tx_type == TransactionType.USER_ATTEMPT_CREDIT + assert user_txs.transactions[0].amount == 5 + assert user_txs.transactions[0].balance_after == 5 + assert user_txs.summary.user_attempt_credit.entry_count == 1 + assert user_txs.summary.user_attempt_credit.total_amount == 5 + + user_txs_by_name = thl_ledger_manager.get_user_txs( + user, qualified_name=attempt_credit_account.qualified_name + ) + assert user_txs_by_name == user_txs + with pytest.raises( LedgerTransactionConditionFailedError, match=r"^duplicate tag$" ): @@ -887,6 +905,15 @@ class TestThlLedgerTxManager: assert thl_ledger_manager.get_user_wallet_balance(user) == round(user_pay * 100) assert thl_ledger_manager.check_ledger_balanced() + attempt_credit_account = ( + thl_ledger_manager.get_account_or_create_user_attempt_credit(user) + ) + user_txs = thl_ledger_manager.get_user_txs( + user, account_uuid=attempt_credit_account.uuid + ) + assert [tx.amount for tx in user_txs.transactions] == [5, -5] + assert [tx.balance_after for tx in user_txs.transactions] == [5, 0] + def test_bp_payment_with_zero_attempt_credit_balance( self, session_factory: Callable[..., Session], diff --git a/tests/managers/thl/test_ledger/test_thl_pem.py b/tests/managers/thl/test_ledger/test_thl_pem.py index fb35aa4..4761438 100644 --- a/tests/managers/thl/test_ledger/test_thl_pem.py +++ b/tests/managers/thl/test_ledger/test_thl_pem.py @@ -30,7 +30,6 @@ if TYPE_CHECKING: class TestThlPayoutEventManager: - def test_get_by_uuid( self, brokerage_product_payout_event_manager: BrokerageProductPayoutEventManager ): @@ -50,8 +49,9 @@ class TestThlPayoutEventManager: bp_payout_event_factory: Callable[..., BrokerageProductPayoutEvent], thl_ledger_manager: ThlLedgerManager, brokerage_product_payout_event_manager: BrokerageProductPayoutEventManager, + create_main_accounts, ): - + create_main_accounts() N_PRODUCTS = randint(3, 10) N_PAYOUT_EVENTS = randint(3, 10) amounts = [] @@ -223,7 +223,6 @@ class TestThlPayoutEventManager: class TestBPPayoutEvent: - def test_get_bp_bp_payout_events_for_products( self, product_factory: Callable[..., Product], diff --git a/tests/managers/thl/test_payout.py b/tests/managers/thl/test_payout.py index 52bbbec..69b9737 100644 --- a/tests/managers/thl/test_payout.py +++ b/tests/managers/thl/test_payout.py @@ -163,7 +163,9 @@ class TestPayout: thl_ledger_manager: ThlLedgerManager, utc_now: datetime, pending_bp_pe: BrokerageProductPayoutEvent, + create_main_accounts, ): + create_main_accounts() thl_ledger_manager.get_account_or_create_bp_wallet(product=product) brokerage_product_payout_event_manager.create_tx_bp_payout_from_payout_event( @@ -871,6 +873,7 @@ class TestBusinessPayoutEventManager: bp1 = business_payout_event_manager.create_from_ach_or_wire( business=gr_business, amount=USDCent(bb1.available_balance), + transaction_id=ach_id1, pm=product_manager, thl_lm=thl_ledger_manager, created=start + timedelta(days=1, hours=5), @@ -901,22 +904,22 @@ class TestBusinessPayoutEventManager: ledger_collection.initial_load(client=None, sync=True) pop_ledger_merge.build(client=client_no_amm, ledger_coll=ledger_collection) - business.prebuild_balance( - thl_pg_config=thl_web_rr, + gr_business.prebuild_balance( + product_manager=product_manager, lm=ledger_manager, ds=mnt_filepath, client=client_no_amm, pop_ledger=pop_ledger_merge, ) - business.prebuild_payouts( + gr_business.prebuild_payouts( bpem=business_payout_event_manager, ) - assert isinstance(business.payouts, list) - assert len(business.payouts) == 2 - assert len(business.payouts[0].bp_payouts) == 2 - assert len(business.payouts[1].bp_payouts) == 1 + assert isinstance(gr_business.payouts, list) + assert len(gr_business.payouts) == 2 + assert len(gr_business.payouts[0].bp_payouts) == 2 + assert len(gr_business.payouts[1].bp_payouts) == 1 - bb2 = business.balance + bb2 = gr_business.balance # Okay os we have the balance before, and after the Business Payout # of bb1.available_balance worth.. -- cgit v1.2.3 From 438ea05fdb98c847d534ae190179b4766fa96f26 Mon Sep 17 00:00:00 2001 From: stuppie Date: Fri, 18 Sep 2026 13:40:54 -0600 Subject: test user payout paypal and tango, and fix bugs --- generalresearch/managers/thl/cashout_method.py | 2 +- generalresearch/managers/thl/wallet/tango.py | 6 +- generalresearch/managers/thl/wallet/user_payout.py | 23 +- .../models/thl/wallet/cashout_method.py | 4 +- test_utils/managers/conftest.py | 25 +- test_utils/managers/thl/conftest.py | 11 - tests/managers/thl/test_ledger/test_thl_pem.py | 32 -- tests/managers/thl/test_payout.py | 2 +- tests/managers/thl/test_wallet/__init__.py | 0 tests/managers/thl/test_wallet/test_user_payout.py | 435 +++++++++++++++++++++ 10 files changed, 454 insertions(+), 86 deletions(-) create mode 100644 tests/managers/thl/test_wallet/__init__.py create mode 100644 tests/managers/thl/test_wallet/test_user_payout.py (limited to 'test_utils') diff --git a/generalresearch/managers/thl/cashout_method.py b/generalresearch/managers/thl/cashout_method.py index 46bde38..90a6876 100644 --- a/generalresearch/managers/thl/cashout_method.py +++ b/generalresearch/managers/thl/cashout_method.py @@ -376,7 +376,7 @@ class CashoutMethodManager(PostgresManager): if user and x["type"] in {PayoutType.PAYPAL, PayoutType.CASH_IN_MAIL}: user = user if isinstance(user, UserRef) else user.to_user_ref() x["user"] = user - x["original_currency"] = x.get("currency") or Currency.USD + x["original_currency"] = x.get("original_currency") or Currency.USD x["currency"] = Currency.USD return CashoutMethod.model_validate(x) diff --git a/generalresearch/managers/thl/wallet/tango.py b/generalresearch/managers/thl/wallet/tango.py index ca993ea..361a885 100644 --- a/generalresearch/managers/thl/wallet/tango.py +++ b/generalresearch/managers/thl/wallet/tango.py @@ -1,6 +1,7 @@ from __future__ import annotations from datetime import timedelta +from decimal import Decimal from threading import Lock from typing import TYPE_CHECKING, Any @@ -69,13 +70,14 @@ class TangoManager: utid = cashout_method.data.utid currency = cashout_method.original_currency if currency and currency != Currency.USD: - amount = round(float(amount) / self.get_exchange_rates()[currency], 2) + amount = round(float(amount) / self.get_exchange_rates()[currency]) + amount_in_currency = Decimal(float(amount) / 100).quantize(Decimal('0.01')) return TangoCashoutMethodRequestData.model_validate( { "accountIdentifier": self.tango_account_id, "customerIdentifier": self.tango_customer_id, "utid": utid, - "amount": str(amount), + "amount": amount_in_currency, "campaign": "300large", "sendEmail": False, "externalRefID": payout_event_id, diff --git a/generalresearch/managers/thl/wallet/user_payout.py b/generalresearch/managers/thl/wallet/user_payout.py index a1c628b..983ab3a 100644 --- a/generalresearch/managers/thl/wallet/user_payout.py +++ b/generalresearch/managers/thl/wallet/user_payout.py @@ -68,8 +68,8 @@ class UserPayoutEventManager(PayoutEventManager): return pe def get_payout_detail(self, pe_uuid: UUIDStr) -> CashoutRequestInfo: - # This gets the payout event, and then extracts information for - # the purposes of returning to the user. + # This gets the payout event and then extracts information to return + # to the user. pe = self.get_by_uuid(pe_uuid=pe_uuid) transaction_info = {} @@ -205,9 +205,6 @@ class UserPayoutEventManager(PayoutEventManager): created: AwareDatetimeISO | None = None, request_data: dict[str, Any] | None = None, # --- Optional: None --- - account_reference_type: str | None = None, - account_reference_uuid: UUIDStr | None = None, - description: str | None = None, ext_ref_id: str | None = None, order_data: dict[str, Any] | CashMailOrderData | None = None, ) -> UserPayoutEvent: @@ -215,10 +212,7 @@ class UserPayoutEventManager(PayoutEventManager): payout_event = UserPayoutEvent( uuid=uuid or uuid4().hex, debit_account_uuid=debit_account_uuid, - account_reference_type=account_reference_type, - account_reference_uuid=account_reference_uuid, cashout_method_uuid=cashout_method_uuid, - description=description, created=created or datetime.now(tz=UTC), amount=amount, status=status or PayoutStatus.PENDING, @@ -250,7 +244,7 @@ class UserPayoutEventManager(PayoutEventManager): assert c.rowcount == 1, f"expected 1 row inserted, got {c.rowcount}" conn.commit() - return payout_event + return self.get_by_uuid(payout_event.uuid) def try_user_request_redeem( self, @@ -261,7 +255,6 @@ class UserPayoutEventManager(PayoutEventManager): cashout_method_manager: CashoutMethodManager, ledger_manager: ThlLedgerManager, user_ip_history_manager: UserIpHistoryManager, - geoip_info_manager: GeoIpInfoManager, redis_client: Redis, slack_client: slack.WebClient | None = None, ) -> tuple[UserPayoutEvent | None, str | None]: @@ -274,7 +267,6 @@ class UserPayoutEventManager(PayoutEventManager): cashout_method_manager=cashout_method_manager, ledger_manager=ledger_manager, user_ip_history_manager=user_ip_history_manager, - geoip_info_manager=geoip_info_manager, redis_client=redis_client, slack_client=slack_client, ), None @@ -290,7 +282,6 @@ class UserPayoutEventManager(PayoutEventManager): cashout_method_manager: CashoutMethodManager, ledger_manager: ThlLedgerManager, user_ip_history_manager: UserIpHistoryManager, - geoip_info_manager: GeoIpInfoManager, redis_client: Redis, slack_client: slack.WebClient | None = None, ) -> UserPayoutEvent: @@ -303,9 +294,7 @@ class UserPayoutEventManager(PayoutEventManager): now = datetime.now(tz=UTC) user.prefetch_product(pg_config=self.pg_config) - country_iso = user_ip_history_manager.get_user_latest_country( - user, geoip_info_manager - ) + country_iso = user_ip_history_manager.get_user_latest_country(user) assert country_iso, "user has no country" usd_exchange_rates = tango_manager.get_exchange_rates() @@ -345,9 +334,7 @@ class UserPayoutEventManager(PayoutEventManager): product = user.product banned_countries = user.product.user_health_config.banned_countries - assert not user_ip_history_manager.is_user_anonymous( - user, geoip_info_manager=geoip_info_manager - ), "Anonymous user requesting redemption" + assert not user_ip_history_manager.is_user_anonymous(user), "Anonymous user requesting redemption" if country_iso in banned_countries: raise AssertionError("Banned country requesting redemption") diff --git a/generalresearch/models/thl/wallet/cashout_method.py b/generalresearch/models/thl/wallet/cashout_method.py index a82fa31..61a8992 100644 --- a/generalresearch/models/thl/wallet/cashout_method.py +++ b/generalresearch/models/thl/wallet/cashout_method.py @@ -3,6 +3,7 @@ from __future__ import annotations import hashlib import logging from datetime import UTC, datetime +from decimal import Decimal from enum import StrEnum from typing import Any, Literal, Self @@ -240,7 +241,8 @@ class TangoCashoutMethodRequestData(BaseModel): accountIdentifier: str = Field() customerIdentifier: str = Field() utid: str = Field(description="tango utid") - amount: USDCent = Field(description="Amount to be paid out") + # This is not necessarily in USD. It is in whatever currency the card is in + amount: Decimal = Field(description="Amount to be paid out (in USD, EUR, etc)") campaign: Literal["300large"] sendEmail: bool = Field(default=False) externalRefID: str = Field(description="External Ref ID") diff --git a/test_utils/managers/conftest.py b/test_utils/managers/conftest.py index 72be79e..a9f92a2 100644 --- a/test_utils/managers/conftest.py +++ b/test_utils/managers/conftest.py @@ -25,7 +25,6 @@ from generalresearch.managers.thl.user_streak import ( ) from generalresearch.managers.thl.userhealth import ( AuditLogManager, - IPRecordManager, UserIpHistoryManager, ) from generalresearch.models.definitions import Source @@ -56,24 +55,6 @@ def audit_log_manager(thl_web_rw: PostgresConfig) -> AuditLogManager: return AuditLogManager(pg_config=thl_web_rw) -@pytest.fixture -def ip_record_manager( - thl_web_rw: PostgresConfig, - thl_redis_config: RedisConfig, - geoip_info_manager: GeoIpInfoManager, -) -> IPRecordManager: - assert thl_web_rw.dsn.path - assert "/unittest-" in thl_web_rw.dsn.path - - from generalresearch.managers.thl.userhealth import IPRecordManager - - return IPRecordManager( - pg_config=thl_web_rw, - redis_config=thl_redis_config, - geoip_info_manager=geoip_info_manager, - ) - - @pytest.fixture def user_iphistory_manager( thl_web_rw: PostgresConfig, @@ -124,6 +105,11 @@ def grip_lookup_results() -> dict[str, GRIPMMDBLookupResult]: network_operator="Cloudflare", ), ), + "2.2.2.2": GRIPMMDBLookupResult( + country=GRIPCountryRecord(country_iso="IT"), + anonymous=GRIPAnonymousRecord(is_anonymous=False), + asn=GRIPAsnRecord(), + ), }, ) @@ -214,7 +200,6 @@ def random_ext_id_factory(base: str = "U02") -> Callable[..., str]: @pytest.fixture(scope="session") def example_tango_cashout_methods( - random_ext_id_factory: Callable[..., str], ) -> list[CashoutMethod]: return [ CashoutMethod( diff --git a/test_utils/managers/thl/conftest.py b/test_utils/managers/thl/conftest.py index 5b96680..2dce648 100644 --- a/test_utils/managers/thl/conftest.py +++ b/test_utils/managers/thl/conftest.py @@ -40,7 +40,6 @@ if TYPE_CHECKING: UserMetadataManager, ) from generalresearch.managers.thl.userhealth import ( - AuditLogManager, IPRecordManager, ) from generalresearch.managers.thl.wall import ( @@ -162,13 +161,6 @@ def brokerage_product_payout_event_manager( ) -@pytest.fixture() -def audit_log_manager(thl_web_rw: PostgresConfig) -> AuditLogManager: - from generalresearch.managers.thl.userhealth import AuditLogManager - - return AuditLogManager(pg_config=thl_web_rw) - - @pytest.fixture(scope="session") def business_payout_event_manager( thl_web_rw: PostgresConfig, thl_redis_config: RedisConfig @@ -338,9 +330,6 @@ def surveypenalty_manager(thl_redis_config: RedisConfig): return SurveyPenaltyManager(redis_config=thl_redis_config) -# --- IP Record --- - - @pytest.fixture(scope="session") def ip_record_manager( thl_web_rw: PostgresConfig, thl_redis_config: RedisConfig diff --git a/tests/managers/thl/test_ledger/test_thl_pem.py b/tests/managers/thl/test_ledger/test_thl_pem.py index 4761438..2d03b25 100644 --- a/tests/managers/thl/test_ledger/test_thl_pem.py +++ b/tests/managers/thl/test_ledger/test_thl_pem.py @@ -122,38 +122,6 @@ class TestThlPayoutEventManager: assert len(res) == (N_PRODUCTS * N_PAYOUT_EVENTS) assert sum([i.amount for i in res]) == sum(amounts) - @pytest.mark.skip - def test_get_payout_detail(self, user_payout_event_manager: UserPayoutEventManager): - """This fails because the description coming back is None, but then - it tries to return a PayoutEvent which validates that the - description can't be None - """ - from generalresearch.models.thl.payout import ( - PayoutType, - ) - - rand_amount = randint(a=99, b=999) - - pe = user_payout_event_manager.create( - debit_account_uuid=uuid4().hex, - account_reference_type="str-type-random", - account_reference_uuid=uuid4().hex, - cashout_method_uuid=uuid4().hex, - description="Best payout !", - amount=rand_amount, - status=PayoutStatus.PENDING, - ext_ref_id="123", - payout_type=PayoutType.CASH_IN_MAIL, - request_data={"foo": 123}, - order_data={}, - ) - - res = user_payout_event_manager.get_payout_detail(pe_uuid=pe.uuid) - assert isinstance(res, CashoutRequestInfo) - - # def test_filter_by(self): - # raise NotImplementedError - def test_create( self, user_payout_event_factory: Callable[..., UserPayoutEvent], diff --git a/tests/managers/thl/test_payout.py b/tests/managers/thl/test_payout.py index 69b9737..a9cd52a 100644 --- a/tests/managers/thl/test_payout.py +++ b/tests/managers/thl/test_payout.py @@ -34,9 +34,9 @@ if TYPE_CHECKING: BrokerageProductPayoutEventManager, BusinessPayoutEventManager, PayoutEventManager, - UserPayoutEventManager, ) from generalresearch.managers.thl.product import ProductManager + from generalresearch.managers.thl.wallet.user_payout import UserPayoutEventManager from generalresearch.models.gr.business import Business from generalresearch.models.thl.ledger import LedgerAccount from generalresearch.models.thl.payout import ( diff --git a/tests/managers/thl/test_wallet/__init__.py b/tests/managers/thl/test_wallet/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/managers/thl/test_wallet/test_user_payout.py b/tests/managers/thl/test_wallet/test_user_payout.py new file mode 100644 index 0000000..bbc97a3 --- /dev/null +++ b/tests/managers/thl/test_wallet/test_user_payout.py @@ -0,0 +1,435 @@ +from datetime import datetime +from decimal import Decimal +from random import randint +from unittest.mock import Mock +from uuid import uuid4 + +import pytest +from faker import Faker + +from generalresearch.currency import USDCent +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.wallet.tango import TangoManager +from generalresearch.managers.thl.wallet.user_payout import ( + UserPayoutEventManager, + make_request_paypal, +) +from generalresearch.models.thl.definitions import PayoutStatus +from generalresearch.models.thl.payout import UserPayoutEvent +from generalresearch.models.thl.user import User +from generalresearch.models.thl.wallet.cashout_method import ( + CashMailCashoutMethodData, + CashMailCashoutMethodRequestData, + CashoutRequestInfo, + PaypalCashoutMethodData, + PaypalCashoutMethodRequestData, + USDeliveryAddress, +) +from generalresearch.models.thl.wallet.definitions import PayoutType + +fake = Faker() + + +class TestUserPayoutEventManager: + def test_get_by_uuid_and_create( + self, + user: User, + user_payout_event_manager: UserPayoutEventManager, + thl_ledger_manager: ThlLedgerManager, + utc_now: datetime, + ): + user_account = thl_ledger_manager.get_account_or_create_user_wallet(user=user) + request_data = PaypalCashoutMethodRequestData.model_validate( + { + "email": fake.email(), + "interface": "api", + } + ) + pe1: UserPayoutEvent = user_payout_event_manager.create( + debit_account_uuid=user_account.uuid, + payout_type=PayoutType.PAYPAL, + cashout_method_uuid=uuid4().hex, + amount=100, + created=utc_now, + request_data=request_data.model_dump(mode="json"), + ) + # these get added by the query + pe1.account_reference_type = "user" + pe1.account_reference_uuid = user.uuid + + pe2 = user_payout_event_manager.get_by_uuid(pe_uuid=pe1.uuid) + + assert pe1 == pe2 + + def test_get_payout_detail_cash_in_mail( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + ): + user = user_with_wallet + data = CashMailCashoutMethodData( + delivery_address=USDeliveryAddress.model_validate( + { + "name_or_attn": "Josh Ackerman", + "address": "123 Fake St", + "city": "San Francisco", + "state": "CA", + "postal_code": "12345", + } + ) + ) + cm = cashout_method_manager.create_cash_in_mail_cashout_method( + data=data, user=user + ) + user_account = thl_ledger_manager.get_account_or_create_user_wallet(user=user) + + rand_amount = randint(a=99, b=999) + request_data = CashMailCashoutMethodRequestData.model_validate( + cm.data.model_dump() + ) + + pe = user_payout_event_manager.create( + debit_account_uuid=user_account.uuid, + cashout_method_uuid=cm.id, + amount=rand_amount, + ext_ref_id=uuid4().hex, + payout_type=PayoutType.CASH_IN_MAIL, + request_data=request_data.model_dump(mode="json"), + ) + + res = user_payout_event_manager.get_payout_detail(pe_uuid=pe.uuid) + assert isinstance(res, CashoutRequestInfo) + + def test_get_payout_detail_paypal( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + ): + user = user_with_wallet + data = PaypalCashoutMethodData(email=fake.email()) + cm = cashout_method_manager.create_paypal_cashout_method(data=data, user=user) + user_account = thl_ledger_manager.get_account_or_create_user_wallet(user=user) + + rand_amount = randint(a=99, b=999) + request_data = make_request_paypal(cm) + pe = user_payout_event_manager.create( + debit_account_uuid=user_account.uuid, + cashout_method_uuid=cm.id, + amount=rand_amount, + ext_ref_id=uuid4().hex, + payout_type=PayoutType.PAYPAL, + request_data=request_data.model_dump(mode="json"), + ) + + res = user_payout_event_manager.get_payout_detail(pe_uuid=pe.uuid) + assert isinstance(res, CashoutRequestInfo) + + +class TestUserRequestRedeem: + @staticmethod + def request_paypal_payout( + *, + user: User, + amount: USDCent, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + ip_record_factory, + thl_redis_client, + ip: str = "8.8.8.8", + fund_amount: Decimal = Decimal("10.00"), + ) -> UserPayoutEvent: + email = fake.email() + cashout_method = cashout_method_manager.create_paypal_cashout_method( + data=PaypalCashoutMethodData(email=email), + user=user, + ) + ip_record_factory(user=user, ip=ip) + thl_ledger_manager.create_tx_user_bonus( + user=user, + amount=fund_amount, + ref_uuid=uuid4().hex, + description="Fund PayPal redemption test", + ) + + tango_manager = Mock(spec=TangoManager) + tango_manager.get_exchange_rates.return_value = {} + + return user_payout_event_manager.user_request_redeem( + user=user, + cashout_method_id=cashout_method.id, + amount=amount, + tango_manager=tango_manager, + cashout_method_manager=cashout_method_manager, + ledger_manager=thl_ledger_manager, + user_ip_history_manager=user_iphistory_manager, + redis_client=thl_redis_client, + ) + + def test_user_request_redeem_paypal( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + geoip_info_manager: GeoIpInfoManager, + ip_record_factory, + thl_redis_client, + ): + user = user_with_wallet + payout = self.request_paypal_payout( + user=user, + amount=USDCent(600), + user_payout_event_manager=user_payout_event_manager, + cashout_method_manager=cashout_method_manager, + thl_ledger_manager=thl_ledger_manager, + user_iphistory_manager=user_iphistory_manager, + ip_record_factory=ip_record_factory, + thl_redis_client=thl_redis_client, + ) + + assert payout.status == PayoutStatus.PENDING + assert payout.payout_type == PayoutType.PAYPAL + assert payout.amount == 600 + assert payout.request_data["interface"] == "api" + assert thl_ledger_manager.get_user_wallet_balance(user) == 400 + assert user_payout_event_manager.get_by_uuid(payout.uuid) == payout + + detail = user_payout_event_manager.get_payout_detail(payout.uuid) + assert not detail.transaction_info + assert detail.description == "PayPal" + + def test_user_request_redeem_paypal_complete( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + geoip_info_manager: GeoIpInfoManager, + ip_record_factory, + thl_redis_client, + create_main_accounts, + ): + create_main_accounts() + user = user_with_wallet + payout = self.request_paypal_payout( + user=user, + amount=USDCent(600), + user_payout_event_manager=user_payout_event_manager, + cashout_method_manager=cashout_method_manager, + thl_ledger_manager=thl_ledger_manager, + user_iphistory_manager=user_iphistory_manager, + ip_record_factory=ip_record_factory, + thl_redis_client=thl_redis_client, + ) + + user_payout_event_manager.update( + payout_event=payout, + status=PayoutStatus.APPROVED, + ext_ref_id="paypal-batch-id", + ) + thl_ledger_manager.create_tx_user_payout_complete( + user=user, + payout_event=payout, + fee_amount=Decimal("0.25"), + ) + user_payout_event_manager.update( + payout_event=payout, + status=PayoutStatus.COMPLETE, + order_data={"transaction_id": "paypal-transaction-id"}, + ) + + detail = user_payout_event_manager.get_payout_detail(pe_uuid=payout.uuid) + assert detail.status == PayoutStatus.COMPLETE + assert detail.transaction_info == {"transaction_id": "paypal-transaction-id"} + + assert thl_ledger_manager.get_user_wallet_balance(user_with_wallet) == 400 + bp_expense = thl_ledger_manager.get_account_or_create_bp_expense_by_uuid( + product_uuid=user.to_user_ref().product_id, expense_name="paypal" + ) + assert thl_ledger_manager.get_account_balance(bp_expense) == -25 + + def test_user_request_redeem_tango_complete( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + ip_record_factory, + thl_redis_client, + create_main_accounts, + setup_cashoutmethod_db, + example_tango_cashout_methods, + ): + create_main_accounts() + setup_cashoutmethod_db() + user = user_with_wallet + # This is the italian Amazon.it gift card + cashout_method = example_tango_cashout_methods[1] + # This is an IP that we have hard-coded by the geoip_info_manager as Italy + ip_record_factory(user=user, ip="2.2.2.2") + + thl_ledger_manager.create_tx_user_bonus( + user=user, + amount=Decimal("5.00"), + ref_uuid=uuid4().hex, + description="Fund Tango redemption test", + ) + + tango_client = Mock() + tango_client.get_exchange_rates.return_value = { + "exchangeRates": [ + {"rewardCurrency": "USD", "baseCurrency": "EUR", "baseFx": 1.14792} + ] + } + tango_manager = TangoManager( + tango_client=tango_client, + tango_account_id="test-account", + tango_customer_id="test-customer", + cashout_method_manager=cashout_method_manager, + ) + request = tango_manager.make_request( + amount=USDCent(2_00), + cashout_method=cashout_method, + payout_event_id=uuid4().hex, + ) + assert request.amount == Decimal(2.0 / 1.14792).quantize( + Decimal("0.01") + ) # ~ 1.74 EUR + + payout = user_payout_event_manager.user_request_redeem( + user=user, + cashout_method_id=cashout_method.id, + amount=USDCent(2_00), + tango_manager=tango_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.request_data["amount"] == '1.74' + assert payout.request_data["externalRefID"] == payout.uuid + + user_payout_event_manager.update( + payout_event=payout, + status=PayoutStatus.APPROVED, + ext_ref_id="tango-order-id", + ) + thl_ledger_manager.create_tx_user_payout_complete( + user=user, + payout_event=payout, + ) + user_payout_event_manager.update( + payout_event=payout, + status=PayoutStatus.COMPLETE, + order_data={ + "reward": { + "credentialList": [ + { + "credentialType": "giftCardCode", + "credentialValue": "TEST-CODE", + } + ], + "redemptionInstructions": "Use this code at checkout.", + } + }, + ) + + detail = user_payout_event_manager.get_payout_detail(pe_uuid=payout.uuid) + assert payout.payout_type == PayoutType.TANGO + assert detail.status == PayoutStatus.COMPLETE + assert detail.transaction_info == { + "credential_list": [ + { + "credentialType": "giftCardCode", + "credentialValue": "TEST-CODE", + } + ], + "redemption_instructions": "Use this code at checkout.", + } + assert thl_ledger_manager.get_user_wallet_balance(user) == 300 + bp_expense = thl_ledger_manager.get_account_or_create_bp_expense_by_uuid( + product_uuid=user.to_user_ref().product_id, + expense_name="tango", + ) + assert thl_ledger_manager.get_account_balance(bp_expense) == -7 + + def test_user_request_redeem_paypal_insufficient_balance( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + ip_record_factory, + thl_redis_client, + ): + with pytest.raises(AssertionError, match="more than their redeemable balance"): + self.request_paypal_payout( + user=user_with_wallet, + amount=USDCent(1_100), + user_payout_event_manager=user_payout_event_manager, + cashout_method_manager=cashout_method_manager, + thl_ledger_manager=thl_ledger_manager, + user_iphistory_manager=user_iphistory_manager, + ip_record_factory=ip_record_factory, + thl_redis_client=thl_redis_client, + ) + + def test_user_request_redeem_paypal_blocked_user( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + ip_record_factory, + thl_redis_client, + ): + user_with_wallet.blocked = True + + with pytest.raises(AssertionError, match="Blocked user"): + self.request_paypal_payout( + user=user_with_wallet, + amount=USDCent(600), + user_payout_event_manager=user_payout_event_manager, + cashout_method_manager=cashout_method_manager, + thl_ledger_manager=thl_ledger_manager, + user_iphistory_manager=user_iphistory_manager, + ip_record_factory=ip_record_factory, + thl_redis_client=thl_redis_client, + ) + + def test_user_request_redeem_paypal_anonymous_user( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + ip_record_factory, + thl_redis_client, + ): + with pytest.raises(AssertionError, match="Anonymous user"): + self.request_paypal_payout( + user=user_with_wallet, + amount=USDCent(600), + user_payout_event_manager=user_payout_event_manager, + cashout_method_manager=cashout_method_manager, + thl_ledger_manager=thl_ledger_manager, + user_iphistory_manager=user_iphistory_manager, + ip_record_factory=ip_record_factory, + thl_redis_client=thl_redis_client, + ip="1.1.1.1", + ) -- cgit v1.2.3 From f4909f0fafd6161dd423859afadb72b916e90564 Mon Sep 17 00:00:00 2001 From: stuppie Date: Fri, 18 Sep 2026 14:56:36 -0600 Subject: 1 single broken managers test --- generalresearch/cacheing.py | 72 ++++++++-------------- generalresearch/managers/thl/cashout_method.py | 19 +++--- .../managers/thl/ledger_manager/conditions.py | 17 +++-- .../managers/thl/ledger_manager/thl_ledger.py | 2 +- generalresearch/managers/thl/payout.py | 2 +- .../managers/thl/user_manager/user_manager.py | 9 ++- test_utils/models/thl/conftest.py | 7 --- test_utils/models/upk/conftest.py | 7 +++ tests/managers/thl/test_ledger/test_lm_tx.py | 4 +- tests/managers/thl/test_ledger/test_lm_tx_locks.py | 21 ++++--- tests/managers/thl/test_ledger/test_thl_pem.py | 14 ++--- tests/managers/thl/test_payout.py | 30 ++++----- tests/managers/thl/test_session_manager.py | 6 +- tests/managers/thl/test_survey_penalty.py | 2 +- tests/managers/thl/test_user_manager/test_base.py | 28 ++++----- tests/managers/thl/test_user_manager/test_redis.py | 15 +++-- tests/managers/thl/test_userhealth.py | 2 - 17 files changed, 121 insertions(+), 136 deletions(-) (limited to 'test_utils') diff --git a/generalresearch/cacheing.py b/generalresearch/cacheing.py index 34df267..55d9c9c 100644 --- a/generalresearch/cacheing.py +++ b/generalresearch/cacheing.py @@ -1,47 +1,25 @@ -from generalresearch import retry - - -class RetryCache: - # Simple pylibmc.Client wrapper that implements a retry on each method - - def __init__(self, client, tries: int = 4, delay: int = 1, backoff: float = 1.5): - import pylibmc - - self.client = client - self.f = retry(pylibmc.Error, tries=tries, delay=delay, backoff=backoff) - - def get(self, key): - @self.f - def _get(key): - return self.client.get(key) - - return _get(key) - - def set(self, key, value, timeout=0): - @self.f - def _set(key, value, timeout): - return self.client.set(key, value, time=timeout) - - return _set(key, value, timeout) - - def delete_multi(self, keys): - @self.f - def _delete_multi(keys): - return self.client.delete_multi(keys) - - return _delete_multi(keys) - - def delete(self, key): - @self.f - def _delete(key): - return self.client.delete(key) - - return _delete(key) - - -if __name__ == "__main__": - import pylibmc - - CACHE = RetryCache(pylibmc.Client(["127.0.0.1:11211"], binary=True)) - CACHE.set("foo", "bar") - print(CACHE.get("foo")) +from cachetools import TTLCache + + +class InstrumentedTTLCache(TTLCache): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.hits = 0 + self.misses = 0 + + def __getitem__(self, key): + try: + value = super().__getitem__(key) + self.hits += 1 + return value + except KeyError: + self.misses += 1 + raise + + def cache_info(self): + return { + "hits": self.hits, + "misses": self.misses, + "currsize": self.currsize, + "maxsize": self.maxsize, + } diff --git a/generalresearch/managers/thl/cashout_method.py b/generalresearch/managers/thl/cashout_method.py index 90a6876..e83b16a 100644 --- a/generalresearch/managers/thl/cashout_method.py +++ b/generalresearch/managers/thl/cashout_method.py @@ -329,6 +329,16 @@ class CashoutMethodManager(PostgresManager): cms = self.get_cashout_methods(user=user) + # Filter out by country before bothering w exchange rates + cms = [ + cm + for cm in cms + if ( + cm.type == PayoutType.TANGO and country_iso.lower() in cm.data.countries + ) + or cm.type != PayoutType.TANGO + ] + for x in cms: # assets in non-USD need to be converted to USD here if x.original_currency is not None: @@ -342,15 +352,6 @@ class CashoutMethodManager(PostgresManager): # Adjust min_value for BP x.min_value = max(x.min_value_usd, min_value) - cms = [ - cm - for cm in cms - if ( - cm.type == PayoutType.TANGO and country_iso.lower() in cm.data.countries - ) - or cm.type != PayoutType.TANGO - ] - return {x.id: x for x in cms} @staticmethod diff --git a/generalresearch/managers/thl/ledger_manager/conditions.py b/generalresearch/managers/thl/ledger_manager/conditions.py index 21a99b5..98502d0 100644 --- a/generalresearch/managers/thl/ledger_manager/conditions.py +++ b/generalresearch/managers/thl/ledger_manager/conditions.py @@ -35,7 +35,10 @@ def generate_condition_mp_payment(wall: Wall) -> Callable[..., tuple[bool, str]] def _condition(lm: LedgerManager) -> tuple[bool, str]: tag = f"{lm.currency.value}:mp_payment:{wall_uuid}" txs = lm.get_tx_ids_by_tag(tag=tag) - return len(txs) == 0, "duplicate tag" + if len(txs) != 0: + return False, "duplicate tag" + else: + return True, "" return _condition @@ -49,8 +52,11 @@ def generate_condition_bp_payment(session: Session) -> Callable[..., tuple[bool, def _condition(lm: LedgerManager) -> tuple[bool, str]: tag = f"{lm.currency.value}:bp_payment:{session_uuid}" - txs_ids = lm.get_tx_ids_by_tag(tag=tag) - return len(txs_ids) == 0, "duplicate tag" + txs = lm.get_tx_ids_by_tag(tag=tag) + if len(txs) != 0: + return False, "duplicate tag" + else: + return True, "" return _condition @@ -62,7 +68,10 @@ def generate_condition_tag_exists(tag: str) -> Callable[..., tuple[bool, str]]: def _condition(lm: LedgerManager) -> tuple[bool, str]: txs_ids = lm.get_tx_ids_by_tag(tag=tag) - return len(txs_ids) == 0, "duplicate tag" + if len(txs_ids) != 0: + return False, "duplicate tag" + else: + return True, "" return _condition diff --git a/generalresearch/managers/thl/ledger_manager/thl_ledger.py b/generalresearch/managers/thl/ledger_manager/thl_ledger.py index 5210189..de7cae9 100644 --- a/generalresearch/managers/thl/ledger_manager/thl_ledger.py +++ b/generalresearch/managers/thl/ledger_manager/thl_ledger.py @@ -303,7 +303,7 @@ class ThlLedgerManager(LedgerManager): user: User, created: datetime | None = None, force: bool = False, - ) -> PositiveInt: + ) -> LedgerTransaction: """ Create a transaction when we complete a task from a marketplace, showing the marketplace paying us for the task complete. diff --git a/generalresearch/managers/thl/payout.py b/generalresearch/managers/thl/payout.py index 3968ea7..af4f712 100644 --- a/generalresearch/managers/thl/payout.py +++ b/generalresearch/managers/thl/payout.py @@ -145,7 +145,7 @@ class BrokerageProductPayoutEventManager(PayoutEventManager): amount = USDCent(payout_event.amount) product_id = payout_event.product_id - txs = thl_ledger_manager.get_tx_by_tag(tag) + txs = thl_ledger_manager.get_txs_by_tag(tag) if not txs: return False diff --git a/generalresearch/managers/thl/user_manager/user_manager.py b/generalresearch/managers/thl/user_manager/user_manager.py index df3fb6b..15fd291 100644 --- a/generalresearch/managers/thl/user_manager/user_manager.py +++ b/generalresearch/managers/thl/user_manager/user_manager.py @@ -7,9 +7,10 @@ from datetime import datetime from threading import Lock from typing import TYPE_CHECKING -from cachetools import TTLCache, cachedmethod +from cachetools import cachedmethod from pydantic import RedisDsn +from generalresearch.cacheing import InstrumentedTTLCache from generalresearch.managers.base import Permission from generalresearch.managers.thl.product import ProductManager from generalresearch.managers.thl.user_manager.exceptions import ( @@ -87,7 +88,7 @@ class UserManager: self.product_manager = ProductManager( pg_config=pg_config, permissions=[Permission.READ] ) - self.get_user_cache = TTLCache(maxsize=10000, ttl=30) + self.get_user_cache = InstrumentedTTLCache(maxsize=10000, ttl=30) self.get_user_cache_lock = Lock() def set_last_seen(self, user: User) -> None: @@ -147,6 +148,10 @@ class UserManager: with self.get_user_cache_lock: self.get_user_cache.clear() + def cache_info(self): + with self.get_user_cache_lock: + return self.get_user_cache.cache_info() + @deepcopy_return @cachedmethod( operator.attrgetter("get_user_cache"), diff --git a/test_utils/models/thl/conftest.py b/test_utils/models/thl/conftest.py index 433003c..a8ad037 100644 --- a/test_utils/models/thl/conftest.py +++ b/test_utils/models/thl/conftest.py @@ -628,13 +628,9 @@ def user_payout_event_factory( debit_account_uuid = debit_account_uuid or uuid4().hex cashout_method_uuid = cashout_method_uuid or uuid4().hex - # account_reference_type = account_reference_type or f"acct-ref-{uuid4().hex}" - # account_reference_uuid = account_reference_uuid or uuid4().hex - # cashout_method_uuid = cashout_method_uuid or uuid4().hex amount = amount or randint(a=99, b=9_999) status = status or rand_choice(list(PayoutStatus)) - description = description or f"desc-{uuid4().hex[:12]}" # ext_ref_id = ext_ref_id or f"ext-ref-{uuid4().hex[:8]}" payout_type = payout_type or rand_choice(list(PayoutType)) request_data = request_data or {} @@ -643,10 +639,7 @@ def user_payout_event_factory( return user_payout_event_manager.create( uuid=uuid, debit_account_uuid=debit_account_uuid, - account_reference_type=account_reference_type, - account_reference_uuid=account_reference_uuid, cashout_method_uuid=cashout_method_uuid, - description=description, created=created, amount=amount, status=status, diff --git a/test_utils/models/upk/conftest.py b/test_utils/models/upk/conftest.py index ad96bbb..520d566 100644 --- a/test_utils/models/upk/conftest.py +++ b/test_utils/models/upk/conftest.py @@ -33,6 +33,13 @@ def insert_data_from_csv( df["id"] = df["id"].map(lambda x: UUID(x).hex) args = df.to_dict("tight")["data"] + # Fix issue with nullable cols + if table_name == 'marketplace_question': + for arg in args: + for idx, col in enumerate(arg): + if pd.isnull(col): + arg[idx] = None + with thl_web_rw.make_connection() as conn: with conn.cursor() as c: if disable_fk_checks: diff --git a/tests/managers/thl/test_ledger/test_lm_tx.py b/tests/managers/thl/test_ledger/test_lm_tx.py index 025f6ac..d98b838 100644 --- a/tests/managers/thl/test_ledger/test_lm_tx.py +++ b/tests/managers/thl/test_ledger/test_lm_tx.py @@ -206,12 +206,12 @@ class TestLedgerManagerGetTx: self, ledger_tx: LedgerTransaction, tag: str, ledger_manager: LedgerManager ): # (1) search for a random tag - res = ledger_manager.get_tx_by_tag(tag="aaa:bbb") + res = ledger_manager.get_txs_by_tag(tag="aaa:bbb") assert isinstance(res, list) assert len(res) == 0 # (2) search for the tag that was used during ledger_transaction creation - res = ledger_manager.get_tx_by_tag(tag=tag) + res = ledger_manager.get_txs_by_tag(tag=tag) assert isinstance(res, list) assert len(res) == 1 diff --git a/tests/managers/thl/test_ledger/test_lm_tx_locks.py b/tests/managers/thl/test_ledger/test_lm_tx_locks.py index 166598e..91d9cbc 100644 --- a/tests/managers/thl/test_ledger/test_lm_tx_locks.py +++ b/tests/managers/thl/test_ledger/test_lm_tx_locks.py @@ -38,7 +38,6 @@ logger = logging.getLogger("LedgerManager") class TestLedgerLocks: - def test_a( self, user_factory: Callable[..., User], @@ -108,16 +107,16 @@ class TestLedgerLocks: f1 = generate_condition_mp_payment(wall=w1) f2 = generate_condition_mp_payment(wall=w2) f3 = generate_condition_mp_payment(wall=w3) - assert f1(ledger_manager) is False - assert f2(lm=ledger_manager) is False - assert f3(lm=ledger_manager) is True + assert f1(ledger_manager) == (False, "duplicate tag") + assert f2(lm=ledger_manager) == (False, "duplicate tag") + assert f3(lm=ledger_manager)[0] == True condition = f3 create_tx_func = lambda: thl_ledger_manager.create_tx_task_complete_( wall=w3, user=user ) assert isinstance(create_tx_func, Callable) - assert f3(ledger_manager) is True + assert f3(ledger_manager)[0] is True ledger_manager.redis_client.delete(flag_name) ledger_manager.redis_client.delete(lock_name) @@ -125,13 +124,14 @@ class TestLedgerLocks: tx = thl_ledger_manager.create_tx_protected( lock_key=lock_key, condition=condition, create_tx_func=create_tx_func ) - assert f3(ledger_manager) is False + assert f3(ledger_manager) == (False, "duplicate tag") # purposely hold the lock open tx = None ledger_manager.redis_client.set(lock_name, "1") - with caplog.at_level(logging.ERROR), pytest.raises( - expected_exception=LedgerTransactionCreateLockError + with ( + caplog.at_level(logging.ERROR), + pytest.raises(expected_exception=LedgerTransactionCreateLockError), ): tx = thl_ledger_manager.create_tx_protected( lock_key=lock_key, @@ -231,8 +231,9 @@ class TestLedgerLocks: # Purposely hold the lock open ledger_manager.redis_client.set(name=lock_name, value="1") - with caplog.at_level(logging.DEBUG), pytest.raises( - expected_exception=LedgerTransactionCreateLockError + with ( + caplog.at_level(logging.DEBUG), + pytest.raises(expected_exception=LedgerTransactionCreateLockError), ): tx = thl_ledger_manager.create_tx_task_complete( wall=wall3, user=user, created=wall3.started diff --git a/tests/managers/thl/test_ledger/test_thl_pem.py b/tests/managers/thl/test_ledger/test_thl_pem.py index 2d03b25..348a02d 100644 --- a/tests/managers/thl/test_ledger/test_thl_pem.py +++ b/tests/managers/thl/test_ledger/test_thl_pem.py @@ -8,13 +8,9 @@ from uuid import UUID, uuid4 import pytest from generalresearch.currency import USDCent -from generalresearch.models.thl.definitions import PayoutStatus from generalresearch.models.thl.payout import ( BrokerageProductPayoutEvent, ) -from generalresearch.models.thl.wallet.cashout_method import ( - CashoutRequestInfo, -) if TYPE_CHECKING: from generalresearch.managers.thl.ledger_manager.ledger import LedgerManager @@ -23,6 +19,7 @@ if TYPE_CHECKING: ) from generalresearch.managers.thl.payout import ( BrokerageProductPayoutEventManager, + BusinessPayoutEventManager, UserPayoutEventManager, ) from generalresearch.models.thl.payout import UserPayoutEvent @@ -153,6 +150,7 @@ class TestThlPayoutEventManager: create_main_accounts: Callable[..., None], thl_ledger_manager: ThlLedgerManager, brokerage_product_payout_event_manager: BrokerageProductPayoutEventManager, + business_payout_event_manager: BusinessPayoutEventManager, ledger_manager: LedgerManager, ): @@ -166,13 +164,13 @@ class TestThlPayoutEventManager: # Save a Brokerage Product Payout, so we have something in the # Payout Event table and the respective ledger TX and Entry rows for it - pe = brokerage_product_payout_event_manager.create_bp_payout_event( + bus_pe = business_payout_event_manager.create_bp_payout_event( thl_ledger_manager=thl_ledger_manager, product=product, amount=USDCent(rand_amount), - skip_wallet_balance_check=True, - skip_one_per_day_check=True, + ext_ref_id=uuid4().hex ) + pe = bus_pe.bp_payouts[0] assert isinstance(pe, BrokerageProductPayoutEvent) # Now try to query for it! @@ -183,7 +181,7 @@ class TestThlPayoutEventManager: res = thl_ledger_manager.get_tx_bp_payouts(account_uuids=[uuid4().hex]) assert len(res) == 0 - # Confirm it added to the users balance. The amount is negative because + # Confirm it added to the user's balance. The amount is negative because # money was sent to the Brokerage product: Product, but they didn't have # any activity that earned them money bal = ledger_manager.get_account_balance(account=account_bp_wallet) diff --git a/tests/managers/thl/test_payout.py b/tests/managers/thl/test_payout.py index a9cd52a..be121d8 100644 --- a/tests/managers/thl/test_payout.py +++ b/tests/managers/thl/test_payout.py @@ -388,7 +388,7 @@ class TestBusinessPayoutEventManager: ledger_collection.initial_load(client=None, sync=True) pop_ledger_merge.build(client=client_no_amm, ledger_coll=ledger_collection) gr_business.prebuild_balance( - thl_pg_config=thl_web_rr, + product_manager=product_manager, lm=thl_ledger_manager, ds=mnt_filepath, client=client_no_amm, @@ -472,6 +472,7 @@ class TestBusinessPayoutEventManager: self, business_payout_event_manager: BusinessPayoutEventManager, request ): # TODO: Generate this file at random + # @max file is not commited fp = os.path.join( request.config.rootpath, "data/pytest_recoup_proportional.csv" ) @@ -690,7 +691,7 @@ class TestBusinessPayoutEventManager: ledger_collection.initial_load(client=None, sync=True) pop_ledger_merge.build(client=client_no_amm, ledger_coll=ledger_collection) gr_business.prebuild_balance( - thl_pg_config=thl_web_rr, + product_manager=product_manager, lm=ledger_manager, ds=mnt_filepath, client=client_no_amm, @@ -862,18 +863,18 @@ class TestBusinessPayoutEventManager: assert gr_business.payouts is None gr_business.prebuild_payouts( - thl_pg_config=thl_web_rr, - thl_lm=thl_ledger_manager, bpem=business_payout_event_manager, ) assert isinstance(gr_business.payouts, list) assert len(gr_business.payouts) == 1 assert gr_business.payouts[0].ext_ref_id == ach_id1 + ach_id2 = uuid4().hex + bp1 = business_payout_event_manager.create_from_ach_or_wire( business=gr_business, amount=USDCent(bb1.available_balance), - transaction_id=ach_id1, + transaction_id=ach_id2, pm=product_manager, thl_lm=thl_ledger_manager, created=start + timedelta(days=1, hours=5), @@ -885,16 +886,12 @@ class TestBusinessPayoutEventManager: bp1_tx = brokerage_product_payout_event_manager.check_for_ledger_tx( thl_ledger_manager=thl_ledger_manager, payout_event=bp1.bp_payouts[0], - product_id=bp1.bp_payouts[0].product_id, - amount=bp1.bp_payouts[0].amount, ) assert bp1_tx bp2_tx = brokerage_product_payout_event_manager.check_for_ledger_tx( thl_ledger_manager=thl_ledger_manager, payout_event=bp1.bp_payouts[1], - product_id=bp1.bp_payouts[1].product_id, - amount=bp1.bp_payouts[1].amount, ) assert bp2_tx @@ -996,13 +993,12 @@ class TestBusinessPayoutEventManager: wall_req_cpi=Decimal("50.00"), started=start + timedelta(days=1, hours=2, minutes=1 + idx), ) - payout_event_manager.set_account_lookup_table(thl_lm=thl_ledger_manager) # Now that we paid out the business: Business, let's confirm the updated balances ledger_collection.initial_load(client=None, sync=True) pop_ledger_merge.build(client=client_no_amm, ledger_coll=ledger_collection) gr_business.prebuild_balance( - thl_pg_config=thl_web_rr, + product_manager=product_manager, lm=ledger_manager, ds=mnt_filepath, client=client_no_amm, @@ -1042,6 +1038,7 @@ class TestBusinessPayoutEventManager: pm=product_manager, thl_lm=thl_ledger_manager, created=start + timedelta(days=1, hours=3), + transaction_id=uuid4().hex, ) assert isinstance(bp1, BusinessPayoutEvent) assert len(bp1.bp_payouts) == 3 @@ -1057,7 +1054,7 @@ class TestBusinessPayoutEventManager: # balance.available_balance are reflective of having a $250 ACH/Wire # sent to the Business gr_business.prebuild_balance( - thl_pg_config=thl_web_rr, + product_manager=product_manager, lm=ledger_manager, ds=mnt_filepath, client=client_no_amm, @@ -1123,14 +1120,13 @@ class TestBusinessPayoutEventManager: wall_req_cpi=Decimal("7.50"), started=start + timedelta(days=1, hours=1 + iidx, minutes=1 + idx), ) - payout_event_manager.set_account_lookup_table(thl_lm=thl_ledger_manager) rm_ledger_collection() rm_pop_ledger_merge() ledger_collection.initial_load(client=None, sync=True) pop_ledger_merge.build(client=client_no_amm, ledger_coll=ledger_collection) gr_business.prebuild_balance( - thl_pg_config=thl_web_rr, + product_manager=product_manager, lm=ledger_manager, ds=mnt_filepath, client=client_no_amm, @@ -1151,7 +1147,7 @@ class TestBusinessPayoutEventManager: ledger_collection.initial_load(client=None, sync=True) pop_ledger_merge.build(client=client_no_amm, ledger_coll=ledger_collection) gr_business.prebuild_balance( - thl_pg_config=thl_web_rr, + product_manager=product_manager, lm=ledger_manager, ds=mnt_filepath, client=client_no_amm, @@ -1175,12 +1171,10 @@ class TestBusinessPayoutEventManager: ledger_collection.initial_load(client=None, sync=True) pop_ledger_merge.build(client=client_no_amm, ledger_coll=ledger_collection) gr_business.prebuild_payouts( - thl_pg_config=thl_web_rr, - thl_lm=thl_ledger_manager, bpem=business_payout_event_manager, ) gr_business.prebuild_balance( - thl_pg_config=thl_web_rr, + product_manager=product_manager, lm=ledger_manager, ds=mnt_filepath, client=client_no_amm, diff --git a/tests/managers/thl/test_session_manager.py b/tests/managers/thl/test_session_manager.py index 60edcb9..4b122ec 100644 --- a/tests/managers/thl/test_session_manager.py +++ b/tests/managers/thl/test_session_manager.py @@ -117,6 +117,7 @@ class TestSessionManagerFilter: def test_team( self, product_factory: Callable[..., Product], + product_manager, user_factory: Callable[..., User], gr_team: Team, session_manager: SessionManager, @@ -129,7 +130,7 @@ class TestSessionManagerFilter: u = user_factory(product=p1) session_manager.create(started=utc_hour_ago, user=u, uuid_id=uuid4().hex) - gr_team.prefetch_products(thl_pg_config=thl_web_rr) + gr_team.prefetch_products(product_manager=product_manager) assert len(gr_team.product_uuids) == 1 res = session_manager.filter(product_uuids=gr_team.product_uuids) assert len(res) == 5 @@ -142,6 +143,7 @@ class TestSessionManagerFilter: session_manager: SessionManager, utc_hour_ago: datetime, thl_web_rr: PostgresConfig, + product_manager, ): p1 = product_factory(business=gr_business) @@ -149,7 +151,7 @@ class TestSessionManagerFilter: u = user_factory(product=p1) session_manager.create(started=utc_hour_ago, user=u, uuid_id=uuid4().hex) - gr_business.prefetch_products(thl_pg_config=thl_web_rr) + gr_business.prefetch_products(product_manager=product_manager) assert len(gr_business.product_uuids) == 1 res = session_manager.filter(product_uuids=gr_business.product_uuids) assert len(res) == 5 diff --git a/tests/managers/thl/test_survey_penalty.py b/tests/managers/thl/test_survey_penalty.py index 04f69d2..71cea16 100644 --- a/tests/managers/thl/test_survey_penalty.py +++ b/tests/managers/thl/test_survey_penalty.py @@ -103,7 +103,7 @@ class TestSurveyPenalty: assert res == {"t:a": 0.1, "t:b": 0.2, "u:b": 0.1} assert surveypenalty_manager.cache.currsize == 1 cached_key = tuple(list(next(iter(surveypenalty_manager.cache.keys())))[1:]) - assert cached_key == ("product_id", product_uuid, "team_id", team_id_random) + assert cached_key == (("product_id", product_uuid), ("team_id", team_id_random)) # Both don't exist, return nothing res = surveypenalty_manager.get_penalties_for( diff --git a/tests/managers/thl/test_user_manager/test_base.py b/tests/managers/thl/test_user_manager/test_base.py index 5d12052..2a7e879 100644 --- a/tests/managers/thl/test_user_manager/test_base.py +++ b/tests/managers/thl/test_user_manager/test_base.py @@ -56,43 +56,43 @@ class TestUserManager: def test_get_user_no_inmemory(self): self.user_manager.clear_user_inmemory_cache(self.user) - self.user_manager.get_user.__wrapped__.cache_clear() + self.user_manager.cache_clear() u = self.user_manager.get_user(user_id=self.user.user_id) # this should hit mysql assert u == self.user - cache_info = self.user_manager.get_user.__wrapped__.cache_info() - assert cache_info.hits == 0, cache_info - assert cache_info.misses == 1, cache_info + cache_info = self.user_manager.cache_info() + assert cache_info['hits'] == 0, cache_info + assert cache_info['misses'] == 1, cache_info # this should hit the lru cache u = self.user_manager.get_user(user_id=self.user.user_id) assert u == self.user - cache_info = self.user_manager.get_user.__wrapped__.cache_info() - assert cache_info.hits == 1, cache_info - assert cache_info.misses == 1, cache_info + cache_info = self.user_manager.cache_info() + assert cache_info['hits'] == 1, cache_info + assert cache_info['misses'] == 1, cache_info def test_get_user_with_inmemory(self): # user_manager = self.get_user_manager() self.user_manager.set_user_inmemory_cache(self.user) - self.user_manager.get_user.__wrapped__.cache_clear() + self.user_manager.cache_clear() u = self.user_manager.get_user(user_id=self.user.user_id) # this should hit inmemory cache assert u == self.user - cache_info = self.user_manager.get_user.__wrapped__.cache_info() - assert cache_info.hits == 0, cache_info - assert cache_info.misses == 1, cache_info + cache_info = self.user_manager.cache_info() + assert cache_info['hits'] == 0, cache_info + assert cache_info['misses'] == 1, cache_info # this should hit the lru cache u = self.user_manager.get_user(user_id=self.user.user_id) assert u == self.user - cache_info = self.user_manager.get_user.__wrapped__.cache_info() - assert cache_info.hits == 1, cache_info - assert cache_info.misses == 1, cache_info + cache_info = self.user_manager.cache_info() + assert cache_info['hits'] == 1, cache_info + assert cache_info['misses'] == 1, cache_info class TestBlockUserManager: diff --git a/tests/managers/thl/test_user_manager/test_redis.py b/tests/managers/thl/test_user_manager/test_redis.py index f6b59c9..89d0a7a 100644 --- a/tests/managers/thl/test_user_manager/test_redis.py +++ b/tests/managers/thl/test_user_manager/test_redis.py @@ -8,15 +8,14 @@ from generalresearch.managers.base import Permission from generalresearch.managers.thl.user_manager.redis_user_manager import ( RedisUserManager, ) +from generalresearch.redis_helper import RedisConfig if TYPE_CHECKING: - from generalresearch.config import GRLBaseSettings from generalresearch.models.thl.user import User from generalresearch.pg_helper import PostgresConfig class TestUserManagerRedis: - def test_get_notset(self, redis_user_manager: RedisUserManager, user: User): redis_user_manager.clear_user(user=user) assert redis_user_manager.get_user(user_id=user.user_id) is None @@ -48,10 +47,10 @@ class TestUserManagerRedis: def test_get_with_cache_prefix( self, - settings: GRLBaseSettings, user: User, thl_web_rw: PostgresConfig, thl_web_rr: PostgresConfig, + thl_redis_config: RedisConfig, ): """ Confirm the prefix functionality is working; we do this so it @@ -67,19 +66,19 @@ class TestUserManagerRedis: pg_config=thl_web_rw, pg_config_rr=thl_web_rr, sql_permissions=[Permission.UPDATE, Permission.CREATE], - redis=settings.redis, - redis_timeout=settings.redis_timeout, + redis=thl_redis_config.dsn, + redis_timeout=1, ) um2 = UserManager( pg_config=thl_web_rw, pg_config_rr=thl_web_rr, sql_permissions=[Permission.UPDATE, Permission.CREATE], - redis=settings.redis, - redis_timeout=settings.redis_timeout, + redis=thl_redis_config.dsn, + redis_timeout=1, cache_prefix="user-lookup-v2", ) - + user = user.to_user_ref() um1.get_or_create_user( product_id=user.product_id, product_user_id=user.product_user_id ) diff --git a/tests/managers/thl/test_userhealth.py b/tests/managers/thl/test_userhealth.py index 8256185..bd2152a 100644 --- a/tests/managers/thl/test_userhealth.py +++ b/tests/managers/thl/test_userhealth.py @@ -226,13 +226,11 @@ class TestIPRecordManager: self, thl_web_rr: PostgresConfig, thl_redis_config: RedisConfig, - geoip_info_manager: GeoIpInfoManager, ip_record_manager: IPRecordManager, ): instance = IPRecordManager( pg_config=thl_web_rr, redis_config=thl_redis_config, - geoip_info_manager=geoip_info_manager, ) assert isinstance(instance, IPRecordManager) assert isinstance(ip_record_manager, IPRecordManager) -- cgit v1.2.3