From 6b4f8d6b8700a65bdf74beb3d95c969a81464a56 Mon Sep 17 00:00:00 2001 From: Max Nanis Date: Tue, 18 Aug 2026 13:14:45 -0700 Subject: build ignore, py-utils replace, typing with Ruff --- .../models/thl/test_contest/test_raffle_contest.py | 10 +- tests/models/thl/test_marketplace_condition.py | 26 ++--- tests/models/thl/test_payout_format.py | 10 +- tests/models/thl/test_product.py | 127 +++++++++++---------- tests/models/thl/test_product_userwalletconfig.py | 1 - tests/models/thl/test_upkquestion.py | 22 ++-- tests/models/thl/test_user.py | 11 +- tests/models/thl/test_user_streak.py | 6 +- tests/models/thl/test_wall.py | 4 +- 9 files changed, 115 insertions(+), 102 deletions(-) (limited to 'tests/models/thl') diff --git a/tests/models/thl/test_contest/test_raffle_contest.py b/tests/models/thl/test_contest/test_raffle_contest.py index e1c0a15..d7920f0 100644 --- a/tests/models/thl/test_contest/test_raffle_contest.py +++ b/tests/models/thl/test_contest/test_raffle_contest.py @@ -6,26 +6,26 @@ from pytest import approx from generalresearch.currency import USDCent from generalresearch.models.thl.contest import ( - ContestPrize, ContestEndCondition, + ContestPrize, ) from generalresearch.models.thl.contest.contest_entry import ContestEntry from generalresearch.models.thl.contest.definitions import ( + ContestEndReason, ContestEntryType, ContestPrizeKind, - ContestType, ContestStatus, - ContestEndReason, + ContestType, ) from generalresearch.models.thl.contest.raffle import RaffleContest - +from generalresearch.models.thl.product import Product from tests.models.thl.test_contest.test_contest import TestContest class TestRaffleContest(TestContest): @pytest.fixture(scope="function") - def raffle_contest(self, product) -> RaffleContest: + def raffle_contest(self, product: Product) -> RaffleContest: return RaffleContest( product_id=product.uuid, name=f"Raffle Contest {uuid4().hex}", diff --git a/tests/models/thl/test_marketplace_condition.py b/tests/models/thl/test_marketplace_condition.py index 217616d..8a4b25c 100644 --- a/tests/models/thl/test_marketplace_condition.py +++ b/tests/models/thl/test_marketplace_condition.py @@ -7,8 +7,8 @@ class TestMarketplaceCondition: def test_list_or(self): from generalresearch.models import LogicalOperator from generalresearch.models.thl.survey.condition import ( - MarketplaceCondition, ConditionValueType, + MarketplaceCondition, ) user_qas = {"q1": {"a2"}} @@ -48,8 +48,8 @@ class TestMarketplaceCondition: def test_list_or_negate(self): from generalresearch.models import LogicalOperator from generalresearch.models.thl.survey.condition import ( - MarketplaceCondition, ConditionValueType, + MarketplaceCondition, ) user_qas = {"q1": {"a2"}} @@ -89,8 +89,8 @@ class TestMarketplaceCondition: def test_list_and(self): from generalresearch.models import LogicalOperator from generalresearch.models.thl.survey.condition import ( - MarketplaceCondition, ConditionValueType, + MarketplaceCondition, ) user_qas = {"q1": {"a1", "a2"}} @@ -139,8 +139,8 @@ class TestMarketplaceCondition: def test_list_and_negate(self): from generalresearch.models import LogicalOperator from generalresearch.models.thl.survey.condition import ( - MarketplaceCondition, ConditionValueType, + MarketplaceCondition, ) user_qas = {"q1": {"a1", "a2"}} @@ -180,8 +180,8 @@ class TestMarketplaceCondition: def test_ranges(self): from generalresearch.models import LogicalOperator from generalresearch.models.thl.survey.condition import ( - MarketplaceCondition, ConditionValueType, + MarketplaceCondition, ) user_qas = {"q1": {"2", "50"}} @@ -247,8 +247,8 @@ class TestMarketplaceCondition: def test_ranges_to_list(self): from generalresearch.models import LogicalOperator from generalresearch.models.thl.survey.condition import ( - MarketplaceCondition, ConditionValueType, + MarketplaceCondition, ) user_qas = {"q1": {"2", "50"}} @@ -267,8 +267,8 @@ class TestMarketplaceCondition: def test_ranges_infinity(self): from generalresearch.models import LogicalOperator from generalresearch.models.thl.survey.condition import ( - MarketplaceCondition, ConditionValueType, + MarketplaceCondition, ) user_qas = {"q1": {"2", "50"}} @@ -310,8 +310,8 @@ class TestMarketplaceCondition: def test_answered(self): from generalresearch.models.thl.survey.condition import ( - MarketplaceCondition, ConditionValueType, + MarketplaceCondition, ) user_qas = {"q1": {"a2"}} @@ -346,8 +346,8 @@ class TestMarketplaceCondition: def test_invite(self): from generalresearch.models.thl.survey.condition import ( - MarketplaceCondition, ConditionValueType, + MarketplaceCondition, ) user_groups = {"g1", "g2", "g3"} @@ -357,14 +357,14 @@ class TestMarketplaceCondition: value_type=ConditionValueType.RECONTACT, values=["g1", "g4"], ) - assert c.evaluate_criterion(user_qas=dict(), user_groups=user_groups) + assert c.evaluate_criterion(user_qas={}, user_groups=user_groups) c = MarketplaceCondition( question_id=None, negate=False, value_type=ConditionValueType.RECONTACT, values=["g4"], ) - assert not c.evaluate_criterion(user_qas=dict(), user_groups=user_groups) + assert not c.evaluate_criterion(user_qas={}, user_groups=user_groups) c = MarketplaceCondition( question_id=None, @@ -372,11 +372,11 @@ class TestMarketplaceCondition: value_type=ConditionValueType.RECONTACT, values=["g1", "g4"], ) - assert not c.evaluate_criterion(user_qas=dict(), user_groups=user_groups) + assert not c.evaluate_criterion(user_qas={}, user_groups=user_groups) c = MarketplaceCondition( question_id=None, negate=True, value_type=ConditionValueType.RECONTACT, values=["g4"], ) - assert c.evaluate_criterion(user_qas=dict(), user_groups=user_groups) + assert c.evaluate_criterion(user_qas={}, user_groups=user_groups) diff --git a/tests/models/thl/test_payout_format.py b/tests/models/thl/test_payout_format.py index dc91f39..83fde25 100644 --- a/tests/models/thl/test_payout_format.py +++ b/tests/models/thl/test_payout_format.py @@ -2,8 +2,8 @@ import pytest from pydantic import BaseModel from generalresearch.models.thl.payout_format import ( - PayoutFormatType, PayoutFormatField, + PayoutFormatType, format_payout_format, ) @@ -20,16 +20,16 @@ class TestPayoutFormat: PayoutFormatTestClass(payout_format="${payout/100:.2f}") # invalid - with pytest.raises(expected_exception=Exception) as e: + with pytest.raises(expected_exception=ValueError): PayoutFormatTestClass(payout_format="{payout10:,.0f} Points") - with pytest.raises(expected_exception=Exception) as e: + with pytest.raises(expected_exception=ValueError): PayoutFormatTestClass(payout_format="payout:,.0f} Points") - with pytest.raises(expected_exception=Exception): + with pytest.raises(expected_exception=ValueError): PayoutFormatTestClass(payout_format="payout") - with pytest.raises(expected_exception=Exception): + with pytest.raises(expected_exception=ValueError): PayoutFormatTestClass(payout_format="{payout;import sys:.0f}") def test_payout_format(self): diff --git a/tests/models/thl/test_product.py b/tests/models/thl/test_product.py index 52f60c2..2d20ba1 100644 --- a/tests/models/thl/test_product.py +++ b/tests/models/thl/test_product.py @@ -1,26 +1,39 @@ import os import shutil -from datetime import datetime, timezone, timedelta +from datetime import datetime, timedelta, timezone from decimal import Decimal -from typing import Optional +from typing import Callable, Optional from uuid import uuid4 import pytest +from dask.distributed import Client as DaskClient from pydantic import ValidationError from generalresearch.currency import USDCent +from generalresearch.incite import GRLDatasets +from generalresearch.incite.mergers.pop_ledger import PopLedgerMerge +from generalresearch.managers.thl.ledger_manager.thl_ledger import ( + ThlLedgerManager, +) +from generalresearch.managers.thl.product import ProductManager from generalresearch.models import Source +from generalresearch.models.gr.business import Business +from generalresearch.models.thl.finance import ProductBalances from generalresearch.models.thl.product import ( - Product, + BrokerageProductPayoutEvent, + BrokerageProductPayoutEventManager, + IntegrationMode, PayoutConfig, PayoutTransformation, + Product, ProfilingConfig, + SourceConfig, SourcesConfig, - IntegrationMode, SupplyConfig, - SourceConfig, SupplyPolicy, ) +from generalresearch.models.thl.session import Session +from generalresearch.models.thl.user import User class TestProduct: @@ -28,11 +41,11 @@ class TestProduct: def test_init(self): # By default, just a Pydantic instance doesn't have an id_int instance = Product.model_validate( - dict( - id="968a9acc79b74b6fb49542d82516d284", - name="test-968a9acc", - redirect_url="https://www.google.com/hey", - ) + obj={ + "id": "968a9acc79b74b6fb49542d82516d284", + "name": "test-968a9acc", + "redirect_url": "https://www.google.com/hey", + } ) assert instance.id_int is None @@ -40,29 +53,31 @@ class TestProduct: # We're not excluding anything here, only in the "*Out" variants assert "id_int" in res - def test_init_db(self, product_manager): + def test_init_db(self, product_manager: ProductManager): # By default, just a Pydantic instance doesn't have an id_int instance = product_manager.create_dummy() assert isinstance(instance.id_int, int) res = instance.model_dump_json() + assert isinstance(res, Product) # we json skip & exclude res = instance.model_dump() + assert isinstance(res, Product) def test_redirect_url(self): p = Product.model_validate( - dict( - id="968a9acc79b74b6fb49542d82516d284", - created="2023-09-21T22:13:09.274672Z", - commission_pct=Decimal("0.05"), - enabled=True, - sources=[{"name": "d", "active": True}], - name="test-968a9acc", - max_session_len=600, - team_id="8b5e94afd8a246bf8556ad9986486baa", - redirect_url="https://www.google.com/hey", - ) + obj={ + "id": "968a9acc79b74b6fb49542d82516d284", + "created": "2023-09-21T22:13:09.274672Z", + "commission_pct": Decimal("0.05"), + "enabled": True, + "sources": [{"name": "d", "active": True}], + "name": "test-968a9acc", + "max_session_len": 600, + "team_id": "8b5e94afd8a246bf8556ad9986486baa", + "redirect_url": "https://www.google.com/hey", + } ) with pytest.raises(expected_exception=ValidationError): @@ -99,14 +114,14 @@ class TestProduct: p.harmonizer_domain = "https://profile.generalresearch.com/" p.harmonizer_domain = "https://profile.generalresearch.com" assert p.harmonizer_domain == "https://profile.generalresearch.com/" - with pytest.raises(expected_exception=Exception): + with pytest.raises(expected_exception=ValueError): p.harmonizer_domain = "" - with pytest.raises(expected_exception=Exception): + with pytest.raises(expected_exception=ValueError): p.harmonizer_domain = None - with pytest.raises(expected_exception=Exception): + with pytest.raises(expected_exception=ValueError): # no https p.harmonizer_domain = "http://profile.generalresearch.com" - with pytest.raises(expected_exception=Exception): + with pytest.raises(expected_exception=ValueError): # "/a" at the end p.harmonizer_domain = "https://profile.generalresearch.com/a" @@ -201,23 +216,29 @@ class TestProduct: assert p.calculate_user_payment( Decimal("0.10"), user_wallet_balance=Decimal(0) ) == Decimal("0.07") + assert p.calculate_user_payment( Decimal("1.05"), user_wallet_balance=Decimal(0) ) == Decimal("0.97") + assert p.calculate_user_payment( Decimal(".05"), user_wallet_balance=Decimal(1) ) == Decimal("0.02") + # final balance will be <0, so pay the full amount assert p.calculate_user_payment( Decimal(".50"), user_wallet_balance=Decimal(-1) ) == p.calculate_user_payment(Decimal("0.50")) + # final balance will be >0, so do the 7c rounding - assert p.calculate_user_payment( + res1 = p.calculate_user_payment( Decimal(".50"), user_wallet_balance=Decimal("-0.10") - ) == ( - p.calculate_user_payment(Decimal(".40"), user_wallet_balance=Decimal(0)) - - Decimal("-0.10") ) + res2 = p.calculate_user_payment( + bp_payout=Decimal(".40"), user_wallet_balance=Decimal(0) + ) + assert res2 + assert res1 == (res2 - Decimal("-0.10")) def test_payout_xform_none(self): p = Product( @@ -568,34 +589,26 @@ class TestProductFinancials: def test_balance( self, - business, - product_factory, - user_factory, - mnt_filepath, - bp_payout_factory, - thl_lm, - lm, - duration, - offset, - thl_redis_config, - start, - thl_web_rr, - brokerage_product_payout_event_manager, - session_with_tx_factory, + business: Business, + product_factory: Callable[..., Product], + user_factory: Callable[..., User], + mnt_filepath: GRLDatasets, + bp_payout_factory: Callable[..., BrokerageProductPayoutEvent], + thl_lm: ThlLedgerManager, + start: datetime, + brokerage_product_payout_event_manager: BrokerageProductPayoutEventManager, + session_with_tx_factory: Callable[..., Session], delete_ledger_db, create_main_accounts, - client_no_amm, + client_no_amm: DaskClient, ledger_collection, - pop_ledger_merge, + pop_ledger_merge: PopLedgerMerge, delete_df_collection, ): delete_ledger_db() create_main_accounts() delete_df_collection(coll=ledger_collection) - from generalresearch.models.thl.product import Product - from generalresearch.models.thl.user import User - from generalresearch.models.thl.finance import ProductBalances from generalresearch.currency import USDCent p1: Product = product_factory(business=business) @@ -759,20 +772,16 @@ class TestProductBalance: def test_inconsistent( self, - product, - mnt_filepath, - thl_lm, - client_no_amm, - thl_redis_config, - brokerage_product_payout_event_manager, + product: Product, + mnt_filepath: GRLDatasets, + thl_lm: ThlLedgerManager, + client_no_amm: DaskClient, delete_ledger_db, create_main_accounts, delete_df_collection, ledger_collection, - business, - user_factory, - product_factory, - session_with_tx_factory, + user_factory: Callable[..., User], + session_with_tx_factory: Callable[..., Session], pop_ledger_merge, start, bp_payout_factory, diff --git a/tests/models/thl/test_product_userwalletconfig.py b/tests/models/thl/test_product_userwalletconfig.py index 4583c46..4f6a6cc 100644 --- a/tests/models/thl/test_product_userwalletconfig.py +++ b/tests/models/thl/test_product_userwalletconfig.py @@ -4,7 +4,6 @@ from random import shuffle as rshuffle from generalresearch.models.thl.product import ( UserWalletConfig, ) - from generalresearch.models.thl.wallet import PayoutType diff --git a/tests/models/thl/test_upkquestion.py b/tests/models/thl/test_upkquestion.py index e67427e..d32875c 100644 --- a/tests/models/thl/test_upkquestion.py +++ b/tests/models/thl/test_upkquestion.py @@ -9,8 +9,12 @@ class TestUpkQuestion: UPKImportance, ) - ui = UPKImportance(task_score=1, task_count=None) - ui = UPKImportance(task_score=0) + res = UPKImportance(task_score=1, task_count=None) + assert isinstance(res, UPKImportance) + + res = UPKImportance(task_score=0) + assert isinstance(res, UPKImportance) + with pytest.raises(ValidationError) as e: UPKImportance(task_score=-1) assert "Input should be greater than or equal to 0" in str(e.value) @@ -27,11 +31,11 @@ class TestUpkQuestion: def test_mc(self): from generalresearch.models.thl.profiling.upk_question import ( + UpkQuestion, UpkQuestionChoice, + UpkQuestionConfigurationMC, UpkQuestionSelectorMC, UpkQuestionType, - UpkQuestion, - UpkQuestionConfigurationMC, ) q = UpkQuestion( @@ -123,12 +127,12 @@ class TestUpkQuestion: def test_te(self): from generalresearch.models.thl.profiling.upk_question import ( - UpkQuestionType, + PatternValidation, UpkQuestion, + UpkQuestionConfigurationTE, UpkQuestionSelectorTE, + UpkQuestionType, UpkQuestionValidation, - PatternValidation, - UpkQuestionConfigurationTE, ) q = UpkQuestion( @@ -223,10 +227,10 @@ class TestUpkQuestion: def test_order(self): from generalresearch.models.thl.profiling.upk_question import ( + UpkQuestion, UpkQuestionChoice, UpkQuestionSelectorMC, UpkQuestionType, - UpkQuestion, order_exclusive_options, ) @@ -296,7 +300,7 @@ class TestUpkQuestionValidateAnswer: answer = ("0", "1") assert question.validate_question_answer(answer) == ( False, - "Single Answer MC question with >1 selected " "answers", + "Single Answer MC question with >1 selected answers", ) def test_validate_answer_MA(self): diff --git a/tests/models/thl/test_user.py b/tests/models/thl/test_user.py index 4f10861..25af694 100644 --- a/tests/models/thl/test_user.py +++ b/tests/models/thl/test_user.py @@ -1,7 +1,8 @@ import json -from datetime import datetime, timezone, timedelta +from datetime import datetime, timedelta, timezone from decimal import Decimal -from random import randint, choice as rand_choice +from random import choice as rand_choice +from random import randint from uuid import uuid4 import pytest @@ -315,7 +316,7 @@ class TestUserUUID: with pytest.raises(ValueError) as cm: User(user_id=self.user_id, uuid=Decimal("0")) - assert "1 validation error for User", str(cm.value) + assert "1 validation error for User" in str(cm.value) assert "Input should be a valid string" in str(cm.value) def test_empty(self): @@ -323,8 +324,8 @@ class TestUserUUID: with pytest.raises(ValueError) as cm: User(user_id=self.user_id, uuid="") - assert "1 validation error for User", str(cm.value) - assert "String should have at least 32 characters", str(cm.value) + assert "1 validation error for User" in str(cm.value) + assert "String should have at least 32 characters" in str(cm.value) def test_invalid_len(self): from generalresearch.models.thl.user import User diff --git a/tests/models/thl/test_user_streak.py b/tests/models/thl/test_user_streak.py index 72efd05..0cacd3e 100644 --- a/tests/models/thl/test_user_streak.py +++ b/tests/models/thl/test_user_streak.py @@ -1,14 +1,14 @@ from datetime import datetime, timedelta -from zoneinfo import ZoneInfo import pytest from pydantic import ValidationError +from zoneinfo import ZoneInfo from generalresearch.models.thl.user_streak import ( - UserStreak, - StreakPeriod, StreakFulfillment, + StreakPeriod, StreakState, + UserStreak, ) diff --git a/tests/models/thl/test_wall.py b/tests/models/thl/test_wall.py index 057aad2..8398c81 100644 --- a/tests/models/thl/test_wall.py +++ b/tests/models/thl/test_wall.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone, timedelta +from datetime import datetime, timedelta, timezone from decimal import Decimal from uuid import uuid4 @@ -76,7 +76,7 @@ class TestWall: ) assert "If status is f, status_code_1 should be in" in str(e.value) - with pytest.raises(expected_exception=ValidationError) as cm: + with pytest.raises(expected_exception=ValidationError): Wall( user_id=1, source=Source.DYNATA, -- cgit v1.2.3