from datetime import UTC, datetime import pytest from sqlalchemy import select from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import Session from src.ingest.b4w import change_category, retire_category, upsert_category from src.models import Category from src.models.b4w import CategoryDb def test_upsert_category_returns_orm_object(db_session: Session): result = upsert_category( db_session, Category.from_str("dummy_parent_1:dummy_parent_2:dummy"), do_commit=False, ) assert result assert isinstance(result, CategoryDb) assert str(result.name) == "dummy" assert str(result.parent.name) == "dummy_parent_2" assert str(result.parent.parent.name) == "dummy_parent_1" assert result.valid_from is None assert result.valid_to is None assert result.last_action == "created" assert all( CategoryDb.__table__.c[name].info.get("description") for name in ("valid_from", "valid_to", "last_action") ) def test_upsert_category_writes_new_to_db(db_session: Session): upsert_category( db_session, Category.from_str("grand_father:father:new_category"), do_commit=True, ) result = db_session.execute( select(CategoryDb).filter_by(name="new_category") ).scalar_one() assert result assert str(result.name) == "new_category" assert str(result.parent.name) == "father" assert str(result.parent.parent.name) == "grand_father" def test_upsert_category_skips_existing(db_session: Session): for _ in range(3): upsert_category( db_session, Category.from_str("grand_father:father:try_again_and_again"), do_commit=True, ) result = db_session.execute( select(CategoryDb).filter_by(name="try_again_and_again") ).all() assert len(result) == 1 def test_change_category_closes_old_version_and_starts_new_one( db_session: Session, ): original = upsert_category( db_session, Category.from_str("old_category"), do_commit=False ) assert original is not None effective_at = datetime(2026, 10, 16, 12, tzinfo=UTC) replacement = change_category( db_session, original.id, Category.from_str("new_category"), effective_at=effective_at, do_commit=False, ) assert original.valid_from is None assert original.valid_to == effective_at assert original.last_action == "changed" assert replacement.name == "new_category" assert replacement.valid_from == effective_at assert replacement.valid_to is None assert replacement.last_action == "changed" db_session.expire(original) db_session.expire(replacement) assert original.valid_to == effective_at assert replacement.valid_from == effective_at # A historical name can be introduced again as a new active version. reintroduced = upsert_category( db_session, Category.from_str("old_category"), do_commit=False ) assert reintroduced is not None assert reintroduced.id != original.id assert reintroduced.valid_from is not None assert reintroduced.valid_from > original.valid_to assert reintroduced.last_action == "changed" def test_retire_category_closes_active_version(db_session: Session): category = upsert_category( db_session, Category.from_str("category_to_retire"), do_commit=False ) assert category is not None effective_at = datetime(2026, 10, 16, 12, tzinfo=UTC) retired = retire_category( db_session, category.id, effective_at=effective_at, do_commit=False ) assert retired.valid_to == effective_at assert retired.last_action == "retired" assert ( db_session.query(CategoryDb) .filter_by(name="category_to_retire", valid_to=None) .one_or_none() is None ) def test_active_root_categories_are_unique(db_session: Session): db_session.add_all( [ CategoryDb(name="duplicate_root"), CategoryDb(name="duplicate_root"), ] ) with pytest.raises(IntegrityError): db_session.flush()