aboutsummaryrefslogtreecommitdiff
path: root/tests/models/thl/test_user.py
diff options
context:
space:
mode:
authorMax Nanis2026-08-26 16:46:45 -0700
committerMax Nanis2026-08-26 16:46:45 -0700
commitcf239865ce440e1a71ee2360514eaeb018620ac9 (patch)
tree1fefff4a0bbf10df6431e541aa5a3ff5e76f0663 /tests/models/thl/test_user.py
parent47ea200eac0eaa7bef02f6ebb05de9afad5ee0d7 (diff)
downloadgeneralresearch-cf239865ce440e1a71ee2360514eaeb018620ac9.tar.gz
generalresearch-cf239865ce440e1a71ee2360514eaeb018620ac9.zip
Ruff afternoon!
Diffstat (limited to 'tests/models/thl/test_user.py')
-rw-r--r--tests/models/thl/test_user.py76
1 files changed, 19 insertions, 57 deletions
diff --git a/tests/models/thl/test_user.py b/tests/models/thl/test_user.py
index 0b8634a..9c4b548 100644
--- a/tests/models/thl/test_user.py
+++ b/tests/models/thl/test_user.py
@@ -1,4 +1,7 @@
+from __future__ import annotations
+
import json
+from collections.abc import Callable
from datetime import UTC, datetime, timedelta, timezone
from decimal import Decimal
from random import choice as rand_choice
@@ -8,18 +11,21 @@ from uuid import uuid4
import pytest
from pydantic import ValidationError
+from generalresearch.managers.thl.ledger_manager.thl_ledger import ThlLedgerManager
+from generalresearch.managers.thl.userhealth import AuditLogManager
+from generalresearch.models.thl.product import Product
+from generalresearch.models.thl.user import User
+
class TestUserUserID:
def test_valid(self):
- from generalresearch.models.thl.user import User
val = randint(1, 2**30)
user = User(user_id=val)
assert user.user_id == val
def test_type(self):
- from generalresearch.models.thl.user import User
# It will cast str to int
assert User(user_id="1").user_id == 1
@@ -44,7 +50,6 @@ class TestUserUserID:
assert "Input should be a valid integer," in str(cm.value)
def test_zero(self):
- from generalresearch.models.thl.user import User
with pytest.raises(expected_exception=ValidationError) as cm:
User(user_id=0)
@@ -52,7 +57,6 @@ class TestUserUserID:
assert "Input should be greater than 0" in str(cm.value)
def test_negative(self):
- from generalresearch.models.thl.user import User
with pytest.raises(expected_exception=ValidationError) as cm:
User(user_id=-1)
@@ -60,7 +64,6 @@ class TestUserUserID:
assert "Input should be greater than 0" in str(cm.value)
def test_too_big(self):
- from generalresearch.models.thl.user import User
val = 2**31
with pytest.raises(expected_exception=ValidationError) as cm:
@@ -69,7 +72,6 @@ class TestUserUserID:
assert "Input should be less than 2147483648" in str(cm.value)
def test_identifiable(self):
- from generalresearch.models.thl.user import User
val = randint(1, 2**30)
user = User(user_id=val)
@@ -80,7 +82,6 @@ class TestUserProductID:
user_id = randint(1, 2**30)
def test_valid(self):
- from generalresearch.models.thl.user import User
product_id = uuid4().hex
@@ -89,7 +90,6 @@ class TestUserProductID:
assert user.product_id == product_id
def test_type(self):
- from generalresearch.models.thl.user import User
with pytest.raises(expected_exception=ValueError) as cm:
User(user_id=self.user_id, product_id=0)
@@ -102,7 +102,6 @@ class TestUserProductID:
assert "Input should be a valid string" in str(cm.value)
def test_empty(self):
- from generalresearch.models.thl.user import User
with pytest.raises(expected_exception=ValueError) as cm:
User(user_id=self.user_id, product_id="")
@@ -110,7 +109,6 @@ class TestUserProductID:
assert "String should have at least 32 characters" in str(cm.value)
def test_invalid_len(self):
- from generalresearch.models.thl.user import User
# Valid uuid4s are 32 char long
product_id = uuid4().hex[:31]
@@ -133,7 +131,6 @@ class TestUserProductID:
assert "String should have at most 32 characters" in str(cm.value)
def test_invalid_uuid(self):
- from generalresearch.models.thl.user import User
# Modify the UUID to break it
product_id = uuid4().hex[:31] + "x"
@@ -144,7 +141,6 @@ class TestUserProductID:
assert "Invalid UUID" in str(cm.value)
def test_invalid_hex_form(self):
- from generalresearch.models.thl.user import User
# Sure not in hex form, but it'll get caught for being the
# wrong length before anything else
@@ -157,7 +153,6 @@ class TestUserProductID:
def test_identifiable(self):
"""Can't create a User with only a product_id because it also
needs to the product_user_id"""
- from generalresearch.models.thl.user import User
product_id = uuid4().hex
with pytest.raises(expected_exception=ValueError) as cm:
@@ -172,10 +167,9 @@ class TestUserProductUserID:
def randomword(self, length: int = 50):
# Raw so nothing is escaped to add additional backslashes
_bpuid_allowed = r"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&()*+,-.:;<=>?@[]^_{|}~"
- return "".join(rand_choice(_bpuid_allowed) for i in range(length))
+ return "".join(rand_choice(_bpuid_allowed) for _ in range(length))
def test_valid(self):
- from generalresearch.models.thl.user import User
product_user_id = uuid4().hex[:12]
user = User(user_id=self.user_id, product_user_id=product_user_id)
@@ -184,7 +178,6 @@ class TestUserProductUserID:
assert user.product_user_id == product_user_id
def test_type(self):
- from generalresearch.models.thl.user import User
with pytest.raises(expected_exception=ValueError) as cm:
User(user_id=self.user_id, product_user_id=0)
@@ -202,7 +195,6 @@ class TestUserProductUserID:
assert "Input should be a valid string" in str(cm.value)
def test_empty(self):
- from generalresearch.models.thl.user import User
with pytest.raises(expected_exception=ValueError) as cm:
User(user_id=self.user_id, product_user_id="")
@@ -210,7 +202,6 @@ class TestUserProductUserID:
assert "String should have at least 3 characters" in str(cm.value)
def test_invalid_len(self):
- from generalresearch.models.thl.user import User
product_user_id = self.randomword(251)
with pytest.raises(expected_exception=ValueError) as cm:
@@ -225,7 +216,6 @@ class TestUserProductUserID:
assert "String should have at least 3 characters" in str(cm.value)
def test_invalid_chars_space(self):
- from generalresearch.models.thl.user import User
product_user_id = f"{self.randomword(50)} {self.randomword(50)}"
with pytest.raises(expected_exception=ValueError) as cm:
@@ -234,7 +224,6 @@ class TestUserProductUserID:
assert "String cannot contain spaces" in str(cm.value)
def test_invalid_chars_slash(self):
- from generalresearch.models.thl.user import User
product_user_id = rf"{self.randomword(50)}\{self.randomword(50)}"
with pytest.raises(expected_exception=ValueError) as cm:
@@ -253,7 +242,6 @@ class TestUserProductUserID:
I wanted a test that made sure the regex was hit. I do not know
how we want to provide with the level of specific String checks
we do in here for specific error messages."""
- from generalresearch.models.thl.user import User
product_user_id = f"{self.randomword(50)}`{self.randomword(50)}"
with pytest.raises(expected_exception=ValueError) as cm:
@@ -275,7 +263,6 @@ class TestUserProductUserID:
def test_identifiable(self):
"""Can't create a User with only a product_user_id because it also
needs to the product_id"""
- from generalresearch.models.thl.user import User
product_user_id = uuid4().hex
with pytest.raises(ValueError) as cm:
@@ -288,7 +275,6 @@ class TestUserUUID:
user_id = randint(1, 2**30)
def test_valid(self):
- from generalresearch.models.thl.user import User
uuid_pk = uuid4().hex
@@ -297,7 +283,6 @@ class TestUserUUID:
assert user.uuid == uuid_pk
def test_type(self):
- from generalresearch.models.thl.user import User
with pytest.raises(ValueError) as cm:
User(user_id=self.user_id, uuid=0)
@@ -315,7 +300,6 @@ class TestUserUUID:
assert "Input should be a valid string" in str(cm.value)
def test_empty(self):
- from generalresearch.models.thl.user import User
with pytest.raises(ValueError) as cm:
User(user_id=self.user_id, uuid="")
@@ -323,7 +307,6 @@ class TestUserUUID:
assert "String should have at least 32 characters" in str(cm.value)
def test_invalid_len(self):
- from generalresearch.models.thl.user import User
# Valid uuid4s are 32 char long
uuid_pk = uuid4().hex[:31]
@@ -341,7 +324,6 @@ class TestUserUUID:
assert "String should have at most 32 characters" in str(cm.value)
def test_invalid_uuid(self):
- from generalresearch.models.thl.user import User
# Modify the UUID to break it
uuid_pk = uuid4().hex[:31] + "x"
@@ -352,7 +334,6 @@ class TestUserUUID:
assert "Invalid UUID" in str(cm.value)
def test_invalid_hex_form(self):
- from generalresearch.models.thl.user import User
# Sure not in hex form, but it'll get caught for being the
# wrong length before anything else
@@ -369,7 +350,6 @@ class TestUserUUID:
assert "Invalid UUID" in str(cm.value)
def test_identifiable(self):
- from generalresearch.models.thl.user import User
user_uuid = uuid4().hex
user = User(uuid=user_uuid)
@@ -380,7 +360,6 @@ class TestUserCreated:
user_id = randint(1, 2**30)
def test_valid(self):
- from generalresearch.models.thl.user import User
user = User(user_id=self.user_id)
dt = datetime.now(tz=UTC)
@@ -389,7 +368,6 @@ class TestUserCreated:
assert user.created == dt
def test_tz_naive_throws_init(self):
- from generalresearch.models.thl.user import User
with pytest.raises(ValueError) as cm:
User(user_id=self.user_id, created=datetime.now(tz=None)) # noqa
@@ -397,7 +375,6 @@ class TestUserCreated:
assert "Input should have timezone info" in str(cm.value)
def test_tz_naive_throws_setter(self):
- from generalresearch.models.thl.user import User
user = User(user_id=self.user_id)
with pytest.raises(ValueError) as cm:
@@ -406,7 +383,6 @@ class TestUserCreated:
assert "Input should have timezone info" in str(cm.value)
def test_tz_utc(self):
- from generalresearch.models.thl.user import User
with pytest.raises(ValueError) as cm:
User(
@@ -417,7 +393,6 @@ class TestUserCreated:
assert "Timezone is not UTC" in str(cm.value)
def test_not_in_future(self):
- from generalresearch.models.thl.user import User
the_future = datetime.now(tz=UTC) + timedelta(minutes=1)
with pytest.raises(ValueError) as cm:
@@ -426,7 +401,6 @@ class TestUserCreated:
assert "Input is in the future" in str(cm.value)
def test_after_anno_domini(self):
- from generalresearch.models.thl.user import User
before_ad = datetime(year=2015, month=1, day=1, tzinfo=UTC) + timedelta(
minutes=1
@@ -441,7 +415,6 @@ class TestUserLastSeen:
user_id = randint(1, 2**30)
def test_valid(self):
- from generalresearch.models.thl.user import User
user = User(user_id=self.user_id)
dt = datetime.now(tz=UTC)
@@ -450,7 +423,6 @@ class TestUserLastSeen:
assert user.last_seen == dt
def test_tz_naive_throws_init(self):
- from generalresearch.models.thl.user import User
with pytest.raises(ValueError) as cm:
User(user_id=self.user_id, last_seen=datetime.now(tz=None)) # noqa
@@ -458,7 +430,6 @@ class TestUserLastSeen:
assert "Input should have timezone info" in str(cm.value)
def test_tz_naive_throws_setter(self):
- from generalresearch.models.thl.user import User
user = User(user_id=self.user_id)
with pytest.raises(ValueError) as cm:
@@ -467,7 +438,6 @@ class TestUserLastSeen:
assert "Input should have timezone info" in str(cm.value)
def test_tz_utc(self):
- from generalresearch.models.thl.user import User
with pytest.raises(ValueError) as cm:
User(
@@ -478,7 +448,6 @@ class TestUserLastSeen:
assert "Timezone is not UTC" in str(cm.value)
def test_not_in_future(self):
- from generalresearch.models.thl.user import User
the_future = datetime.now(tz=UTC) + timedelta(minutes=1)
with pytest.raises(ValueError) as cm:
@@ -487,7 +456,6 @@ class TestUserLastSeen:
assert "Input is in the future" in str(cm.value)
def test_after_anno_domini(self):
- from generalresearch.models.thl.user import User
before_ad = datetime(year=2015, month=1, day=1, tzinfo=UTC) + timedelta(
minutes=1
@@ -502,7 +470,6 @@ class TestUserBlocked:
user_id = randint(1, 2**30)
def test_valid(self):
- from generalresearch.models.thl.user import User
user = User(user_id=self.user_id, blocked=True)
assert user.blocked
@@ -510,7 +477,6 @@ class TestUserBlocked:
def test_str_casting(self):
"""We don't want any of these to work, and that's why
we set strict=True on the column"""
- from generalresearch.models.thl.user import User
with pytest.raises(ValueError) as cm:
User(user_id=self.user_id, blocked="true")
@@ -547,7 +513,6 @@ class TestUserTiming:
user_id = randint(1, 2**30)
def test_valid(self):
- from generalresearch.models.thl.user import User
created = datetime.now(tz=UTC) - timedelta(minutes=60)
last_seen = datetime.now(tz=UTC) - timedelta(minutes=59)
@@ -557,7 +522,6 @@ class TestUserTiming:
assert user.last_seen == last_seen
def test_created_first(self):
- from generalresearch.models.thl.user import User
created = datetime.now(tz=UTC) - timedelta(minutes=60)
last_seen = datetime.now(tz=UTC) - timedelta(minutes=59)
@@ -572,7 +536,6 @@ class TestUserModelVerification:
"""Tests that may be dependent on more than 1 attribute"""
def test_identifiable(self):
- from generalresearch.models.thl.user import User
product_id = uuid4().hex
product_user_id = uuid4().hex
@@ -580,7 +543,6 @@ class TestUserModelVerification:
assert user.is_identifiable
def test_valid_helper(self):
- from generalresearch.models.thl.user import User
user_bool = User.is_valid_ubp(
product_id=uuid4().hex, product_user_id=uuid4().hex
@@ -594,7 +556,6 @@ class TestUserModelVerification:
class TestUserSerialization:
def test_basic_json(self):
- from generalresearch.models.thl.user import User
product_id = uuid4().hex
product_user_id = uuid4().hex
@@ -615,7 +576,6 @@ class TestUserSerialization:
assert d.get("created").endswith("Z")
def test_basic_dict(self):
- from generalresearch.models.thl.user import User
product_id = uuid4().hex
product_user_id = uuid4().hex
@@ -633,10 +593,11 @@ class TestUserSerialization:
assert not d.get("blocked")
assert d.get("product") is None
- assert d.get("created").tzinfo == UTC
+ created = d.get("created")
+ assert isinstance(created, datetime)
+ assert created.tzinfo == UTC
def test_from_json(self):
- from generalresearch.models.thl.user import User
product_id = uuid4().hex
product_user_id = uuid4().hex
@@ -651,12 +612,13 @@ class TestUserSerialization:
u = User.model_validate_json(user.to_json())
assert u.product_id == product_id
assert u.product is None
+ assert isinstance(u.created, datetime)
assert u.created.tzinfo == UTC
class TestUserMethods:
- def test_audit_log(self, user, audit_log_manager):
+ def test_audit_log(self, user: User, audit_log_manager: AuditLogManager):
assert user.audit_log is None
user.prefetch_audit_log(audit_log_manager=audit_log_manager)
assert user.audit_log == []
@@ -668,21 +630,21 @@ class TestUserMethods:
def test_transactions(
self,
user_factory: Callable[..., User],
- thl_lm,
+ thl_ledger_manager: ThlLedgerManager,
session_with_tx_factory: Callable[..., None],
- product_user_wallet_yes,
+ product_user_wallet_yes: Product,
):
u1 = user_factory(product=product_user_wallet_yes)
assert u1.transactions is None
- u1.prefetch_transactions(thl_lm=thl_lm)
+ u1.prefetch_transactions(thl_lm=thl_ledger_manager)
assert u1.transactions == []
session_with_tx_factory(user=u1)
- u1.prefetch_transactions(thl_lm=thl_lm)
+ u1.prefetch_transactions(thl_lm=thl_ledger_manager)
assert len(u1.transactions) == 1
@pytest.mark.skip(reason="TODO")
- def test_location_history(self, user):
+ def test_location_history(self, user: User):
assert user.location_history is None