Category overhaul #20
+9
-1
@@ -87,13 +87,19 @@ def populated_db_b4w(
|
|||||||
Only the first-layer ingestion table is filled; no further processing.
|
Only the first-layer ingestion table is filled; no further processing.
|
||||||
``**kwargs`` are forwarded to ``B4wExportFactory.build``; callable values
|
``**kwargs`` are forwarded to ``B4wExportFactory.build``; callable values
|
||||||
are resolved per row (e.g. ``ValDt=lambda: faker.date_between(...)``).
|
are resolved per row (e.g. ``ValDt=lambda: faker.date_between(...)``).
|
||||||
|
An optional ``setup`` callback runs after database creation and before rows
|
||||||
|
are inserted.
|
||||||
``Id1`` is reserved: it is set per row (``1..row_count``) and cannot be
|
``Id1`` is reserved: it is set per row (``1..row_count``) and cannot be
|
||||||
overridden via ``**kwargs``.
|
overridden via ``**kwargs``.
|
||||||
The returned session is *not* closed by the fixture; close it in the test.
|
The returned session is *not* closed by the fixture; close it in the test.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def populate_db_b4w(
|
def populate_db_b4w(
|
||||||
name: str, row_count: int = DEFAULT_B4W_ROWS, **kwargs: Any
|
name: str,
|
||||||
|
row_count: int = DEFAULT_B4W_ROWS,
|
||||||
|
*,
|
||||||
|
setup: Callable[[Session], None] | None = None,
|
||||||
|
**kwargs: Any,
|
||||||
) -> Session:
|
) -> Session:
|
||||||
if "Id1" in kwargs:
|
if "Id1" in kwargs:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
@@ -101,6 +107,8 @@ def populated_db_b4w(
|
|||||||
"cannot be overridden via **kwargs"
|
"cannot be overridden via **kwargs"
|
||||||
)
|
)
|
||||||
session = db_session_factory(name)
|
session = db_session_factory(name)
|
||||||
|
if setup is not None:
|
||||||
|
setup(session)
|
||||||
for i in range(row_count):
|
for i in range(row_count):
|
||||||
row_kwargs = {k: (v() if callable(v) else v) for k, v in kwargs.items()}
|
row_kwargs = {k: (v() if callable(v) else v) for k, v in kwargs.items()}
|
||||||
row = B4wExportFactory.build(Id1=i + 1, **cast(dict[str, Any], row_kwargs))
|
row = B4wExportFactory.build(Id1=i + 1, **cast(dict[str, Any], row_kwargs))
|
||||||
|
|||||||
+29
-1
@@ -1,4 +1,5 @@
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from faker import Faker
|
from faker import Faker
|
||||||
@@ -6,10 +7,37 @@ from polyfactory.factories.pydantic_factory import ModelFactory
|
|||||||
from polyfactory.fields import Use
|
from polyfactory.fields import Use
|
||||||
|
|
||||||
from src.models.b4w import B4wExport
|
from src.models.b4w import B4wExport
|
||||||
|
from src.models.category import Category
|
||||||
from src.models.comdirect import ComdirectCreditCard, ComdirectDepot, ComdirectGiro
|
from src.models.comdirect import ComdirectCreditCard, ComdirectDepot, ComdirectGiro
|
||||||
from src.models.transactions import Transaction
|
from src.models.transactions import Transaction
|
||||||
|
|
||||||
DEFAULT_CATEGORY = "root_category:parent_category:leaf_category"
|
CATEGORY_PATHS = tuple(
|
||||||
|
line.strip()
|
||||||
|
for line in (
|
||||||
|
Path(__file__).resolve().parents[1]
|
||||||
|
/ "scripts/initial_load/categories/categories.txt"
|
||||||
|
).read_text(encoding="utf-8").splitlines()
|
||||||
|
if line.strip()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _category_from_path(path: str) -> Category:
|
||||||
|
category = Category.from_str(path)
|
||||||
|
if category is None:
|
||||||
|
raise ValueError("Category list contains an empty path")
|
||||||
|
return category
|
||||||
|
|
||||||
|
|
||||||
|
class CategoryFactory:
|
||||||
|
__faker__ = Faker("de_DE")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def build(cls) -> Category:
|
||||||
|
return _category_from_path(cls.__faker__.random_element(CATEGORY_PATHS))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_all() -> list[Category]:
|
||||||
|
return [_category_from_path(path) for path in CATEGORY_PATHS]
|
||||||
|
|
||||||
|
|
||||||
def _faker_or_none(faker: Faker, non_default: Any, default: Any = None, p_value=0.5):
|
def _faker_or_none(faker: Faker, non_default: Any, default: Any = None, p_value=0.5):
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from sqlalchemy import inspect
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from src.models.category import CategoryBudgetDb, CategoryDb
|
from src.models.category import CategoryBudgetDb, CategoryDb
|
||||||
|
from tests.factories import CategoryFactory
|
||||||
|
|
||||||
|
|
||||||
def test_category_budget_schema(db_session: Session):
|
def test_category_budget_schema(db_session: Session):
|
||||||
@@ -32,7 +33,7 @@ def test_category_budget_schema(db_session: Session):
|
|||||||
|
|
||||||
|
|
||||||
def test_category_budget_defaults_null_dates(db_session: Session):
|
def test_category_budget_defaults_null_dates(db_session: Session):
|
||||||
category = CategoryDb(name="Lebenshaltung")
|
category = CategoryDb(name=CategoryFactory.build().name)
|
||||||
db_session.add(category)
|
db_session.add(category)
|
||||||
db_session.flush()
|
db_session.flush()
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ def test_category_budget_defaults_null_dates(db_session: Session):
|
|||||||
|
|
||||||
|
|
||||||
def test_category_budget_date_window(db_session: Session):
|
def test_category_budget_date_window(db_session: Session):
|
||||||
category = CategoryDb(name="Freizeit")
|
category = CategoryDb(name=CategoryFactory.build().name)
|
||||||
db_session.add(category)
|
db_session.add(category)
|
||||||
db_session.flush()
|
db_session.flush()
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ def test_category_budget_date_window(db_session: Session):
|
|||||||
|
|
||||||
|
|
||||||
def test_category_budget_relationship_both_ways(db_session: Session):
|
def test_category_budget_relationship_both_ways(db_session: Session):
|
||||||
category = CategoryDb(name="Beruf")
|
category = CategoryDb(name=CategoryFactory.build().name)
|
||||||
db_session.add(category)
|
db_session.add(category)
|
||||||
db_session.flush()
|
db_session.flush()
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ def test_category_budget_relationship_both_ways(db_session: Session):
|
|||||||
|
|
||||||
|
|
||||||
def test_category_has_multiple_budgets(db_session: Session):
|
def test_category_has_multiple_budgets(db_session: Session):
|
||||||
category = CategoryDb(name="Wohnen")
|
category = CategoryDb(name=CategoryFactory.build().name)
|
||||||
db_session.add(category)
|
db_session.add(category)
|
||||||
db_session.flush()
|
db_session.flush()
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from datetime import date
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from src.models.category import Category, CategoryBudget
|
from src.models.category import Category, CategoryBudget
|
||||||
|
from tests.factories import CategoryFactory
|
||||||
|
|
||||||
|
|
||||||
class TestCategoryComputedFields:
|
class TestCategoryComputedFields:
|
||||||
@@ -75,7 +76,7 @@ class TestCategoryBudgetValidation:
|
|||||||
|
|
||||||
def test_valid_budget_no_dates(self):
|
def test_valid_budget_no_dates(self):
|
||||||
"""Budget with None dates is valid."""
|
"""Budget with None dates is valid."""
|
||||||
cat = Category.from_str("test")
|
cat = CategoryFactory.build()
|
||||||
budget = CategoryBudget(
|
budget = CategoryBudget(
|
||||||
category=cat,
|
category=cat,
|
||||||
amount_eur=100.00,
|
amount_eur=100.00,
|
||||||
@@ -86,7 +87,7 @@ class TestCategoryBudgetValidation:
|
|||||||
def test_valid_budget_with_dates(self):
|
def test_valid_budget_with_dates(self):
|
||||||
"""Budget with valid date range is accepted."""
|
"""Budget with valid date range is accepted."""
|
||||||
|
|
||||||
cat = Category.from_str("test")
|
cat = CategoryFactory.build()
|
||||||
budget = CategoryBudget(
|
budget = CategoryBudget(
|
||||||
category=cat,
|
category=cat,
|
||||||
amount_eur=100.00,
|
amount_eur=100.00,
|
||||||
@@ -99,7 +100,7 @@ class TestCategoryBudgetValidation:
|
|||||||
def test_invalid_date_range_raises(self):
|
def test_invalid_date_range_raises(self):
|
||||||
"""Budget with valid_from > valid_to raises ValueError."""
|
"""Budget with valid_from > valid_to raises ValueError."""
|
||||||
|
|
||||||
cat = Category.from_str("test")
|
cat = CategoryFactory.build()
|
||||||
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
CategoryBudget(
|
CategoryBudget(
|
||||||
@@ -116,7 +117,7 @@ class TestCategoryBudgetValidation:
|
|||||||
def test_valid_budget_exact_boundary(self):
|
def test_valid_budget_exact_boundary(self):
|
||||||
"""Budget with exact same from and to is valid (single day)."""
|
"""Budget with exact same from and to is valid (single day)."""
|
||||||
|
|
||||||
cat = Category.from_str("test")
|
cat = CategoryFactory.build()
|
||||||
budget = CategoryBudget(
|
budget = CategoryBudget(
|
||||||
category=cat,
|
category=cat,
|
||||||
amount_eur=100.00,
|
amount_eur=100.00,
|
||||||
@@ -129,7 +130,7 @@ class TestCategoryBudgetValidation:
|
|||||||
def test_validation_preserves_original_values(self):
|
def test_validation_preserves_original_values(self):
|
||||||
"""Validation doesn't change the model's original values."""
|
"""Validation doesn't change the model's original values."""
|
||||||
|
|
||||||
cat = Category.from_str("test")
|
cat = CategoryFactory.build()
|
||||||
budget = CategoryBudget(
|
budget = CategoryBudget(
|
||||||
category=cat,
|
category=cat,
|
||||||
amount_eur=150.75,
|
amount_eur=150.75,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from sqlalchemy.orm import Session
|
|||||||
from src.domain_utils import find_category_db, set_category_budget
|
from src.domain_utils import find_category_db, set_category_budget
|
||||||
from src.models import Category, CategoryBudget, CategoryBudgetDb
|
from src.models import Category, CategoryBudget, CategoryBudgetDb
|
||||||
from src.models.category import upsert_category
|
from src.models.category import upsert_category
|
||||||
|
from tests.factories import CategoryFactory
|
||||||
|
|
||||||
# NOTE: MYPY errors suppressed for test fixtures - see AGENTS.md for type checking notes.
|
# NOTE: MYPY errors suppressed for test fixtures - see AGENTS.md for type checking notes.
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ class TestSetCategoryBudgetNoOverlap:
|
|||||||
|
|
||||||
def test_no_overlap_add_new(self, db_session: Session):
|
def test_no_overlap_add_new(self, db_session: Session):
|
||||||
"""When new budget has no overlap with existing, add both."""
|
"""When new budget has no overlap with existing, add both."""
|
||||||
cat = Category(name="NoOverlap")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ class TestSetCategoryBudgetNoOverlap:
|
|||||||
|
|
||||||
def test_completely_before_no_overlap(self, db_session: Session):
|
def test_completely_before_no_overlap(self, db_session: Session):
|
||||||
"""New budget completely before existing - should add both."""
|
"""New budget completely before existing - should add both."""
|
||||||
cat = Category(name="BeforeExisting")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -79,7 +80,7 @@ class TestSetCategoryBudgetFullOverlap:
|
|||||||
|
|
||||||
def test_full_overlap_replace(self, db_session: Session):
|
def test_full_overlap_replace(self, db_session: Session):
|
||||||
"""When new budget fully overlaps existing, replace entirely."""
|
"""When new budget fully overlaps existing, replace entirely."""
|
||||||
cat = Category(name="FullOverlap")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ class TestSetCategoryBudgetPartialOverlapStart:
|
|||||||
|
|
||||||
def test_partial_overlap_at_start(self, db_session: Session):
|
def test_partial_overlap_at_start(self, db_session: Session):
|
||||||
"""When new budget overlaps existing from the start, keep end and cut before."""
|
"""When new budget overlaps existing from the start, keep end and cut before."""
|
||||||
cat = Category(name="OverlapStart")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -150,7 +151,7 @@ class TestSetCategoryBudgetPartialOverlapEnd:
|
|||||||
|
|
||||||
def test_partial_overlap_at_end(self, db_session: Session):
|
def test_partial_overlap_at_end(self, db_session: Session):
|
||||||
"""When new budget overlaps existing at the end, keep start and cut after."""
|
"""When new budget overlaps existing at the end, keep start and cut after."""
|
||||||
cat = Category(name="OverlapEnd")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -186,7 +187,7 @@ class TestSetCategoryBudgetMiddleOverlap:
|
|||||||
|
|
||||||
def test_complex_overlap_middle(self, db_session: Session):
|
def test_complex_overlap_middle(self, db_session: Session):
|
||||||
"""Handle overlap in the middle that splits existing budget into two parts."""
|
"""Handle overlap in the middle that splits existing budget into two parts."""
|
||||||
cat = Category(name="SplitMiddle")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -223,7 +224,7 @@ class TestSetCategoryBudgetUnbounded:
|
|||||||
|
|
||||||
def test_unbounded_start(self, db_session: Session):
|
def test_unbounded_start(self, db_session: Session):
|
||||||
"""Budget with None valid_from (unbounded start)."""
|
"""Budget with None valid_from (unbounded start)."""
|
||||||
cat = Category(name="UnboundedStart")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -244,7 +245,7 @@ class TestSetCategoryBudgetUnbounded:
|
|||||||
|
|
||||||
def test_unbounded_end(self, db_session: Session):
|
def test_unbounded_end(self, db_session: Session):
|
||||||
"""Budget with None valid_to (unbounded end)."""
|
"""Budget with None valid_to (unbounded end)."""
|
||||||
cat = Category(name="UnboundedEnd")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -265,7 +266,7 @@ class TestSetCategoryBudgetUnbounded:
|
|||||||
|
|
||||||
def test_unbounded_both(self, db_session: Session):
|
def test_unbounded_both(self, db_session: Session):
|
||||||
"""Budget with both dates unbounded (None)."""
|
"""Budget with both dates unbounded (None)."""
|
||||||
cat = Category(name="UnboundedBoth")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -290,7 +291,7 @@ class TestSetCategoryBudgetWithExistingUnbounded:
|
|||||||
|
|
||||||
def test_add_bounded_after_unbounded(self, db_session: Session):
|
def test_add_bounded_after_unbounded(self, db_session: Session):
|
||||||
"""Add a bounded budget after an unbounded one (no overlap)."""
|
"""Add a bounded budget after an unbounded one (no overlap)."""
|
||||||
cat = Category(name="AfterUnbounded")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -319,7 +320,7 @@ class TestSetCategoryBudgetWithExistingUnbounded:
|
|||||||
|
|
||||||
def test_add_bounded_before_unbounded(self, db_session: Session):
|
def test_add_bounded_before_unbounded(self, db_session: Session):
|
||||||
"""Add a bounded budget before an unbounded one (no overlap)."""
|
"""Add a bounded budget before an unbounded one (no overlap)."""
|
||||||
cat = Category(name="BeforeUnbounded")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -352,7 +353,7 @@ class TestSetCategoryBudgetMultipleWithSameAmount:
|
|||||||
|
|
||||||
def test_same_range_different_amount(self, db_session: Session):
|
def test_same_range_different_amount(self, db_session: Session):
|
||||||
"""When two budgets have same range but different amounts, only one should remain."""
|
"""When two budgets have same range but different amounts, only one should remain."""
|
||||||
cat = Category(name="SameRangeDifferentAmount")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -386,7 +387,7 @@ class TestSetCategoryBudgetDateEdgeCases:
|
|||||||
|
|
||||||
def test_adjacent_budgets_no_overlap(self, db_session: Session):
|
def test_adjacent_budgets_no_overlap(self, db_session: Session):
|
||||||
"""Two budgets that are adjacent (end of one is start-1 of next) should both exist."""
|
"""Two budgets that are adjacent (end of one is start-1 of next) should both exist."""
|
||||||
cat = Category(name="Adjacent")
|
cat = CategoryFactory.build()
|
||||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@@ -497,7 +498,7 @@ class TestSetCategoryBudgetValidation:
|
|||||||
|
|
||||||
def test_valid_budget_no_dates(self):
|
def test_valid_budget_no_dates(self):
|
||||||
"""Budget with None dates is valid."""
|
"""Budget with None dates is valid."""
|
||||||
cat = Category.from_str("test")
|
cat = CategoryFactory.build()
|
||||||
budget = CategoryBudget(
|
budget = CategoryBudget(
|
||||||
category=cat,
|
category=cat,
|
||||||
amount_eur=100.00,
|
amount_eur=100.00,
|
||||||
@@ -507,7 +508,7 @@ class TestSetCategoryBudgetValidation:
|
|||||||
|
|
||||||
def test_valid_budget_with_dates(self):
|
def test_valid_budget_with_dates(self):
|
||||||
"""Budget with dates is valid."""
|
"""Budget with dates is valid."""
|
||||||
cat = Category.from_str("test")
|
cat = CategoryFactory.build()
|
||||||
budget = CategoryBudget(
|
budget = CategoryBudget(
|
||||||
category=cat,
|
category=cat,
|
||||||
amount_eur=100.00,
|
amount_eur=100.00,
|
||||||
@@ -519,7 +520,7 @@ class TestSetCategoryBudgetValidation:
|
|||||||
|
|
||||||
def test_invalid_date_range_raises(self):
|
def test_invalid_date_range_raises(self):
|
||||||
"""Budget with valid_from > valid_to should raise validation error."""
|
"""Budget with valid_from > valid_to should raise validation error."""
|
||||||
cat = Category.from_str("test")
|
cat = CategoryFactory.build()
|
||||||
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
CategoryBudget(
|
CategoryBudget(
|
||||||
@@ -533,7 +534,7 @@ class TestSetCategoryBudgetValidation:
|
|||||||
|
|
||||||
def test_amount_must_be_positive(self):
|
def test_amount_must_be_positive(self):
|
||||||
"""Amount must be greater than zero."""
|
"""Amount must be greater than zero."""
|
||||||
cat = Category.from_str("test")
|
cat = CategoryFactory.build()
|
||||||
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
CategoryBudget(
|
CategoryBudget(
|
||||||
|
|||||||
@@ -1,8 +1,17 @@
|
|||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
import faker
|
import faker
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from src.models.b4w import B4wDb
|
from src.models.b4w import B4wDb
|
||||||
|
from src.models.category import CategoryDb, upsert_category
|
||||||
|
from tests.factories import CategoryFactory
|
||||||
|
|
||||||
|
|
||||||
|
def _load_initial_categories(session: Session) -> None:
|
||||||
|
for category in CategoryFactory.build_all():
|
||||||
|
upsert_category(session, category, do_commit=False)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
def test_b4w_populate(populated_db_b4w):
|
def test_b4w_populate(populated_db_b4w):
|
||||||
@@ -10,10 +19,12 @@ def test_b4w_populate(populated_db_b4w):
|
|||||||
session = populated_db_b4w(
|
session = populated_db_b4w(
|
||||||
"b4w_basic",
|
"b4w_basic",
|
||||||
100,
|
100,
|
||||||
|
setup=_load_initial_categories,
|
||||||
ValDt=lambda: faker.Faker().date_between(val_dt_min, val_dt_max),
|
ValDt=lambda: faker.Faker().date_between(val_dt_min, val_dt_max),
|
||||||
)
|
)
|
||||||
assert session
|
assert session
|
||||||
assert session.query(B4wDb).count() == 100
|
assert session.query(B4wDb).count() == 100
|
||||||
|
assert session.query(CategoryDb).count() >= len(CategoryFactory.build_all())
|
||||||
# ``ValDt`` was given as a per-row callable: it must have been resolved
|
# ``ValDt`` was given as a per-row callable: it must have been resolved
|
||||||
# and persisted for every row.
|
# and persisted for every row.
|
||||||
dates = [d for (d,) in session.query(B4wDb.date_valid).all()]
|
dates = [d for (d,) in session.query(B4wDb.date_valid).all()]
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from src.ingest.transactions import (
|
|||||||
from src.models.b4w import B4wDb
|
from src.models.b4w import B4wDb
|
||||||
from src.models.comdirect import ComdirectGiroDb
|
from src.models.comdirect import ComdirectGiroDb
|
||||||
from src.models.transactions import Transaction, TransactionDb
|
from src.models.transactions import Transaction, TransactionDb
|
||||||
from tests.factories import B4wExportFactory
|
from tests.factories import B4wExportFactory, CategoryFactory
|
||||||
|
|
||||||
B4W_PATH = Path("tests/test_data/b4w_export_dummy_01.csv")
|
B4W_PATH = Path("tests/test_data/b4w_export_dummy_01.csv")
|
||||||
COMDIRECT_PATH = Path("tests/test_data/comdirect_export_dummy_01.txt")
|
COMDIRECT_PATH = Path("tests/test_data/comdirect_export_dummy_01.txt")
|
||||||
@@ -108,7 +108,7 @@ def test_consolidate_b4w_signed_amount_and_category(db_session: Session):
|
|||||||
export = B4wExportFactory.build(
|
export = B4wExportFactory.build(
|
||||||
Amt="50.00",
|
Amt="50.00",
|
||||||
RmtInf="Supermarket",
|
RmtInf="Supermarket",
|
||||||
Category="Lebenshaltung:Lebensmittel",
|
Category=CategoryFactory.build().to_str(),
|
||||||
CdtDbtInd="DBIT",
|
CdtDbtInd="DBIT",
|
||||||
)
|
)
|
||||||
staging = upsert_b4w(db_session, export, do_commit=False)
|
staging = upsert_b4w(db_session, export, do_commit=False)
|
||||||
|
|||||||
Reference in New Issue
Block a user