Write a set of queries to go along with the event sourcing model
This commit is contained in:
0
tests/queries/__init__.py
Normal file
0
tests/queries/__init__.py
Normal file
0
tests/queries/test_add_product.py
Normal file
0
tests/queries/test_add_product.py
Normal file
0
tests/queries/test_add_user.py
Normal file
0
tests/queries/test_add_user.py
Normal file
84
tests/queries/test_adjust_interest.py
Normal file
84
tests/queries/test_adjust_interest.py
Normal file
@@ -0,0 +1,84 @@
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from dibbler.models import Transaction, User
|
||||
from dibbler.queries import adjust_interest, current_interest
|
||||
|
||||
|
||||
def insert_test_data(sql_session: Session) -> User:
|
||||
user = User("Test User")
|
||||
sql_session.add(user)
|
||||
sql_session.commit()
|
||||
|
||||
return user
|
||||
|
||||
|
||||
def test_adjust_interest_unitialized_user(sql_session: Session) -> None:
|
||||
user = User("Uninitialized User")
|
||||
|
||||
with pytest.raises(ValueError, match="User must be persisted in the database."):
|
||||
adjust_interest(
|
||||
sql_session,
|
||||
user=user,
|
||||
new_interest=4,
|
||||
message="Attempting to adjust interest for uninitialized user",
|
||||
)
|
||||
|
||||
|
||||
def test_adjust_interest_no_history(sql_session: Session) -> None:
|
||||
user = insert_test_data(sql_session)
|
||||
|
||||
adjust_interest(
|
||||
sql_session,
|
||||
user=user,
|
||||
new_interest=3,
|
||||
message="Setting initial interest rate",
|
||||
)
|
||||
sql_session.commit()
|
||||
|
||||
current_interest_rate = current_interest(sql_session)
|
||||
|
||||
assert current_interest_rate == 3
|
||||
|
||||
|
||||
def test_adjust_interest_existing_history(sql_session: Session) -> None:
|
||||
user = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 10, 1, 9, 0, 0),
|
||||
user_id=user.id,
|
||||
interest_rate_percent=5,
|
||||
message="Initial interest rate",
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
current_interest_rate = current_interest(sql_session)
|
||||
assert current_interest_rate == 5
|
||||
|
||||
adjust_interest(
|
||||
sql_session,
|
||||
user=user,
|
||||
new_interest=2,
|
||||
message="Adjusting interest rate",
|
||||
)
|
||||
sql_session.commit()
|
||||
|
||||
current_interest_rate = current_interest(sql_session)
|
||||
assert current_interest_rate == 2
|
||||
|
||||
|
||||
def test_adjust_interest_negative_failure(sql_session: Session) -> None:
|
||||
user = insert_test_data(sql_session)
|
||||
|
||||
with pytest.raises(ValueError, match="Interest rate cannot be negative"):
|
||||
adjust_interest(
|
||||
sql_session,
|
||||
user=user,
|
||||
new_interest=-1,
|
||||
message="Attempting to set negative interest rate",
|
||||
)
|
||||
166
tests/queries/test_adjust_penalty.py
Normal file
166
tests/queries/test_adjust_penalty.py
Normal file
@@ -0,0 +1,166 @@
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from dibbler.models import Transaction, User
|
||||
from dibbler.models.Transaction import (
|
||||
DEFAULT_PENALTY_MULTIPLIER_PERCENTAGE,
|
||||
DEFAULT_PENALTY_THRESHOLD,
|
||||
)
|
||||
from dibbler.queries import adjust_penalty, current_penalty
|
||||
|
||||
|
||||
def insert_test_data(sql_session: Session) -> User:
|
||||
user = User("Test User")
|
||||
sql_session.add(user)
|
||||
sql_session.commit()
|
||||
|
||||
return user
|
||||
|
||||
|
||||
def test_adjust_penalty_unitialized_user(sql_session: Session) -> None:
|
||||
user = User("Uninitialized User")
|
||||
|
||||
with pytest.raises(ValueError, match="User must be persisted in the database."):
|
||||
adjust_penalty(
|
||||
sql_session,
|
||||
user=user,
|
||||
new_penalty=-100,
|
||||
new_penalty_multiplier=110,
|
||||
message="Attempting to adjust penalty for uninitialized user",
|
||||
)
|
||||
|
||||
|
||||
def test_adjust_penalty_no_history(sql_session: Session) -> None:
|
||||
user = insert_test_data(sql_session)
|
||||
|
||||
adjust_penalty(
|
||||
sql_session,
|
||||
user=user,
|
||||
new_penalty=-200,
|
||||
message="Setting initial interest rate",
|
||||
)
|
||||
sql_session.commit()
|
||||
|
||||
(penalty, multiplier) = current_penalty(sql_session)
|
||||
|
||||
assert penalty == -200
|
||||
assert multiplier == DEFAULT_PENALTY_MULTIPLIER_PERCENTAGE
|
||||
|
||||
|
||||
def test_adjust_penalty_multiplier_no_history(sql_session: Session) -> None:
|
||||
user = insert_test_data(sql_session)
|
||||
|
||||
adjust_penalty(
|
||||
sql_session,
|
||||
user=user,
|
||||
new_penalty_multiplier=125,
|
||||
message="Setting initial interest rate",
|
||||
)
|
||||
sql_session.commit()
|
||||
|
||||
(penalty, multiplier) = current_penalty(sql_session)
|
||||
|
||||
assert penalty == DEFAULT_PENALTY_THRESHOLD
|
||||
assert multiplier == 125
|
||||
|
||||
|
||||
def test_adjust_penalty_multiplier_less_than_100_fail(sql_session: Session) -> None:
|
||||
user = insert_test_data(sql_session)
|
||||
|
||||
adjust_penalty(
|
||||
sql_session,
|
||||
user=user,
|
||||
new_penalty_multiplier=100,
|
||||
message="Setting initial interest rate",
|
||||
)
|
||||
sql_session.commit()
|
||||
|
||||
(_, multiplier) = current_penalty(sql_session)
|
||||
|
||||
assert multiplier == 100
|
||||
|
||||
with pytest.raises(ValueError, match="Penalty multiplier cannot be less than 100%"):
|
||||
adjust_penalty(
|
||||
sql_session,
|
||||
user=user,
|
||||
new_penalty_multiplier=99,
|
||||
message="Setting initial interest rate",
|
||||
)
|
||||
|
||||
|
||||
def test_adjust_penalty_existing_history(sql_session: Session) -> None:
|
||||
user = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.adjust_penalty(
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
penalty_threshold=-150,
|
||||
penalty_multiplier_percent=110,
|
||||
message="Initial penalty settings",
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
(penalty, _) = current_penalty(sql_session)
|
||||
assert penalty == -150
|
||||
|
||||
adjust_penalty(
|
||||
sql_session,
|
||||
user=user,
|
||||
new_penalty=-250,
|
||||
message="Adjusting penalty threshold",
|
||||
)
|
||||
sql_session.commit()
|
||||
|
||||
(penalty, _) = current_penalty(sql_session)
|
||||
assert penalty == -250
|
||||
|
||||
|
||||
def test_adjust_penalty_multiplier_existing_history(sql_session: Session) -> None:
|
||||
user = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.adjust_penalty(
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
penalty_threshold=-150,
|
||||
penalty_multiplier_percent=110,
|
||||
message="Initial penalty settings",
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
(_, multiplier) = current_penalty(sql_session)
|
||||
assert multiplier == 110
|
||||
|
||||
adjust_penalty(
|
||||
sql_session,
|
||||
user=user,
|
||||
new_penalty_multiplier=130,
|
||||
message="Adjusting penalty multiplier",
|
||||
)
|
||||
sql_session.commit()
|
||||
(_, multiplier) = current_penalty(sql_session)
|
||||
assert multiplier == 130
|
||||
|
||||
|
||||
def test_adjust_penalty_and_multiplier(sql_session: Session) -> None:
|
||||
user = insert_test_data(sql_session)
|
||||
|
||||
adjust_penalty(
|
||||
sql_session,
|
||||
user=user,
|
||||
new_penalty=-300,
|
||||
new_penalty_multiplier=150,
|
||||
message="Setting both penalty and multiplier",
|
||||
)
|
||||
sql_session.commit()
|
||||
|
||||
(penalty, multiplier) = current_penalty(sql_session)
|
||||
assert penalty == -300
|
||||
assert multiplier == 150
|
||||
35
tests/queries/test_current_interest.py
Normal file
35
tests/queries/test_current_interest.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from dibbler.models.Transaction import DEFAULT_INTEREST_RATE_PERCENTAGE
|
||||
from dibbler.models import Transaction, User
|
||||
from dibbler.queries import current_interest
|
||||
|
||||
|
||||
def test_current_interest_no_history(sql_session: Session) -> None:
|
||||
assert current_interest(sql_session) == DEFAULT_INTEREST_RATE_PERCENTAGE
|
||||
|
||||
|
||||
def test_current_interest_with_history(sql_session: Session) -> None:
|
||||
user = User("Admin User")
|
||||
sql_session.add(user)
|
||||
sql_session.commit()
|
||||
|
||||
transactions = [
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
interest_rate_percent=5,
|
||||
user_id=user.id,
|
||||
),
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 11, 1, 10, 0, 0),
|
||||
interest_rate_percent=7,
|
||||
user_id=user.id,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
assert current_interest(sql_session) == 7
|
||||
42
tests/queries/test_current_penalty.py
Normal file
42
tests/queries/test_current_penalty.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from dibbler.models import Transaction, User
|
||||
from dibbler.models.Transaction import (
|
||||
DEFAULT_PENALTY_MULTIPLIER_PERCENTAGE,
|
||||
DEFAULT_PENALTY_THRESHOLD,
|
||||
)
|
||||
from dibbler.queries import current_penalty
|
||||
|
||||
|
||||
def test_current_penalty_no_history(sql_session: Session) -> None:
|
||||
assert current_penalty(sql_session) == (
|
||||
DEFAULT_PENALTY_THRESHOLD,
|
||||
DEFAULT_PENALTY_MULTIPLIER_PERCENTAGE,
|
||||
)
|
||||
|
||||
|
||||
def test_current_penalty_with_history(sql_session: Session) -> None:
|
||||
user = User("Admin User")
|
||||
sql_session.add(user)
|
||||
sql_session.commit()
|
||||
|
||||
transactions = [
|
||||
Transaction.adjust_penalty(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
penalty_threshold=-200,
|
||||
penalty_multiplier_percent=150,
|
||||
user_id=user.id,
|
||||
),
|
||||
Transaction.adjust_penalty(
|
||||
time=datetime(2023, 10, 2, 10, 0, 0),
|
||||
penalty_threshold=-300,
|
||||
penalty_multiplier_percent=200,
|
||||
user_id=user.id,
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
assert current_penalty(sql_session) == (-300, 200)
|
||||
198
tests/queries/test_joint_buy_product.py
Normal file
198
tests/queries/test_joint_buy_product.py
Normal file
@@ -0,0 +1,198 @@
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from dibbler.models import Product, Transaction, User
|
||||
from dibbler.queries import joint_buy_product
|
||||
|
||||
|
||||
def insert_test_data(sql_session: Session) -> tuple[User, User, User, Product]:
|
||||
user1 = User("Test User 1")
|
||||
user2 = User("Test User 2")
|
||||
user3 = User("Test User 3")
|
||||
product = Product("1234567890123", "Test Product")
|
||||
|
||||
sql_session.add_all([user1, user2, user3, product])
|
||||
sql_session.commit()
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
user_id=user1.id,
|
||||
product_id=product.id,
|
||||
amount=30,
|
||||
per_product=10,
|
||||
product_count=3,
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
)
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
return user1, user2, user3, product
|
||||
|
||||
|
||||
def test_joint_buy_product_uninitialized_product(sql_session: Session) -> None:
|
||||
user = User("Test User 1")
|
||||
sql_session.add(user)
|
||||
sql_session.commit()
|
||||
|
||||
product = Product("1234567890123", "Uninitialized Product")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user],
|
||||
product=product,
|
||||
product_count=1,
|
||||
)
|
||||
|
||||
|
||||
def test_joint_buy_product_no_users(sql_session: Session) -> None:
|
||||
user, _, _, product = insert_test_data(sql_session)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[],
|
||||
product=product,
|
||||
product_count=1,
|
||||
)
|
||||
|
||||
|
||||
def test_joint_buy_product_uninitialized_instigator(sql_session: Session) -> None:
|
||||
user, user2, _, product = insert_test_data(sql_session)
|
||||
|
||||
uninitialized_user = User("Uninitialized User")
|
||||
with pytest.raises(ValueError):
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=uninitialized_user,
|
||||
users=[user, user2],
|
||||
product=product,
|
||||
product_count=1,
|
||||
)
|
||||
|
||||
|
||||
def test_joint_buy_product_uninitialized_user_in_list(sql_session: Session) -> None:
|
||||
user, _, _, product = insert_test_data(sql_session)
|
||||
|
||||
uninitialized_user = User("Uninitialized User")
|
||||
with pytest.raises(ValueError):
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user, uninitialized_user],
|
||||
product=product,
|
||||
product_count=1,
|
||||
)
|
||||
|
||||
|
||||
def test_joint_buy_product_invalid_product_count(sql_session: Session) -> None:
|
||||
user, _, _, product = insert_test_data(sql_session)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user],
|
||||
product=product,
|
||||
product_count=0,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user],
|
||||
product=product,
|
||||
product_count=-1,
|
||||
)
|
||||
|
||||
|
||||
def test_joint_single_user(sql_session: Session) -> None:
|
||||
user, _, _, product = insert_test_data(sql_session)
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user],
|
||||
product=product,
|
||||
product_count=1,
|
||||
)
|
||||
|
||||
|
||||
def test_joint_buy_product(sql_session: Session) -> None:
|
||||
user, user2, user3, product = insert_test_data(sql_session)
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user, user2, user3],
|
||||
product=product,
|
||||
product_count=1,
|
||||
)
|
||||
|
||||
|
||||
def test_joint_buy_product_more_than_in_stock(sql_session: Session) -> None:
|
||||
user, user2, user3, product = insert_test_data(sql_session)
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user, user2, user3],
|
||||
product=product,
|
||||
product_count=5,
|
||||
)
|
||||
|
||||
|
||||
def test_joint_buy_product_out_of_stock(sql_session: Session) -> None:
|
||||
user, user2, user3, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.buy_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=3,
|
||||
time=datetime(2024, 1, 2, 10, 0, 0),
|
||||
)
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user, user2, user3],
|
||||
product=product,
|
||||
product_count=10,
|
||||
)
|
||||
|
||||
|
||||
def test_joint_buy_product_duplicate_user(sql_session: Session) -> None:
|
||||
user, user2, _, product = insert_test_data(sql_session)
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user, user, user2],
|
||||
product=product,
|
||||
product_count=1,
|
||||
)
|
||||
|
||||
|
||||
def test_joint_buy_product_non_involved_instigator(sql_session: Session) -> None:
|
||||
user, user2, user3, product = insert_test_data(sql_session)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user2, user3],
|
||||
product=product,
|
||||
product_count=1,
|
||||
)
|
||||
311
tests/queries/test_product_owners.py
Normal file
311
tests/queries/test_product_owners.py
Normal file
@@ -0,0 +1,311 @@
|
||||
from datetime import datetime
|
||||
from pprint import pprint
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from dibbler.models import Product, User
|
||||
from dibbler.models.Transaction import Transaction
|
||||
from dibbler.queries import product_owners, product_owners_log, product_stock
|
||||
|
||||
|
||||
def insert_test_data(sql_session: Session) -> tuple[Product, User]:
|
||||
user = User("testuser")
|
||||
product = Product("1234567890123", "Test Product")
|
||||
|
||||
sql_session.add(user)
|
||||
sql_session.add(product)
|
||||
|
||||
sql_session.commit()
|
||||
|
||||
return product, user
|
||||
|
||||
|
||||
def test_product_owners_unitilialized_product(sql_session: Session) -> None:
|
||||
user = User("testuser")
|
||||
sql_session.add(user)
|
||||
sql_session.commit()
|
||||
|
||||
product = Product("1234567890123", "Uninitialized Product")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
product_owners(sql_session, product)
|
||||
|
||||
|
||||
def test_product_owners_no_transactions(sql_session: Session) -> None:
|
||||
product, _ = insert_test_data(sql_session)
|
||||
|
||||
pprint(product_owners_log(sql_session, product))
|
||||
|
||||
owners = product_owners(sql_session, product)
|
||||
assert owners == []
|
||||
|
||||
|
||||
def test_product_owners_add_products(sql_session: Session) -> None:
|
||||
product, user = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=30,
|
||||
per_product=10,
|
||||
product_count=3,
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
)
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_owners_log(sql_session, product))
|
||||
|
||||
owners = product_owners(sql_session, product)
|
||||
assert owners == [user, user, user]
|
||||
|
||||
|
||||
def test_product_owners_add_and_buy_products(sql_session: Session) -> None:
|
||||
product, user = insert_test_data(sql_session)
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=30,
|
||||
per_product=10,
|
||||
product_count=3,
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
),
|
||||
Transaction.buy_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
time=datetime(2024, 1, 2, 10, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_owners_log(sql_session, product))
|
||||
|
||||
owners = product_owners(sql_session, product)
|
||||
assert owners == [user, user]
|
||||
|
||||
|
||||
def test_product_owners_add_and_throw_products(sql_session: Session) -> None:
|
||||
product, user = insert_test_data(sql_session)
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=40,
|
||||
per_product=10,
|
||||
product_count=4,
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
),
|
||||
Transaction.throw_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=2,
|
||||
time=datetime(2024, 1, 2, 10, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_owners_log(sql_session, product))
|
||||
|
||||
owners = product_owners(sql_session, product)
|
||||
assert owners == [user, user]
|
||||
|
||||
|
||||
def test_product_owners_multiple_users(sql_session: Session) -> None:
|
||||
product, user1 = insert_test_data(sql_session)
|
||||
user2 = User("testuser2")
|
||||
sql_session.add(user2)
|
||||
sql_session.commit()
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
user_id=user1.id,
|
||||
product_id=product.id,
|
||||
amount=20,
|
||||
per_product=10,
|
||||
product_count=2,
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
),
|
||||
Transaction.add_product(
|
||||
user_id=user2.id,
|
||||
product_id=product.id,
|
||||
amount=30,
|
||||
per_product=10,
|
||||
product_count=3,
|
||||
time=datetime(2024, 1, 2, 10, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_owners_log(sql_session, product))
|
||||
|
||||
owners = product_owners(sql_session, product)
|
||||
assert owners == [user2, user2, user2, user1, user1]
|
||||
|
||||
|
||||
def test_product_owners_adjust_stock_down(sql_session: Session) -> None:
|
||||
product, user = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=50,
|
||||
per_product=10,
|
||||
product_count=5,
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
),
|
||||
Transaction.adjust_stock(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=-2,
|
||||
time=datetime(2024, 1, 2, 10, 0, 0),
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_owners_log(sql_session, product))
|
||||
|
||||
assert product_stock(sql_session, product) == 3
|
||||
|
||||
owners = product_owners(sql_session, product)
|
||||
assert owners == [user, user, user]
|
||||
|
||||
|
||||
def test_product_owners_adjust_stock_up(sql_session: Session) -> None:
|
||||
product, user = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=20,
|
||||
per_product=10,
|
||||
product_count=2,
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
),
|
||||
Transaction.adjust_stock(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=3,
|
||||
time=datetime(2024, 1, 2, 10, 0, 0),
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_owners_log(sql_session, product))
|
||||
|
||||
owners = product_owners(sql_session, product)
|
||||
assert owners == [user, user, None, None, None]
|
||||
|
||||
|
||||
def test_product_owners_negative_stock(sql_session: Session) -> None:
|
||||
product, user = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=10,
|
||||
per_product=10,
|
||||
product_count=1,
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
),
|
||||
Transaction.buy_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=2,
|
||||
time=datetime(2024, 1, 2, 10, 0, 0),
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
owners = product_owners(sql_session, product)
|
||||
assert owners == []
|
||||
|
||||
|
||||
def test_product_owners_add_products_from_negative_stock(sql_session: Session) -> None:
|
||||
product, user = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.buy_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=2,
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
),
|
||||
Transaction.add_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=30,
|
||||
per_product=10,
|
||||
product_count=3,
|
||||
time=datetime(2024, 1, 2, 10, 0, 0),
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_owners_log(sql_session, product))
|
||||
|
||||
owners = product_owners(sql_session, product)
|
||||
assert owners == [user]
|
||||
|
||||
|
||||
def test_product_owners_interleaved_users(sql_session: Session) -> None:
|
||||
product, user1 = insert_test_data(sql_session)
|
||||
user2 = User("testuser2")
|
||||
sql_session.add(user2)
|
||||
sql_session.commit()
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
user_id=user1.id,
|
||||
product_id=product.id,
|
||||
amount=20,
|
||||
per_product=10,
|
||||
product_count=2,
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
),
|
||||
Transaction.add_product(
|
||||
user_id=user2.id,
|
||||
product_id=product.id,
|
||||
amount=30,
|
||||
per_product=10,
|
||||
product_count=3,
|
||||
time=datetime(2024, 1, 2, 10, 0, 0),
|
||||
),
|
||||
Transaction.buy_product(
|
||||
user_id=user1.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
time=datetime(2024, 1, 3, 10, 0, 0),
|
||||
),
|
||||
Transaction.add_product(
|
||||
user_id=user1.id,
|
||||
product_id=product.id,
|
||||
amount=10,
|
||||
per_product=10,
|
||||
product_count=1,
|
||||
time=datetime(2024, 1, 4, 10, 0, 0),
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_owners_log(sql_session, product))
|
||||
|
||||
owners = product_owners(sql_session, product)
|
||||
assert owners == [user1, user2, user2, user2, user1]
|
||||
432
tests/queries/test_product_price.py
Normal file
432
tests/queries/test_product_price.py
Normal file
@@ -0,0 +1,432 @@
|
||||
import math
|
||||
from datetime import datetime
|
||||
from pprint import pprint
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from dibbler.models import Product, Transaction, User
|
||||
from dibbler.queries import product_price, product_price_log, joint_buy_product
|
||||
|
||||
# TODO: see if we can use pytest_runtest_makereport to print the "product_price_log"s
|
||||
# only on failures instead of inlining it in every test function
|
||||
|
||||
|
||||
def insert_test_data(sql_session: Session) -> tuple[User, Product]:
|
||||
user = User("Test User")
|
||||
product = Product("1234567890123", "Test Product")
|
||||
|
||||
sql_session.add(user)
|
||||
sql_session.add(product)
|
||||
sql_session.commit()
|
||||
|
||||
return user, product
|
||||
|
||||
|
||||
def test_product_price_uninitialized_product(sql_session: Session) -> None:
|
||||
user = User("Test User")
|
||||
sql_session.add(user)
|
||||
sql_session.commit()
|
||||
|
||||
product = Product("1234567890123", "Uninitialized Product")
|
||||
|
||||
with pytest.raises(ValueError, match="Product must be persisted in the database."):
|
||||
product_price(sql_session, product)
|
||||
|
||||
|
||||
def test_product_price_no_transactions(sql_session: Session) -> None:
|
||||
_, product = insert_test_data(sql_session)
|
||||
|
||||
pprint(product_price_log(sql_session, product))
|
||||
|
||||
assert product_price(sql_session, product) == 0
|
||||
|
||||
|
||||
def test_product_price_basic_history(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
amount=27 * 2 - 1,
|
||||
per_product=27,
|
||||
product_count=2,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_price_log(sql_session, product))
|
||||
|
||||
assert product_price(sql_session, product) == 27
|
||||
|
||||
|
||||
def test_product_price_sold_out(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
amount=27 * 2 - 1,
|
||||
per_product=27,
|
||||
product_count=2,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 1),
|
||||
product_count=2,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_price_log(sql_session, product))
|
||||
|
||||
assert product_price(sql_session, product) == 27
|
||||
|
||||
|
||||
def test_product_price_interest(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
interest_rate_percent=110,
|
||||
user_id=user.id,
|
||||
),
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 1),
|
||||
amount=27 * 2 - 1,
|
||||
per_product=27,
|
||||
product_count=2,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_price_log(sql_session, product))
|
||||
|
||||
product_price_ = product_price(sql_session, product)
|
||||
product_price_interest = product_price(sql_session, product, include_interest=True)
|
||||
|
||||
assert product_price_ == 27
|
||||
assert product_price_interest == math.ceil(27 * 1.1)
|
||||
|
||||
|
||||
def test_product_price_changing_interest(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
interest_rate_percent=110,
|
||||
user_id=user.id,
|
||||
),
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 1),
|
||||
amount=27 * 2 - 1,
|
||||
per_product=27,
|
||||
product_count=2,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 10, 1, 12, 0, 2),
|
||||
interest_rate_percent=120,
|
||||
user_id=user.id,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_price_log(sql_session, product))
|
||||
|
||||
product_price_interest = product_price(sql_session, product, include_interest=True)
|
||||
assert product_price_interest == math.ceil(27 * 1.2)
|
||||
|
||||
|
||||
def test_product_price_old_transaction(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 1),
|
||||
amount=27 * 2,
|
||||
per_product=27,
|
||||
product_count=2,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
# Price should be 27
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 2),
|
||||
amount=38 * 3,
|
||||
per_product=38,
|
||||
product_count=3,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
# price should be averaged upwards
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
until_transaction = transactions[0]
|
||||
|
||||
pprint(
|
||||
product_price_log(
|
||||
sql_session,
|
||||
product,
|
||||
until=until_transaction,
|
||||
)
|
||||
)
|
||||
|
||||
product_price_ = product_price(
|
||||
sql_session,
|
||||
product,
|
||||
until=until_transaction,
|
||||
)
|
||||
assert product_price_ == 27
|
||||
|
||||
|
||||
# Price goes up and gets rounded up to the next integer
|
||||
def test_product_price_round_up_from_below(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 1),
|
||||
amount=27 * 2,
|
||||
per_product=27,
|
||||
product_count=2,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
# Price should be 27
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 2),
|
||||
amount=38 * 3,
|
||||
per_product=38,
|
||||
product_count=3,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
# price should be averaged upwards
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_price_log(sql_session, product))
|
||||
|
||||
product_price_ = product_price(sql_session, product)
|
||||
assert product_price_ == math.ceil((27 * 2 + 38 * 3) / (2 + 3))
|
||||
|
||||
|
||||
# Price goes down and gets rounded up to the next integer
|
||||
def test_product_price_round_up_from_above(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 1),
|
||||
amount=27 * 2,
|
||||
per_product=27,
|
||||
product_count=2,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
# Price should be 27
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 2),
|
||||
amount=20 * 3,
|
||||
per_product=20,
|
||||
product_count=3,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
# price should be averaged downwards
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_price_log(sql_session, product))
|
||||
|
||||
product_price_ = product_price(sql_session, product)
|
||||
assert product_price_ == math.ceil((27 * 2 + 20 * 3) / (2 + 3))
|
||||
|
||||
|
||||
def test_product_price_with_negative_stock_single_addition(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 13, 0, 0),
|
||||
amount=1,
|
||||
per_product=10,
|
||||
product_count=1,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 13, 0, 1),
|
||||
product_count=10,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 13, 0, 2),
|
||||
amount=22,
|
||||
per_product=22,
|
||||
product_count=1,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_price_log(sql_session, product))
|
||||
|
||||
# Stock went subzero, price should be the last added product price
|
||||
product1_price = product_price(sql_session, product)
|
||||
assert product1_price == 22
|
||||
|
||||
|
||||
def test_product_price_with_negative_stock_multiple_additions(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 13, 0, 0),
|
||||
amount=1,
|
||||
per_product=10,
|
||||
product_count=1,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 13, 0, 1),
|
||||
product_count=10,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 13, 0, 2),
|
||||
amount=22,
|
||||
per_product=22,
|
||||
product_count=1,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 13, 0, 3),
|
||||
amount=29,
|
||||
per_product=29,
|
||||
product_count=2,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_price_log(sql_session, product))
|
||||
|
||||
# Stock went subzero, price should be the last added product price
|
||||
product1_price = product_price(sql_session, product)
|
||||
assert product1_price == math.ceil(29)
|
||||
|
||||
|
||||
def test_product_price_joint_transactions(sql_session: Session) -> None:
|
||||
user1, product = insert_test_data(sql_session)
|
||||
user2 = User("Test User 2")
|
||||
sql_session.add(user2)
|
||||
sql_session.commit()
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
amount=30 * 3,
|
||||
per_product=30,
|
||||
product_count=3,
|
||||
user_id=user1.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 1),
|
||||
amount=20 * 2,
|
||||
per_product=20,
|
||||
product_count=2,
|
||||
user_id=user2.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
product_price_ = product_price(sql_session, product)
|
||||
assert product_price_ == math.ceil((30 * 3 + 20 * 2) / (3 + 2))
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
time=datetime(2023, 10, 1, 12, 0, 2),
|
||||
instigator=user1,
|
||||
users=[user1, user2],
|
||||
product=product,
|
||||
product_count=2,
|
||||
)
|
||||
|
||||
pprint(product_price_log(sql_session, product))
|
||||
|
||||
old_product_price = product_price_
|
||||
product_price_ = product_price(sql_session, product)
|
||||
assert product_price_ == old_product_price, (
|
||||
"Joint buy transactions should not affect product price"
|
||||
)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 3),
|
||||
amount=25 * 4,
|
||||
per_product=25,
|
||||
product_count=4,
|
||||
user_id=user1.id,
|
||||
product_id=product.id,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(product_price_log(sql_session, product))
|
||||
product_price_ = product_price(sql_session, product)
|
||||
|
||||
# Expected state:
|
||||
# Added products:
|
||||
# Count: 3 + 2 = 5, Price: (30 * 3 + 20 * 2) / 5 = 26
|
||||
# Joint bought products:
|
||||
# Count: 5 - 2 = 3, Price: n/a (should not affect price)
|
||||
# Added products:
|
||||
# Count: 3 + 4 = 7, Price: (26 * 3 + 25 * 4) / (3 + 4) = 25.57 -> 26
|
||||
|
||||
assert product_price_ == math.ceil((26 * 3 + 25 * 4) / (3 + 4))
|
||||
|
||||
|
||||
def test_product_price_until(sql_session: Session) -> None: ...
|
||||
250
tests/queries/test_product_stock.py
Normal file
250
tests/queries/test_product_stock.py
Normal file
@@ -0,0 +1,250 @@
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from dibbler.models import Product, Transaction, User
|
||||
from dibbler.queries import joint_buy_product, product_stock
|
||||
|
||||
|
||||
def insert_test_data(sql_session: Session) -> tuple[User, Product]:
|
||||
user = User("Test User 1")
|
||||
product = Product("1234567890123", "Test Product")
|
||||
sql_session.add(user)
|
||||
sql_session.add(product)
|
||||
sql_session.commit()
|
||||
return user, product
|
||||
|
||||
|
||||
def test_product_stock_uninitialized_product(sql_session: Session) -> None:
|
||||
user = User("Test User 1")
|
||||
sql_session.add(user)
|
||||
sql_session.commit()
|
||||
|
||||
product = Product("1234567890123", "Uninitialized Product")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
product_stock(sql_session, product)
|
||||
|
||||
|
||||
def test_product_stock_basic_history(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
sql_session.commit()
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
amount=10,
|
||||
per_product=10,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
assert product_stock(sql_session, product) == 1
|
||||
|
||||
|
||||
def test_product_stock_adjust_stock_up(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=50,
|
||||
per_product=10,
|
||||
product_count=5,
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
),
|
||||
Transaction.adjust_stock(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=2,
|
||||
time=datetime(2024, 1, 2, 10, 0, 0),
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
assert product_stock(sql_session, product) == 5 + 2
|
||||
|
||||
|
||||
def test_product_stock_adjust_stock_down(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=50,
|
||||
per_product=10,
|
||||
product_count=5,
|
||||
time=datetime(2024, 1, 1, 10, 0, 0),
|
||||
),
|
||||
Transaction.adjust_stock(
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=-2,
|
||||
time=datetime(2024, 1, 2, 10, 0, 0),
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
assert product_stock(sql_session, product) == 5 - 2
|
||||
|
||||
|
||||
def test_product_stock_complex_history(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 13, 0, 0),
|
||||
amount=27 * 2,
|
||||
per_product=27,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=2,
|
||||
),
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 13, 0, 1),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=3,
|
||||
),
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 13, 0, 2),
|
||||
amount=50 * 4,
|
||||
per_product=50,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=4,
|
||||
),
|
||||
Transaction.adjust_stock(
|
||||
time=datetime(2023, 10, 1, 15, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=3,
|
||||
),
|
||||
Transaction.adjust_stock(
|
||||
time=datetime(2023, 10, 1, 15, 0, 1),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=-2,
|
||||
),
|
||||
Transaction.throw_product(
|
||||
time=datetime(2023, 10, 1, 15, 0, 2),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
assert product_stock(sql_session, product) == 2 - 3 + 4 + 3 - 2 - 1
|
||||
|
||||
|
||||
def test_product_stock_no_transactions(sql_session: Session) -> None:
|
||||
_, product = insert_test_data(sql_session)
|
||||
|
||||
assert product_stock(sql_session, product) == 0
|
||||
|
||||
|
||||
def test_negative_product_stock(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 14, 0, 0),
|
||||
amount=50,
|
||||
per_product=50,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
),
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 14, 0, 1),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=2,
|
||||
),
|
||||
Transaction.adjust_stock(
|
||||
time=datetime(2023, 10, 1, 16, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=-1,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
# The stock should be negative because we added and bought the product
|
||||
assert product_stock(sql_session, product) == 1 - 2 - 1
|
||||
|
||||
|
||||
def test_product_stock_joint_transaction(sql_session: Session) -> None:
|
||||
user1, product = insert_test_data(sql_session)
|
||||
|
||||
user2 = User("Test User 2")
|
||||
sql_session.add(user2)
|
||||
sql_session.commit()
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 17, 0, 0),
|
||||
amount=100,
|
||||
per_product=100,
|
||||
user_id=user1.id,
|
||||
product_id=product.id,
|
||||
product_count=5,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
time=datetime(2023, 10, 1, 17, 0, 1),
|
||||
instigator=user1,
|
||||
users=[user1, user2],
|
||||
product=product,
|
||||
product_count=3,
|
||||
)
|
||||
|
||||
assert product_stock(sql_session, product) == 5 - 3
|
||||
|
||||
|
||||
def test_product_stock_until(sql_session: Session) -> None:
|
||||
user, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
amount=10,
|
||||
per_product=10,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
),
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 2, 12, 0, 0),
|
||||
amount=20,
|
||||
per_product=10,
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=2,
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
assert product_stock(sql_session, product, until=datetime(2023, 10, 1, 23, 59, 59)) == 1
|
||||
88
tests/queries/test_search_product.py
Normal file
88
tests/queries/test_search_product.py
Normal file
@@ -0,0 +1,88 @@
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from dibbler.models import Product
|
||||
from dibbler.queries import search_product
|
||||
|
||||
|
||||
def insert_test_data(sql_session: Session) -> list[Product]:
|
||||
products = [
|
||||
Product("1234567890123", "Test Product A"),
|
||||
Product("2345678901234", "Test Product B"),
|
||||
Product("3456789012345", "Another Product"),
|
||||
Product("4567890123456", "Hidden Product", hidden=True),
|
||||
]
|
||||
|
||||
sql_session.add_all(products)
|
||||
sql_session.commit()
|
||||
|
||||
return products
|
||||
|
||||
|
||||
def test_search_product_no_products(sql_session: Session) -> None:
|
||||
result = search_product("Nonexistent Product", sql_session)
|
||||
|
||||
assert isinstance(result, list)
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
|
||||
def test_search_product_name_exact_match(sql_session: Session) -> None:
|
||||
insert_test_data(sql_session)
|
||||
|
||||
result = search_product("Test Product A", sql_session)
|
||||
assert isinstance(result, Product)
|
||||
assert result.bar_code == "1234567890123"
|
||||
|
||||
|
||||
def test_search_product_name_partial_match(sql_session: Session) -> None:
|
||||
insert_test_data(sql_session)
|
||||
|
||||
result = search_product("Test Product", sql_session)
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 2
|
||||
names = {product.name for product in result}
|
||||
assert names == {"Test Product A", "Test Product B"}
|
||||
|
||||
|
||||
def test_search_product_name_no_match(sql_session: Session) -> None:
|
||||
insert_test_data(sql_session)
|
||||
|
||||
result = search_product("Nonexistent", sql_session)
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 0
|
||||
|
||||
|
||||
def test_search_product_barcode_exact_match(sql_session: Session) -> None:
|
||||
products = insert_test_data(sql_session)
|
||||
|
||||
product = products[1] # Test Product B
|
||||
|
||||
result = search_product(product.bar_code, sql_session)
|
||||
assert isinstance(result, Product)
|
||||
assert result.name == product.name
|
||||
|
||||
|
||||
# Should not be able to find hidden products
|
||||
def test_search_product_hidden_products(sql_session: Session) -> None:
|
||||
insert_test_data(sql_session)
|
||||
result = search_product("Hidden Product", sql_session)
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 0
|
||||
|
||||
|
||||
# Should be able to find hidden products if specified
|
||||
def test_search_product_find_hidden_products(sql_session: Session) -> None:
|
||||
insert_test_data(sql_session)
|
||||
result = search_product("Hidden Product", sql_session, find_hidden_products=True)
|
||||
assert isinstance(result, Product)
|
||||
assert result.name == "Hidden Product"
|
||||
|
||||
|
||||
# Should be able to find hidden products by barcode despite not specified
|
||||
def test_search_product_hidden_products_by_barcode(sql_session: Session) -> None:
|
||||
products = insert_test_data(sql_session)
|
||||
hidden_product = products[3] # Hidden Product
|
||||
|
||||
result = search_product(hidden_product.bar_code, sql_session)
|
||||
assert isinstance(result, Product)
|
||||
assert result.name == "Hidden Product"
|
||||
78
tests/queries/test_search_user.py
Normal file
78
tests/queries/test_search_user.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from dibbler.models import User
|
||||
from dibbler.queries import search_user
|
||||
|
||||
USER = [
|
||||
("alice", 123),
|
||||
("bob", 125),
|
||||
("charlie", 126),
|
||||
("david", 127),
|
||||
("eve", 128),
|
||||
("evey", 129),
|
||||
("evy", 130),
|
||||
("-symbol-man", 131),
|
||||
("user_123", 132),
|
||||
]
|
||||
|
||||
|
||||
def setup_users(sql_session: Session) -> None:
|
||||
for username, rfid in USER:
|
||||
user = User(name=username, rfid=str(rfid))
|
||||
sql_session.add(user)
|
||||
sql_session.commit()
|
||||
|
||||
|
||||
def test_search_user_exact_match(sql_session: Session) -> None:
|
||||
setup_users(sql_session)
|
||||
|
||||
user = search_user("alice", sql_session)
|
||||
assert user is not None
|
||||
assert isinstance(user, User)
|
||||
assert user.name == "alice"
|
||||
|
||||
user = search_user("125", sql_session)
|
||||
assert user is not None
|
||||
assert isinstance(user, User)
|
||||
assert user.name == "bob"
|
||||
|
||||
|
||||
def test_search_user_partial_match(sql_session: Session) -> None:
|
||||
setup_users(sql_session)
|
||||
|
||||
users = search_user("ev", sql_session)
|
||||
assert isinstance(users, list)
|
||||
assert len(users) == 3
|
||||
names = {user.name for user in users}
|
||||
assert names == {"eve", "evey", "evy"}
|
||||
|
||||
users = search_user("user", sql_session)
|
||||
assert isinstance(users, list)
|
||||
assert len(users) == 1
|
||||
assert users[0].name == "user_123"
|
||||
|
||||
|
||||
def test_search_user_no_match(sql_session: Session) -> None:
|
||||
setup_users(sql_session)
|
||||
|
||||
result = search_user("nonexistent", sql_session)
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 0
|
||||
|
||||
|
||||
def test_search_user_special_characters(sql_session: Session) -> None:
|
||||
setup_users(sql_session)
|
||||
|
||||
user = search_user("-symbol-man", sql_session)
|
||||
assert user is not None
|
||||
assert isinstance(user, User)
|
||||
assert user.name == "-symbol-man"
|
||||
|
||||
|
||||
def test_search_by_rfid(sql_session: Session) -> None:
|
||||
setup_users(sql_session)
|
||||
|
||||
user = search_user("130", sql_session)
|
||||
assert user is not None
|
||||
assert isinstance(user, User)
|
||||
assert user.name == "evy"
|
||||
624
tests/queries/test_transaction_log.py
Normal file
624
tests/queries/test_transaction_log.py
Normal file
@@ -0,0 +1,624 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from dibbler.models import (
|
||||
Product,
|
||||
Transaction,
|
||||
TransactionType,
|
||||
User,
|
||||
)
|
||||
from dibbler.queries import transaction_log
|
||||
|
||||
|
||||
def insert_test_data(sql_session: Session) -> tuple[User, User, Product, Product]:
|
||||
user1 = User("Test User 1")
|
||||
user2 = User("Test User 2")
|
||||
|
||||
product1 = Product("1234567890123", "Test Product 1")
|
||||
product2 = Product("9876543210987", "Test Product 2")
|
||||
sql_session.add_all([user1, user2, product1, product2])
|
||||
sql_session.commit()
|
||||
|
||||
return user1, user2, product1, product2
|
||||
|
||||
|
||||
def insert_default_test_transactions(
|
||||
sql_session: Session,
|
||||
user1: User,
|
||||
user2: User,
|
||||
product1: Product,
|
||||
product2: Product,
|
||||
) -> list[Transaction]:
|
||||
transactions = [
|
||||
Transaction.adjust_balance(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
amount=100,
|
||||
user_id=user1.id,
|
||||
),
|
||||
Transaction.adjust_balance(
|
||||
time=datetime(2023, 10, 1, 10, 0, 1),
|
||||
amount=50,
|
||||
user_id=user2.id,
|
||||
),
|
||||
Transaction.adjust_balance(
|
||||
time=datetime(2023, 10, 1, 10, 0, 2),
|
||||
amount=-50,
|
||||
user_id=user1.id,
|
||||
),
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
amount=27 * 2,
|
||||
per_product=27,
|
||||
product_count=2,
|
||||
user_id=user1.id,
|
||||
product_id=product1.id,
|
||||
),
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 1),
|
||||
product_count=1,
|
||||
user_id=user2.id,
|
||||
product_id=product2.id,
|
||||
),
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 2),
|
||||
amount=15 * 1,
|
||||
per_product=15,
|
||||
product_count=1,
|
||||
user_id=user2.id,
|
||||
product_id=product2.id,
|
||||
),
|
||||
Transaction.transfer(
|
||||
time=datetime(2023, 10, 1, 14, 0, 0),
|
||||
amount=30,
|
||||
user_id=user1.id,
|
||||
transfer_user_id=user2.id,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
return transactions
|
||||
|
||||
|
||||
def test_transaction_log_uninitialized_user(sql_session: Session) -> None:
|
||||
user = User("Uninitialized User")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
transaction_log(sql_session, user=user)
|
||||
|
||||
|
||||
def test_transaction_log_uninitialized_product(sql_session: Session) -> None:
|
||||
product = Product("1234567890123", "Uninitialized Product")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
transaction_log(sql_session, product=product)
|
||||
|
||||
|
||||
def test_transaction_log_after_product_and_user_not_allowed(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
transaction_log(
|
||||
sql_session,
|
||||
user=user,
|
||||
product=product,
|
||||
after_time=datetime(2023, 10, 1, 11, 0, 0),
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_after_datetime_and_transaction_id_not_allowed(
|
||||
sql_session: Session,
|
||||
) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
transaction_log(
|
||||
sql_session,
|
||||
user=user,
|
||||
after_time=datetime(2023, 10, 1, 11, 0, 0),
|
||||
after_transaction_id=1,
|
||||
)
|
||||
|
||||
|
||||
def test_user_transactions_before_product_and_user_not_allowed(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
transaction_log(
|
||||
sql_session,
|
||||
user=user,
|
||||
product=product,
|
||||
before_time=datetime(2023, 10, 1, 15, 0, 0),
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_before_datetime_and_transaction_id_not_allowed(
|
||||
sql_session: Session,
|
||||
) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
transaction_log(
|
||||
sql_session,
|
||||
user=user,
|
||||
before_time=datetime(2023, 10, 1, 15, 0, 0),
|
||||
before_transaction_id=1,
|
||||
)
|
||||
|
||||
|
||||
def test_user_transactions_no_transactions(sql_session: Session) -> None:
|
||||
insert_test_data(sql_session)
|
||||
|
||||
transactions = transaction_log(sql_session)
|
||||
|
||||
assert len(transactions) == 0
|
||||
|
||||
|
||||
def test_transaction_log_basic(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
assert len(transaction_log(sql_session)) == 7
|
||||
|
||||
|
||||
def test_transaction_log_filtered_by_user(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
assert len(transaction_log(sql_session, user=user)) == 4
|
||||
assert len(transaction_log(sql_session, user=user2)) == 3
|
||||
|
||||
|
||||
def test_transaction_log_filtered_by_product(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
assert len(transaction_log(sql_session, product=product)) == 1
|
||||
assert len(transaction_log(sql_session, product=product2)) == 2
|
||||
|
||||
|
||||
def test_transaction_log_after_datetime(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
after_time=transactions[2].time,
|
||||
)
|
||||
)
|
||||
== len(transactions) - 2
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_after_datetime_no_transactions(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
after_time=transactions[-1].time + timedelta(seconds=1),
|
||||
)
|
||||
)
|
||||
== 0
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_after_datetime_exclusive(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
after_time=transactions[2].time,
|
||||
exclusive_after=True,
|
||||
)
|
||||
)
|
||||
== len(transactions) - 3
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_after_transaction_id(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
first_transaction = transactions[0]
|
||||
|
||||
assert len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
after_transaction_id=first_transaction.id,
|
||||
)
|
||||
) == len(transactions)
|
||||
|
||||
|
||||
def test_transaction_log_after_transaction_id_one_transaction(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
last_transaction = transactions[-1]
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
after_transaction_id=last_transaction.id,
|
||||
)
|
||||
)
|
||||
== 1
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_after_transaction_id_exclusive(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
third_transaction = transactions[2]
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
after_transaction_id=third_transaction.id,
|
||||
exclusive_after=True,
|
||||
)
|
||||
)
|
||||
== len(transactions) - 3
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_before_datetime(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
before_time=transactions[-3].time,
|
||||
)
|
||||
)
|
||||
== len(transactions) - 2
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_before_datetime_no_transactions(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
before_time=transactions[0].time - timedelta(seconds=1),
|
||||
)
|
||||
)
|
||||
== 0
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_before_datetime_exclusive(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
before_time=transactions[-3].time,
|
||||
exclusive_before=True,
|
||||
)
|
||||
)
|
||||
== len(transactions) - 3
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_before_transaction_id(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
last_transaction = transactions[-3]
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
before_transaction_id=last_transaction.id,
|
||||
)
|
||||
)
|
||||
== len(transactions) - 2
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_before_transaction_id_one_transaction(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
first_transaction = transactions[0]
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
before_transaction_id=first_transaction.id,
|
||||
)
|
||||
)
|
||||
== 1
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_before_transaction_id_exclusive(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
last_transaction = transactions[-3]
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
before_transaction_id=last_transaction.id,
|
||||
exclusive_before=True,
|
||||
)
|
||||
)
|
||||
== len(transactions) - 3
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_before_after_datetime_combined(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
second_transaction = transactions[1]
|
||||
fifth_transaction = transactions[4]
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
after_time=second_transaction.time,
|
||||
before_time=fifth_transaction.time,
|
||||
)
|
||||
)
|
||||
== 4
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_before_after_transaction_id_combined(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
second_transaction = transactions[1]
|
||||
fifth_transaction = transactions[4]
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
after_transaction_id=second_transaction.id,
|
||||
before_transaction_id=fifth_transaction.id,
|
||||
)
|
||||
)
|
||||
== 4
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_before_date_after_transaction_id(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
second_transaction = transactions[1]
|
||||
fifth_transaction = transactions[4]
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
before_time=fifth_transaction.time,
|
||||
after_transaction_id=second_transaction.id,
|
||||
)
|
||||
)
|
||||
== 4
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_before_transaction_id_after_date(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
second_transaction = transactions[1]
|
||||
fifth_transaction = transactions[4]
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
before_transaction_id=fifth_transaction.id,
|
||||
after_time=second_transaction.time,
|
||||
)
|
||||
)
|
||||
== 4
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_limit(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
assert len(transaction_log(sql_session, limit=3)) == 3
|
||||
assert len(transaction_log(sql_session, limit=len(transactions) + 3)) == len(transactions)
|
||||
|
||||
|
||||
def test_transaction_log_filtered_by_transaction_type(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
transaction_type=[TransactionType.ADJUST_BALANCE],
|
||||
)
|
||||
)
|
||||
== 3
|
||||
)
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
transaction_type=[TransactionType.ADD_PRODUCT],
|
||||
)
|
||||
)
|
||||
== 2
|
||||
)
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
transaction_type=[TransactionType.BUY_PRODUCT, TransactionType.ADD_PRODUCT],
|
||||
)
|
||||
)
|
||||
== 3
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_filtered_by_transaction_type_negated(sql_session: Session) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
transaction_type=[TransactionType.ADJUST_BALANCE],
|
||||
negate_transaction_type_filter=True,
|
||||
)
|
||||
)
|
||||
== len(transactions) - 3
|
||||
)
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
transaction_type=[TransactionType.ADD_PRODUCT],
|
||||
negate_transaction_type_filter=True,
|
||||
)
|
||||
)
|
||||
== len(transactions) - 2
|
||||
)
|
||||
assert (
|
||||
len(
|
||||
transaction_log(
|
||||
sql_session,
|
||||
transaction_type=[TransactionType.BUY_PRODUCT, TransactionType.ADD_PRODUCT],
|
||||
negate_transaction_type_filter=True,
|
||||
)
|
||||
)
|
||||
== len(transactions) - 3
|
||||
)
|
||||
|
||||
|
||||
def test_transaction_log_combined_filter_user_datetime_transaction_type_limit(
|
||||
sql_session: Session,
|
||||
) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
second_transaction = transactions[1]
|
||||
sixth_transaction = transactions[5]
|
||||
|
||||
result = transaction_log(
|
||||
sql_session,
|
||||
user=user,
|
||||
after_time=second_transaction.time,
|
||||
before_time=sixth_transaction.time,
|
||||
transaction_type=[TransactionType.ADJUST_BALANCE, TransactionType.ADD_PRODUCT],
|
||||
limit=2,
|
||||
)
|
||||
|
||||
assert len(result) == 2
|
||||
|
||||
|
||||
def test_transaction_log_combined_filter_user_transaction_id_transaction_type_limit(
|
||||
sql_session: Session,
|
||||
) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
second_transaction = transactions[1]
|
||||
sixth_transaction = transactions[5]
|
||||
|
||||
result = transaction_log(
|
||||
sql_session,
|
||||
user=user,
|
||||
after_transaction_id=second_transaction.id,
|
||||
before_transaction_id=sixth_transaction.id,
|
||||
transaction_type=[TransactionType.ADJUST_BALANCE, TransactionType.ADD_PRODUCT],
|
||||
limit=2,
|
||||
)
|
||||
|
||||
assert len(result) == 2
|
||||
|
||||
|
||||
def test_transaction_log_combined_filter_product_datetime_transaction_type_limit(
|
||||
sql_session: Session,
|
||||
) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
second_transaction = transactions[1]
|
||||
sixth_transaction = transactions[5]
|
||||
|
||||
result = transaction_log(
|
||||
sql_session,
|
||||
product=product2,
|
||||
after_time=second_transaction.time,
|
||||
before_time=sixth_transaction.time,
|
||||
transaction_type=[TransactionType.BUY_PRODUCT, TransactionType.ADD_PRODUCT],
|
||||
limit=2,
|
||||
)
|
||||
|
||||
assert len(result) == 2
|
||||
|
||||
|
||||
def test_transaction_log_combined_filter_product_transaction_id_transaction_type_limit(
|
||||
sql_session: Session,
|
||||
) -> None:
|
||||
user, user2, product, product2 = insert_test_data(sql_session)
|
||||
transactions = insert_default_test_transactions(sql_session, user, user2, product, product2)
|
||||
|
||||
second_transaction = transactions[1]
|
||||
sixth_transaction = transactions[5]
|
||||
|
||||
result = transaction_log(
|
||||
sql_session,
|
||||
product=product2,
|
||||
after_transaction_id=second_transaction.id,
|
||||
before_transaction_id=sixth_transaction.id,
|
||||
transaction_type=[TransactionType.BUY_PRODUCT, TransactionType.ADD_PRODUCT],
|
||||
limit=2,
|
||||
)
|
||||
|
||||
assert len(result) == 2
|
||||
|
||||
|
||||
# NOTE: see the corresponding TODO's above the function definition
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Not yet implemented")
|
||||
def test_transaction_log_filtered_by_user_joint_transactions(sql_session: Session) -> None: ...
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Not yet implemented")
|
||||
def test_transaction_log_filtered_by_user_throw_away_transactions(sql_session: Session) -> None: ...
|
||||
709
tests/queries/test_user_balance.py
Normal file
709
tests/queries/test_user_balance.py
Normal file
@@ -0,0 +1,709 @@
|
||||
import math
|
||||
from datetime import datetime
|
||||
from pprint import pprint
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from dibbler.models import Product, Transaction, User
|
||||
from dibbler.models.Transaction import DEFAULT_PENALTY_MULTIPLIER_PERCENTAGE
|
||||
from dibbler.queries import joint_buy_product, user_balance, user_balance_log
|
||||
from dibbler.queries.user_balance import _joint_transaction_query, _non_joint_transaction_query
|
||||
|
||||
# TODO: see if we can use pytest_runtest_makereport to print the "user_balance_log"s
|
||||
# only on failures instead of inlining it in every test function
|
||||
|
||||
|
||||
def insert_test_data(sql_session: Session) -> tuple[User, User, User, Product]:
|
||||
user = User("Test User")
|
||||
user2 = User("Test User 2")
|
||||
user3 = User("Test User 3")
|
||||
product = Product("1234567890123", "Test Product")
|
||||
|
||||
sql_session.add_all([user, user2, user3, product])
|
||||
sql_session.commit()
|
||||
|
||||
return user, user2, user3, product
|
||||
|
||||
|
||||
def _product_cost(
|
||||
per_product: int,
|
||||
product_count: int,
|
||||
interest_rate_percent: int,
|
||||
apply_penalty: bool,
|
||||
penalty_multiplier_percent: int,
|
||||
) -> int:
|
||||
base_cost = per_product * product_count
|
||||
cost_with_interest = math.ceil(base_cost * (interest_rate_percent / 100))
|
||||
if apply_penalty:
|
||||
total_cost = math.ceil(cost_with_interest * (penalty_multiplier_percent / 100))
|
||||
else:
|
||||
total_cost = cost_with_interest
|
||||
return total_cost
|
||||
|
||||
|
||||
def test_non_joint_transaction_query(sql_session) -> None:
|
||||
user1, user2, user3, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.adjust_balance(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user1.id,
|
||||
amount=100,
|
||||
),
|
||||
Transaction.adjust_balance(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user2.id,
|
||||
amount=50,
|
||||
),
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 1),
|
||||
user_id=user2.id,
|
||||
amount=70,
|
||||
product_id=product.id,
|
||||
product_count=3,
|
||||
per_product=30,
|
||||
),
|
||||
Transaction.transfer(
|
||||
time=datetime(2023, 10, 1, 10, 0, 2),
|
||||
user_id=user1.id,
|
||||
transfer_user_id=user2.id,
|
||||
amount=50,
|
||||
),
|
||||
Transaction.transfer(
|
||||
time=datetime(2023, 10, 1, 10, 0, 3),
|
||||
user_id=user2.id,
|
||||
transfer_user_id=user3.id,
|
||||
amount=30,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
t = transactions
|
||||
|
||||
result = [
|
||||
row[0]
|
||||
for row in sql_session.execute(
|
||||
_non_joint_transaction_query(
|
||||
user_id=user1.id,
|
||||
use_cache=False,
|
||||
),
|
||||
).all()
|
||||
]
|
||||
assert result == [t[0].id, t[3].id]
|
||||
|
||||
result = [
|
||||
row[0]
|
||||
for row in sql_session.execute(
|
||||
_non_joint_transaction_query(
|
||||
user_id=user2.id,
|
||||
use_cache=False,
|
||||
),
|
||||
).all()
|
||||
]
|
||||
assert result == [t[1].id, t[2].id, t[3].id, t[4].id]
|
||||
|
||||
result = [
|
||||
row[0]
|
||||
for row in sql_session.execute(
|
||||
_non_joint_transaction_query(
|
||||
user_id=user3.id,
|
||||
use_cache=False,
|
||||
),
|
||||
).all()
|
||||
]
|
||||
assert result == [t[4].id]
|
||||
|
||||
|
||||
def test_joint_transaction_query(sql_session: Session) -> None:
|
||||
user1, user2, user3, product = insert_test_data(sql_session)
|
||||
|
||||
j1 = joint_buy_product(
|
||||
sql_session,
|
||||
product=product,
|
||||
product_count=3,
|
||||
instigator=user1,
|
||||
users=[user1, user2],
|
||||
)
|
||||
|
||||
j2 = joint_buy_product(
|
||||
sql_session,
|
||||
product=product,
|
||||
product_count=2,
|
||||
instigator=user1,
|
||||
users=[user1, user1, user2],
|
||||
)
|
||||
|
||||
j3 = joint_buy_product(
|
||||
sql_session,
|
||||
product=product,
|
||||
product_count=2,
|
||||
instigator=user1,
|
||||
users=[user1, user3, user3],
|
||||
)
|
||||
|
||||
j4 = joint_buy_product(
|
||||
sql_session,
|
||||
product=product,
|
||||
product_count=2,
|
||||
instigator=user2,
|
||||
users=[user2, user3, user3],
|
||||
)
|
||||
|
||||
result = list(
|
||||
sql_session.execute(
|
||||
_joint_transaction_query(
|
||||
user_id=user1.id,
|
||||
use_cache=False,
|
||||
),
|
||||
).all(),
|
||||
)
|
||||
assert list(result) == [
|
||||
(j1[0].id, 1, 2),
|
||||
(j2[0].id, 2, 3),
|
||||
(j3[0].id, 1, 3),
|
||||
]
|
||||
|
||||
result = list(
|
||||
sql_session.execute(
|
||||
_joint_transaction_query(
|
||||
user_id=user2.id,
|
||||
use_cache=False,
|
||||
),
|
||||
).all(),
|
||||
)
|
||||
assert list(result) == [
|
||||
(j1[0].id, 1, 2),
|
||||
(j2[0].id, 1, 3),
|
||||
(j4[0].id, 1, 3),
|
||||
]
|
||||
|
||||
result = list(
|
||||
sql_session.execute(
|
||||
_joint_transaction_query(
|
||||
user_id=user3.id,
|
||||
use_cache=False,
|
||||
),
|
||||
).all(),
|
||||
)
|
||||
assert list(result) == [
|
||||
(j3[0].id, 2, 3),
|
||||
(j4[0].id, 2, 3),
|
||||
]
|
||||
|
||||
|
||||
def test_user_balance_no_transactions(sql_session: Session) -> None:
|
||||
user, *_ = insert_test_data(sql_session)
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
balance = user_balance(sql_session, user)
|
||||
|
||||
assert balance == 0
|
||||
|
||||
|
||||
def test_user_balance_basic_history(sql_session: Session) -> None:
|
||||
user, _, _, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.adjust_balance(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
amount=100,
|
||||
),
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 1),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=27,
|
||||
per_product=27,
|
||||
product_count=1,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
balance = user_balance(sql_session, user)
|
||||
|
||||
assert balance == 100 + 27
|
||||
|
||||
|
||||
def test_user_balance_with_transfers(sql_session: Session) -> None:
|
||||
user1, user2, _, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.adjust_balance(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user1.id,
|
||||
amount=100,
|
||||
),
|
||||
Transaction.transfer(
|
||||
time=datetime(2023, 10, 1, 10, 0, 1),
|
||||
user_id=user1.id,
|
||||
transfer_user_id=user2.id,
|
||||
amount=50,
|
||||
),
|
||||
Transaction.transfer(
|
||||
time=datetime(2023, 10, 1, 10, 0, 2),
|
||||
user_id=user2.id,
|
||||
transfer_user_id=user1.id,
|
||||
amount=30,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(user_balance_log(sql_session, user1))
|
||||
|
||||
user1_balance = user_balance(sql_session, user1)
|
||||
assert user1_balance == 100 - 50 + 30
|
||||
|
||||
pprint(user_balance_log(sql_session, user2))
|
||||
|
||||
user2_balance = user_balance(sql_session, user2)
|
||||
assert user2_balance == 50 - 30
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Not yet implemented")
|
||||
def test_user_balance_complex_history(sql_session: Session) -> None: ...
|
||||
|
||||
|
||||
def test_user_balance_penalty(sql_session: Session) -> None:
|
||||
user, _, _, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=27,
|
||||
per_product=27,
|
||||
product_count=1,
|
||||
),
|
||||
Transaction.adjust_balance(
|
||||
time=datetime(2023, 10, 1, 11, 0, 0),
|
||||
user_id=user.id,
|
||||
amount=-200,
|
||||
),
|
||||
# Penalized, pays 2x the price (default penalty)
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
assert user_balance(sql_session, user) == 27 - 200 - (
|
||||
27 * (DEFAULT_PENALTY_MULTIPLIER_PERCENTAGE // 100)
|
||||
)
|
||||
|
||||
|
||||
def test_user_balance_changing_penalty(sql_session: Session) -> None:
|
||||
user, _, _, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=27,
|
||||
per_product=27,
|
||||
product_count=1,
|
||||
),
|
||||
Transaction.adjust_balance(
|
||||
time=datetime(2023, 10, 1, 11, 0, 0),
|
||||
user_id=user.id,
|
||||
amount=-200,
|
||||
),
|
||||
# Penalized, pays 2x the price (default penalty)
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
),
|
||||
Transaction.adjust_penalty(
|
||||
time=datetime(2023, 10, 1, 13, 0, 0),
|
||||
user_id=user.id,
|
||||
penalty_multiplier_percent=300,
|
||||
penalty_threshold=-100,
|
||||
),
|
||||
# Penalized, pays 3x the price
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 14, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
),
|
||||
]
|
||||
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
assert user_balance(sql_session, user) == 27 - 200 - (
|
||||
27 * (DEFAULT_PENALTY_MULTIPLIER_PERCENTAGE // 100)
|
||||
) - (27 * 3)
|
||||
|
||||
|
||||
def test_user_balance_interest(sql_session: Session) -> None:
|
||||
user, _, _, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=27,
|
||||
per_product=27,
|
||||
product_count=1,
|
||||
),
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 10, 1, 11, 0, 0),
|
||||
user_id=user.id,
|
||||
interest_rate_percent=110,
|
||||
),
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
assert user_balance(sql_session, user) == 27 - math.ceil(27 * 1.1)
|
||||
|
||||
|
||||
def test_user_balance_changing_interest(sql_session: Session) -> None:
|
||||
user, _, _, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=27 * 3,
|
||||
per_product=27,
|
||||
product_count=3,
|
||||
),
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 10, 1, 11, 0, 0),
|
||||
user_id=user.id,
|
||||
interest_rate_percent=110,
|
||||
),
|
||||
# Pays 1.1x the price
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
),
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 10, 1, 13, 0, 0),
|
||||
user_id=user.id,
|
||||
interest_rate_percent=120,
|
||||
),
|
||||
# Pays 1.2x the price
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 14, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
assert user_balance(sql_session, user) == 27 * 3 - math.ceil(27 * 1.1) - math.ceil(27 * 1.2)
|
||||
|
||||
|
||||
def test_user_balance_penalty_interest_combined(sql_session: Session) -> None:
|
||||
user, _, _, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=27,
|
||||
per_product=27,
|
||||
product_count=1,
|
||||
),
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 10, 1, 11, 0, 0),
|
||||
user_id=user.id,
|
||||
interest_rate_percent=110,
|
||||
),
|
||||
Transaction.adjust_balance(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
user_id=user.id,
|
||||
amount=-200,
|
||||
),
|
||||
# Penalized, pays 2x the price (default penalty)
|
||||
# Pays 1.1x the price
|
||||
Transaction.buy_product(
|
||||
time=datetime(2023, 10, 1, 13, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
product_count=1,
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
assert user_balance(sql_session, user) == (
|
||||
27 - 200 - math.ceil(27 * (DEFAULT_PENALTY_MULTIPLIER_PERCENTAGE // 100) * 1.1)
|
||||
)
|
||||
|
||||
|
||||
def test_user_balance_joint_transaction_single_user(sql_session: Session) -> None:
|
||||
user, _, _, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=27 * 3,
|
||||
per_product=27,
|
||||
product_count=3,
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user],
|
||||
product=product,
|
||||
product_count=2,
|
||||
)
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
assert user_balance(sql_session, user) == (27 * 3) - (27 * 2)
|
||||
|
||||
|
||||
def test_user_balance_joint_transactions_multiple_users(sql_session: Session) -> None:
|
||||
user, user2, user3, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=27 * 3,
|
||||
per_product=27,
|
||||
product_count=3,
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user, user2, user3],
|
||||
product=product,
|
||||
product_count=2,
|
||||
)
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
assert user_balance(sql_session, user) == (27 * 3) - math.ceil((27 * 2) / 3)
|
||||
|
||||
|
||||
def test_user_balance_joint_transactions_multiple_times_self(sql_session: Session) -> None:
|
||||
user, user2, _, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=27 * 3,
|
||||
per_product=27,
|
||||
product_count=3,
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user, user, user2],
|
||||
product=product,
|
||||
product_count=2,
|
||||
)
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
assert user_balance(sql_session, user) == (27 * 3) - math.ceil((27 * 2) * (2 / 3))
|
||||
|
||||
|
||||
def test_user_balance_joint_transactions_interest(sql_session: Session) -> None:
|
||||
user, user2, _, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=27 * 3,
|
||||
per_product=27,
|
||||
product_count=3,
|
||||
),
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 10, 1, 11, 0, 0),
|
||||
user_id=user.id,
|
||||
interest_rate_percent=110,
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user, user2],
|
||||
product=product,
|
||||
product_count=2,
|
||||
)
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
assert user_balance(sql_session, user) == (27 * 3) - math.ceil(math.ceil(27 * 2 / 2) * 1.1)
|
||||
|
||||
|
||||
def test_user_balance_joint_transactions_changing_interest(sql_session: Session) -> None:
|
||||
user, user2, _, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=27 * 4,
|
||||
per_product=27,
|
||||
product_count=4,
|
||||
),
|
||||
# Pays 1.1x the price
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 10, 1, 11, 0, 0),
|
||||
user_id=user.id,
|
||||
interest_rate_percent=110,
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user, user2],
|
||||
product=product,
|
||||
product_count=2,
|
||||
)
|
||||
|
||||
transactions = [
|
||||
# Pays 1.2x the price
|
||||
Transaction.adjust_interest(
|
||||
time=datetime(2023, 10, 1, 12, 0, 0),
|
||||
user_id=user.id,
|
||||
interest_rate_percent=120,
|
||||
)
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user, user2],
|
||||
product=product,
|
||||
product_count=1,
|
||||
)
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
assert user_balance(sql_session, user) == (
|
||||
(27 * 4) - math.ceil(math.ceil(27 * 2 / 2) * 1.1) - math.ceil(math.ceil(27 * 1 / 2) * 1.2)
|
||||
)
|
||||
|
||||
|
||||
def test_user_balance_joint_transactions_penalty(sql_session: Session) -> None:
|
||||
user, user2, _, product = insert_test_data(sql_session)
|
||||
|
||||
transactions = [
|
||||
Transaction.add_product(
|
||||
time=datetime(2023, 10, 1, 10, 0, 0),
|
||||
user_id=user.id,
|
||||
product_id=product.id,
|
||||
amount=27 * 3,
|
||||
per_product=27,
|
||||
product_count=3,
|
||||
),
|
||||
Transaction.adjust_balance(
|
||||
time=datetime(2023, 10, 1, 11, 0, 0),
|
||||
user_id=user.id,
|
||||
amount=-200,
|
||||
),
|
||||
]
|
||||
sql_session.add_all(transactions)
|
||||
sql_session.commit()
|
||||
|
||||
joint_buy_product(
|
||||
sql_session,
|
||||
instigator=user,
|
||||
users=[user, user2],
|
||||
product=product,
|
||||
product_count=2,
|
||||
)
|
||||
|
||||
pprint(user_balance_log(sql_session, user))
|
||||
|
||||
assert user_balance(sql_session, user) == (
|
||||
(27 * 3)
|
||||
- 200
|
||||
- math.ceil(math.ceil(27 * 2 / 2) * (DEFAULT_PENALTY_MULTIPLIER_PERCENTAGE // 100))
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Not yet implemented")
|
||||
def test_user_balance_joint_transactions_changing_penalty(sql_session: Session) -> None: ...
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Not yet implemented")
|
||||
def test_user_balance_joint_transactions_penalty_interest_combined(
|
||||
sql_session: Session,
|
||||
) -> None: ...
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Not yet implemented")
|
||||
def test_user_balance_until(sql_session: Session) -> None: ...
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Not yet implemented")
|
||||
def test_user_balance_throw_away_products(sql_session: Session) -> None: ...
|
||||
Reference in New Issue
Block a user