fixup! WIP
Run tests / run-tests (push) Successful in 1m25s

This commit is contained in:
2025-12-10 11:39:30 +09:00
parent 2207001136
commit 7f4a980eef
14 changed files with 242 additions and 111 deletions
+23 -21
View File
@@ -43,21 +43,22 @@ def _product_owners_query(
# Subset of transactions that we'll want to iterate over. # Subset of transactions that we'll want to iterate over.
trx_subset = ( trx_subset = (
select( select(
func.row_number().over(order_by=asc(Transaction.time)).label("i"), func.row_number().over(order_by=Transaction.time.desc()).label("i"),
Transaction.time, Transaction.time,
Transaction.id, Transaction.id,
Transaction.type_, Transaction.type_,
Transaction.user_id, Transaction.user_id,
Transaction.product_count, Transaction.product_count,
) )
# TODO: maybe add value constraint on ADJUST_STOCK?
.where( .where(
Transaction.type_.in_( Transaction.type_.in_(
[ [
TransactionType.ADD_PRODUCT, TransactionType.ADD_PRODUCT,
TransactionType.BUY_PRODUCT, # TransactionType.BUY_PRODUCT,
TransactionType.ADJUST_STOCK, TransactionType.ADJUST_STOCK,
TransactionType.JOINT, # TransactionType.JOINT,
TransactionType.THROW_PRODUCT, # TransactionType.THROW_PRODUCT,
] ]
), ),
Transaction.product_id == product_id, Transaction.product_id == product_id,
@@ -112,16 +113,16 @@ def _product_owners_query(
recursive_cte.c.products_left_to_account_for - trx_subset.c.product_count, recursive_cte.c.products_left_to_account_for - trx_subset.c.product_count,
), ),
# Someone buys/joins/throws the product -> decrease the number of products left to account for # Someone buys/joins/throws the product -> decrease the number of products left to account for
( # (
trx_subset.c.type_.in_( # trx_subset.c.type_.in_(
[ # [
TransactionType.BUY_PRODUCT, # TransactionType.BUY_PRODUCT,
TransactionType.JOINT, # TransactionType.JOINT,
TransactionType.THROW_PRODUCT, # TransactionType.THROW_PRODUCT,
] # ]
), # ),
recursive_cte.c.products_left_to_account_for - trx_subset.c.product_count, # recursive_cte.c.products_left_to_account_for - trx_subset.c.product_count,
), # ),
# Someone adjusts the stock -> # Someone adjusts the stock ->
# If adjusted upwards -> products owned by nobody, decrease products left to account for # If adjusted upwards -> products owned by nobody, decrease products left to account for
# If adjusted downwards -> products taken away from owners, decrease products left to account for # If adjusted downwards -> products taken away from owners, decrease products left to account for
@@ -130,11 +131,11 @@ def _product_owners_query(
and (trx_subset.c.product_count > 0), and (trx_subset.c.product_count > 0),
recursive_cte.c.products_left_to_account_for - trx_subset.c.product_count, recursive_cte.c.products_left_to_account_for - trx_subset.c.product_count,
), ),
( # (
(trx_subset.c.type_ == TransactionType.ADJUST_STOCK) # (trx_subset.c.type_ == TransactionType.ADJUST_STOCK)
and (trx_subset.c.product_count < 0), # and (trx_subset.c.product_count < 0),
recursive_cte.c.products_left_to_account_for + trx_subset.c.product_count, # recursive_cte.c.products_left_to_account_for + trx_subset.c.product_count,
), # ),
else_=recursive_cte.c.products_left_to_account_for, else_=recursive_cte.c.products_left_to_account_for,
).label("products_left_to_account_for"), ).label("products_left_to_account_for"),
) )
@@ -191,7 +192,7 @@ def product_owners_log(
onclause=User.id == recursive_cte.c.user_id, onclause=User.id == recursive_cte.c.user_id,
isouter=True, isouter=True,
) )
.order_by(recursive_cte.c.i.desc()) .order_by(recursive_cte.c.time.desc())
).all() ).all()
if result is None: if result is None:
@@ -235,7 +236,7 @@ def product_owners(
User, User,
) )
.join(User, User.id == recursive_cte.c.user_id, isouter=True) .join(User, User.id == recursive_cte.c.user_id, isouter=True)
.order_by(recursive_cte.c.i.desc()) .order_by(recursive_cte.c.time.desc())
).all() ).all()
print(db_result) print(db_result)
@@ -249,6 +250,7 @@ def product_owners(
# User is none, and product_count is not 0 -> add None product_count times # User is none, and product_count is not 0 -> add None product_count times
# User is none, and product_count is 0 -> check how much products are left to account for, # User is none, and product_count is 0 -> check how much products are left to account for,
# TODO: embed this into the query itself?
for products_left_to_account_for, product_count, user in db_result: for products_left_to_account_for, product_count, user in db_result:
if user is not None: if user is not None:
if products_left_to_account_for < 0: if products_left_to_account_for < 0:
+1 -1
View File
@@ -42,7 +42,7 @@
devShells = forAllSystems (system: pkgs: { devShells = forAllSystems (system: pkgs: {
default = self.devShells.${system}.dibbler; default = self.devShells.${system}.dibbler;
dibbler = pkgs.callPackage ./nix/shell.nix { dibbler = pkgs.callPackage ./nix/shell.nix {
python = pkgs.python312; python3 = pkgs.python312;
}; };
}); });
+3 -2
View File
@@ -1,6 +1,6 @@
{ {
mkShell, mkShell,
python, python3,
ruff, ruff,
uv, uv,
}: }:
@@ -9,12 +9,13 @@ mkShell {
packages = [ packages = [
ruff ruff
uv uv
(python.withPackages (ps: with ps; [ (python3.withPackages (ps: with ps; [
brother-ql brother-ql
matplotlib matplotlib
psycopg2 psycopg2
python-barcode python-barcode
sqlalchemy sqlalchemy
sqlparse
pytest pytest
pytest-cov pytest-cov
+1
View File
@@ -26,6 +26,7 @@ test = [
"pytest-cov", "pytest-cov",
"coverage-badge>=1.1.2", "coverage-badge>=1.1.2",
"pytest-html>=4.1.1", "pytest-html>=4.1.1",
"sqlparse>=0.5.4",
] ]
[tool.setuptools.packages.find] [tool.setuptools.packages.find]
+28 -2
View File
@@ -1,5 +1,7 @@
import pytest import logging
import pytest
import sqlparse
from sqlalchemy import create_engine, event from sqlalchemy import create_engine, event
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@@ -14,12 +16,36 @@ def pytest_addoption(parser):
) )
class SqlParseFormatter(logging.Formatter):
def format(self, record):
recordMessage = record.getMessage()
if not recordMessage.startswith("[") and any(
recordMessage.startswith(keyword)
for keyword in [
"SELECT",
"INSERT",
"UPDATE",
"DELETE",
"WITH",
]
):
formatted_sql = sqlparse.format(recordMessage, reindent=True, keyword_case="upper")
record.msg = "\n" + formatted_sql
return super().format(record)
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def sql_session(request): def sql_session(request):
"""Create a new SQLAlchemy session for testing.""" """Create a new SQLAlchemy session for testing."""
echo = request.config.getoption("--echo") logging.basicConfig()
logger = logging.getLogger("sqlalchemy.engine")
handler = logging.StreamHandler()
handler.setFormatter(SqlParseFormatter())
logger.addHandler(handler)
echo = request.config.getoption("--echo")
engine = create_engine( engine = create_engine(
"sqlite:///:memory:", "sqlite:///:memory:",
echo=echo, echo=echo,
+10 -9
View File
@@ -1,18 +1,23 @@
import pytest
from datetime import datetime from datetime import datetime
import pytest
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from dibbler.models import Transaction, User from dibbler.models import Transaction, User
from dibbler.queries import adjust_interest, current_interest from dibbler.queries import adjust_interest, current_interest
def test_adjust_interest_no_history(sql_session: Session) -> None: def insert_test_data(sql_session: Session) -> User:
user = User("Test User") user = User("Test User")
sql_session.add(user) sql_session.add(user)
sql_session.commit() sql_session.commit()
return user
def test_adjust_interest_no_history(sql_session: Session) -> None:
user = insert_test_data(sql_session)
adjust_interest( adjust_interest(
sql_session, sql_session,
user_id=user.id, user_id=user.id,
@@ -27,9 +32,7 @@ def test_adjust_interest_no_history(sql_session: Session) -> None:
def test_adjust_interest_existing_history(sql_session: Session) -> None: def test_adjust_interest_existing_history(sql_session: Session) -> None:
user = User("Test User") user = insert_test_data(sql_session)
sql_session.add(user)
sql_session.commit()
transactions = [ transactions = [
Transaction.adjust_interest( Transaction.adjust_interest(
@@ -58,9 +61,7 @@ def test_adjust_interest_existing_history(sql_session: Session) -> None:
def test_adjust_interest_negative_failure(sql_session: Session) -> None: def test_adjust_interest_negative_failure(sql_session: Session) -> None:
user = User("Test User") user = insert_test_data(sql_session)
sql_session.add(user)
sql_session.commit()
with pytest.raises(ValueError, match="Interest rate cannot be negative"): with pytest.raises(ValueError, match="Interest rate cannot be negative"):
adjust_interest( adjust_interest(
+12 -16
View File
@@ -11,11 +11,17 @@ from dibbler.models.Transaction import (
from dibbler.queries import adjust_penalty, current_penalty from dibbler.queries import adjust_penalty, current_penalty
def test_adjust_penalty_no_history(sql_session: Session) -> None: def insert_test_data(sql_session: Session) -> User:
user = User("Test User") user = User("Test User")
sql_session.add(user) sql_session.add(user)
sql_session.commit() sql_session.commit()
return user
def test_adjust_penalty_no_history(sql_session: Session) -> None:
user = insert_test_data(sql_session)
adjust_penalty( adjust_penalty(
sql_session, sql_session,
user_id=user.id, user_id=user.id,
@@ -31,9 +37,7 @@ def test_adjust_penalty_no_history(sql_session: Session) -> None:
def test_adjust_penalty_multiplier_no_history(sql_session: Session) -> None: def test_adjust_penalty_multiplier_no_history(sql_session: Session) -> None:
user = User("Test User") user = insert_test_data(sql_session)
sql_session.add(user)
sql_session.commit()
adjust_penalty( adjust_penalty(
sql_session, sql_session,
@@ -50,9 +54,7 @@ def test_adjust_penalty_multiplier_no_history(sql_session: Session) -> None:
def test_adjust_penalty_multiplier_less_than_100_fail(sql_session: Session) -> None: def test_adjust_penalty_multiplier_less_than_100_fail(sql_session: Session) -> None:
user = User("Test User") user = insert_test_data(sql_session)
sql_session.add(user)
sql_session.commit()
adjust_penalty( adjust_penalty(
sql_session, sql_session,
@@ -76,9 +78,7 @@ def test_adjust_penalty_multiplier_less_than_100_fail(sql_session: Session) -> N
def test_adjust_penalty_existing_history(sql_session: Session) -> None: def test_adjust_penalty_existing_history(sql_session: Session) -> None:
user = User("Test User") user = insert_test_data(sql_session)
sql_session.add(user)
sql_session.commit()
transactions = [ transactions = [
Transaction.adjust_penalty( Transaction.adjust_penalty(
@@ -108,9 +108,7 @@ def test_adjust_penalty_existing_history(sql_session: Session) -> None:
def test_adjust_penalty_multiplier_existing_history(sql_session: Session) -> None: def test_adjust_penalty_multiplier_existing_history(sql_session: Session) -> None:
user = User("Test User") user = insert_test_data(sql_session)
sql_session.add(user)
sql_session.commit()
transactions = [ transactions = [
Transaction.adjust_penalty( Transaction.adjust_penalty(
@@ -139,9 +137,7 @@ def test_adjust_penalty_multiplier_existing_history(sql_session: Session) -> Non
def test_adjust_penalty_and_multiplier(sql_session: Session) -> None: def test_adjust_penalty_and_multiplier(sql_session: Session) -> None:
user = User("Test User") user = insert_test_data(sql_session)
sql_session.add(user)
sql_session.commit()
adjust_penalty( adjust_penalty(
sql_session, sql_session,
+17
View File
@@ -1,9 +1,26 @@
import pytest
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@pytest.mark.skip(reason="Not yet implemented")
def test_joint_buy_product_missing_product(sql_session: Session) -> None: ... def test_joint_buy_product_missing_product(sql_session: Session) -> None: ...
@pytest.mark.skip(reason="Not yet implemented")
def test_joint_buy_product_missing_user(sql_session: Session) -> None: ... def test_joint_buy_product_missing_user(sql_session: Session) -> None: ...
@pytest.mark.skip(reason="Not yet implemented")
def test_joint_buy_product_out_of_stock(sql_session: Session) -> None: ... def test_joint_buy_product_out_of_stock(sql_session: Session) -> None: ...
@pytest.mark.skip(reason="Not yet implemented")
def test_joint_buy_product(sql_session: Session) -> None: ... def test_joint_buy_product(sql_session: Session) -> None: ...
@pytest.mark.skip(reason="Not yet implemented")
def test_joint_buy_product_duplicate_user(sql_session: Session) -> None: ... def test_joint_buy_product_duplicate_user(sql_session: Session) -> None: ...
@pytest.mark.skip(reason="Not yet implemented")
def test_joint_buy_product_non_involved_instigator(sql_session: Session) -> None: ... def test_joint_buy_product_non_involved_instigator(sql_session: Session) -> None: ...
+24 -2
View File
@@ -1,10 +1,11 @@
from datetime import datetime
from pprint import pprint from pprint import pprint
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from dibbler.models import Product, User from dibbler.models import Product, User
from dibbler.models.Transaction import Transaction from dibbler.models.Transaction import Transaction
from dibbler.queries import product_owners, product_owners_log from dibbler.queries import product_owners, product_owners_log, product_stock
def insert_test_data(sql_session: Session) -> tuple[Product, User]: def insert_test_data(sql_session: Session) -> tuple[Product, User]:
@@ -38,6 +39,7 @@ def test_product_owners_add_products(sql_session: Session) -> None:
amount=30, amount=30,
per_product=10, per_product=10,
product_count=3, product_count=3,
time=datetime(2024, 1, 1, 10, 0, 0),
) )
] ]
sql_session.add_all(transactions) sql_session.add_all(transactions)
@@ -58,11 +60,13 @@ def test_product_owners_add_and_buy_products(sql_session: Session) -> None:
amount=30, amount=30,
per_product=10, per_product=10,
product_count=3, product_count=3,
time=datetime(2024, 1, 1, 10, 0, 0),
), ),
Transaction.buy_product( Transaction.buy_product(
user_id=user.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=1, product_count=1,
time=datetime(2024, 1, 2, 10, 0, 0),
), ),
] ]
@@ -84,11 +88,13 @@ def test_product_owners_add_and_throw_products(sql_session: Session) -> None:
amount=40, amount=40,
per_product=10, per_product=10,
product_count=4, product_count=4,
time=datetime(2024, 1, 1, 10, 0, 0),
), ),
Transaction.throw_product( Transaction.throw_product(
user_id=user.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=2, product_count=2,
time=datetime(2024, 1, 2, 10, 0, 0),
), ),
] ]
@@ -113,6 +119,7 @@ def test_product_owners_multiple_users(sql_session: Session) -> None:
amount=20, amount=20,
per_product=10, per_product=10,
product_count=2, product_count=2,
time=datetime(2024, 1, 1, 10, 0, 0),
), ),
Transaction.add_product( Transaction.add_product(
user_id=user2.id, user_id=user2.id,
@@ -120,6 +127,7 @@ def test_product_owners_multiple_users(sql_session: Session) -> None:
amount=30, amount=30,
per_product=10, per_product=10,
product_count=3, product_count=3,
time=datetime(2024, 1, 2, 10, 0, 0),
), ),
] ]
@@ -142,11 +150,13 @@ def test_product_owners_adjust_stock_down(sql_session: Session) -> None:
amount=50, amount=50,
per_product=10, per_product=10,
product_count=5, product_count=5,
time=datetime(2024, 1, 1, 10, 0, 0),
), ),
Transaction.adjust_stock( Transaction.adjust_stock(
user_id=user.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=-2, product_count=-2,
time=datetime(2024, 1, 2, 10, 0, 0),
), ),
] ]
sql_session.add_all(transactions) sql_session.add_all(transactions)
@@ -154,6 +164,8 @@ def test_product_owners_adjust_stock_down(sql_session: Session) -> None:
pprint(product_owners_log(sql_session, product)) pprint(product_owners_log(sql_session, product))
assert product_stock(sql_session, product) == 3
owners = product_owners(sql_session, product) owners = product_owners(sql_session, product)
assert owners == [user, user, user] assert owners == [user, user, user]
@@ -168,11 +180,13 @@ def test_product_owners_adjust_stock_up(sql_session: Session) -> None:
amount=20, amount=20,
per_product=10, per_product=10,
product_count=2, product_count=2,
time=datetime(2024, 1, 1, 10, 0, 0),
), ),
Transaction.adjust_stock( Transaction.adjust_stock(
user_id=user.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=3, product_count=3,
time=datetime(2024, 1, 2, 10, 0, 0),
), ),
] ]
sql_session.add_all(transactions) sql_session.add_all(transactions)
@@ -194,11 +208,13 @@ def test_product_owners_negative_stock(sql_session: Session) -> None:
amount=10, amount=10,
per_product=10, per_product=10,
product_count=1, product_count=1,
time=datetime(2024, 1, 1, 10, 0, 0),
), ),
Transaction.buy_product( Transaction.buy_product(
user_id=user.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=2, product_count=2,
time=datetime(2024, 1, 2, 10, 0, 0),
), ),
] ]
sql_session.add_all(transactions) sql_session.add_all(transactions)
@@ -216,6 +232,7 @@ def test_product_owners_add_products_from_negative_stock(sql_session: Session) -
user_id=user.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=2, product_count=2,
time=datetime(2024, 1, 1, 10, 0, 0),
), ),
Transaction.add_product( Transaction.add_product(
user_id=user.id, user_id=user.id,
@@ -223,6 +240,7 @@ def test_product_owners_add_products_from_negative_stock(sql_session: Session) -
amount=30, amount=30,
per_product=10, per_product=10,
product_count=3, product_count=3,
time=datetime(2024, 1, 2, 10, 0, 0),
), ),
] ]
sql_session.add_all(transactions) sql_session.add_all(transactions)
@@ -247,6 +265,7 @@ def test_product_owners_interleaved_users(sql_session: Session) -> None:
amount=20, amount=20,
per_product=10, per_product=10,
product_count=2, product_count=2,
time=datetime(2024, 1, 1, 10, 0, 0),
), ),
Transaction.add_product( Transaction.add_product(
user_id=user2.id, user_id=user2.id,
@@ -254,11 +273,13 @@ def test_product_owners_interleaved_users(sql_session: Session) -> None:
amount=30, amount=30,
per_product=10, per_product=10,
product_count=3, product_count=3,
time=datetime(2024, 1, 2, 10, 0, 0),
), ),
Transaction.buy_product( Transaction.buy_product(
user_id=user1.id, user_id=user1.id,
product_id=product.id, product_id=product.id,
product_count=1, product_count=1,
time=datetime(2024, 1, 3, 10, 0, 0),
), ),
Transaction.add_product( Transaction.add_product(
user_id=user1.id, user_id=user1.id,
@@ -266,6 +287,7 @@ def test_product_owners_interleaved_users(sql_session: Session) -> None:
amount=10, amount=10,
per_product=10, per_product=10,
product_count=1, product_count=1,
time=datetime(2024, 1, 4, 10, 0, 0),
), ),
] ]
sql_session.add_all(transactions) sql_session.add_all(transactions)
@@ -274,4 +296,4 @@ def test_product_owners_interleaved_users(sql_session: Session) -> None:
pprint(product_owners_log(sql_session, product)) pprint(product_owners_log(sql_session, product))
owners = product_owners(sql_session, product) owners = product_owners(sql_session, product)
assert owners == [user1, user2, user2, user1, user1] assert owners == [user1, user2, user2, user2, user1]
+3
View File
@@ -416,3 +416,6 @@ def test_product_price_joint_transactions(sql_session: Session) -> None:
# Count: 3 + 4 = 7, Price: (26 * 3 + 25 * 4) / (3 + 4) = 25.57 -> 26 # 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)) assert product_price_ == math.ceil((26 * 3 + 25 * 4) / (3 + 4))
def test_product_price_until(sql_session: Session) -> None: ...
+85 -44
View File
@@ -1,26 +1,27 @@
from datetime import datetime from datetime import datetime
import pytest
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from dibbler.models import Product, Transaction, User from dibbler.models import Product, Transaction, User
from dibbler.queries import product_stock, joint_buy_product from dibbler.models.TransactionType import TransactionTypeSQL
from dibbler.queries import joint_buy_product, product_stock
def insert_test_data(sql_session: Session) -> None: def insert_test_data(sql_session: Session) -> tuple[User, Product]:
user1 = User("Test User 1") user = User("Test User 1")
product = Product("1234567890123", "Test Product")
sql_session.add(user1) sql_session.add(user)
sql_session.add(product)
sql_session.commit() sql_session.commit()
return user, product
def test_product_stock_basic_history(sql_session: Session) -> None: def test_product_stock_basic_history(sql_session: Session) -> None:
insert_test_data(sql_session) user, product = insert_test_data(sql_session)
user1 = sql_session.scalars(select(User).where(User.name == "Test User 1")).one()
product = Product("1234567890123", "Test Product")
sql_session.add(product)
sql_session.commit() sql_session.commit()
transactions = [ transactions = [
@@ -28,7 +29,7 @@ def test_product_stock_basic_history(sql_session: Session) -> None:
time=datetime(2023, 10, 1, 12, 0, 0), time=datetime(2023, 10, 1, 12, 0, 0),
amount=10, amount=10,
per_product=10, per_product=10,
user_id=user1.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=1, product_count=1,
), ),
@@ -40,27 +41,71 @@ def test_product_stock_basic_history(sql_session: Session) -> None:
assert product_stock(sql_session, product) == 1 assert product_stock(sql_session, product) == 1
def test_product_stock_complex_history(sql_session: Session) -> None: def test_product_stock_adjust_stock_up(sql_session: Session) -> None:
insert_test_data(sql_session) user, product = insert_test_data(sql_session)
user1 = sql_session.scalars(select(User).where(User.name == "Test User 1")).one() transactions = [
Transaction.add_product(
product = Product("1234567890123", "Test Product") user_id=user.id,
sql_session.add(product) 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() 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 = [ transactions = [
Transaction.add_product( Transaction.add_product(
time=datetime(2023, 10, 1, 13, 0, 0), time=datetime(2023, 10, 1, 13, 0, 0),
amount=27 * 2, amount=27 * 2,
per_product=27, per_product=27,
user_id=user1.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=2, product_count=2,
), ),
Transaction.buy_product( Transaction.buy_product(
time=datetime(2023, 10, 1, 13, 0, 1), time=datetime(2023, 10, 1, 13, 0, 1),
user_id=user1.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=3, product_count=3,
), ),
@@ -68,67 +113,63 @@ def test_product_stock_complex_history(sql_session: Session) -> None:
time=datetime(2023, 10, 1, 13, 0, 2), time=datetime(2023, 10, 1, 13, 0, 2),
amount=50 * 4, amount=50 * 4,
per_product=50, per_product=50,
user_id=user1.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=4, product_count=4,
), ),
Transaction.adjust_stock( Transaction.adjust_stock(
time=datetime(2023, 10, 1, 15, 0, 0), time=datetime(2023, 10, 1, 15, 0, 0),
user_id=user1.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=3, product_count=3,
), ),
Transaction.adjust_stock( Transaction.adjust_stock(
time=datetime(2023, 10, 1, 15, 0, 1), time=datetime(2023, 10, 1, 15, 0, 1),
user_id=user1.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=-2, 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.add_all(transactions)
sql_session.commit() sql_session.commit()
assert product_stock(sql_session, product) == 2 - 3 + 4 + 3 - 2 assert product_stock(sql_session, product) == 2 - 3 + 4 + 3 - 2 - 1
def test_product_stock_no_transactions(sql_session: Session) -> None: def test_product_stock_no_transactions(sql_session: Session) -> None:
insert_test_data(sql_session) _, product = insert_test_data(sql_session)
product = Product("1234567890123", "Test Product")
sql_session.add(product)
sql_session.commit()
assert product_stock(sql_session, product) == 0 assert product_stock(sql_session, product) == 0
def test_negative_product_stock(sql_session: Session) -> None: def test_negative_product_stock(sql_session: Session) -> None:
insert_test_data(sql_session) user, product = insert_test_data(sql_session)
user1 = sql_session.scalars(select(User).where(User.name == "Test User 1")).one()
product = Product("1234567890123", "Test Product")
sql_session.add(product)
sql_session.commit()
transactions = [ transactions = [
Transaction.add_product( Transaction.add_product(
time=datetime(2023, 10, 1, 14, 0, 0), time=datetime(2023, 10, 1, 14, 0, 0),
amount=50, amount=50,
per_product=50, per_product=50,
user_id=user1.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=1, product_count=1,
), ),
Transaction.buy_product( Transaction.buy_product(
time=datetime(2023, 10, 1, 14, 0, 1), time=datetime(2023, 10, 1, 14, 0, 1),
user_id=user1.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=2, product_count=2,
), ),
Transaction.adjust_stock( Transaction.adjust_stock(
time=datetime(2023, 10, 1, 16, 0, 0), time=datetime(2023, 10, 1, 16, 0, 0),
user_id=user1.id, user_id=user.id,
product_id=product.id, product_id=product.id,
product_count=-1, product_count=-1,
), ),
@@ -142,17 +183,12 @@ def test_negative_product_stock(sql_session: Session) -> None:
def test_product_stock_joint_transaction(sql_session: Session) -> None: def test_product_stock_joint_transaction(sql_session: Session) -> None:
insert_test_data(sql_session) user1, product = insert_test_data(sql_session)
user1 = sql_session.scalars(select(User).where(User.name == "Test User 1")).one()
user2 = User("Test User 2") user2 = User("Test User 2")
sql_session.add(user2) sql_session.add(user2)
sql_session.commit() sql_session.commit()
product = Product("1234567890123", "Test Product")
sql_session.add(product)
sql_session.commit()
transactions = [ transactions = [
Transaction.add_product( Transaction.add_product(
time=datetime(2023, 10, 1, 17, 0, 0), time=datetime(2023, 10, 1, 17, 0, 0),
@@ -179,4 +215,9 @@ def test_product_stock_joint_transaction(sql_session: Session) -> None:
assert product_stock(sql_session, product) == 5 - 3 assert product_stock(sql_session, product) == 5 - 3
@pytest.mark.skip(reason="Not yet implemented")
def test_product_stock_until(sql_session: Session) -> None: ...
@pytest.mark.skip(reason="Not yet implemented")
def test_product_stock_throw_away(sql_session: Session) -> None: ... def test_product_stock_throw_away(sql_session: Session) -> None: ...
+4
View File
@@ -567,5 +567,9 @@ def test_transaction_log_combined_filter_product_transaction_id_transaction_type
# NOTE: see the corresponding TODO's above the function definition # 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: ... 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: ... def test_transaction_log_filtered_by_user_throw_away_transactions(sql_session: Session) -> None: ...
+20 -14
View File
@@ -2,6 +2,8 @@ import math
from datetime import datetime from datetime import datetime
from pprint import pprint from pprint import pprint
import pytest
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from dibbler.models import Product, Transaction, User from dibbler.models import Product, Transaction, User
@@ -302,29 +304,33 @@ def test_user_balance_penalty_interest_combined(sql_session: Session) -> None:
assert user_balance(sql_session, user) == (27 - 200 - math.ceil(27 * 2 * 1.1)) assert user_balance(sql_session, user) == (27 - 200 - math.ceil(27 * 2 * 1.1))
def test_user_balance_joint_transactions(sql_session: Session): @pytest.mark.skip(reason="Not yet implemented")
pass def test_user_balance_joint_transactions(sql_session: Session): ...
def test_user_balance_joint_transactions_interest(sql_session: Session): @pytest.mark.skip(reason="Not yet implemented")
pass def test_user_balance_joint_transactions_interest(sql_session: Session): ...
def test_user_balance_joint_transactions_changing_interest(sql_session: Session): @pytest.mark.skip(reason="Not yet implemented")
pass def test_user_balance_joint_transactions_changing_interest(sql_session: Session): ...
def test_user_balance_joint_transactions_penalty(sql_session: Session): @pytest.mark.skip(reason="Not yet implemented")
pass def test_user_balance_joint_transactions_penalty(sql_session: Session): ...
def test_user_balance_joint_transactions_changing_penalty(sql_session: Session): @pytest.mark.skip(reason="Not yet implemented")
pass def test_user_balance_joint_transactions_changing_penalty(sql_session: Session): ...
def test_user_balance_joint_transactions_penalty_interest_combined(sql_session: Session): @pytest.mark.skip(reason="Not yet implemented")
pass def test_user_balance_joint_transactions_penalty_interest_combined(sql_session: Session): ...
def test_user_balance_throw_away_products(sql_session: Session): @pytest.mark.skip(reason="Not yet implemented")
pass def test_user_balance_until(sql_session: Session): ...
@pytest.mark.skip(reason="Not yet implemented")
def test_user_balance_throw_away_products(sql_session: Session): ...
Generated
+11
View File
@@ -262,6 +262,7 @@ test = [
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-cov" }, { name = "pytest-cov" },
{ name = "pytest-html" }, { name = "pytest-html" },
{ name = "sqlparse" },
] ]
[package.metadata] [package.metadata]
@@ -279,6 +280,7 @@ test = [
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-cov" }, { name = "pytest-cov" },
{ name = "pytest-html", specifier = ">=4.1.1" }, { name = "pytest-html", specifier = ">=4.1.1" },
{ name = "sqlparse", specifier = ">=0.5.4" },
] ]
[[package]] [[package]]
@@ -1038,6 +1040,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718, upload-time = "2025-10-10T15:29:45.32Z" }, { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718, upload-time = "2025-10-10T15:29:45.32Z" },
] ]
[[package]]
name = "sqlparse"
version = "0.5.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/18/67/701f86b28d63b2086de47c942eccf8ca2208b3be69715a1119a4e384415a/sqlparse-0.5.4.tar.gz", hash = "sha256:4396a7d3cf1cd679c1be976cf3dc6e0a51d0111e87787e7a8d780e7d5a998f9e", size = 120112, upload-time = "2025-11-28T07:10:18.377Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/25/70/001ee337f7aa888fb2e3f5fd7592a6afc5283adb1ed44ce8df5764070f22/sqlparse-0.5.4-py3-none-any.whl", hash = "sha256:99a9f0314977b76d776a0fcb8554de91b9bb8a18560631d6bc48721d07023dcb", size = 45933, upload-time = "2025-11-28T07:10:19.73Z" },
]
[[package]] [[package]]
name = "tomli" name = "tomli"
version = "2.3.0" version = "2.3.0"