"""Tests for BaseMixin.""" import pytest from sqlalchemy import select from sqlalchemy.orm import Session from abacus_models.core.errors import ERROR_ENTITY_DOES_NOT_EXIST from tests.conftest import ( TestModelCompositeKey, TestModelFull, TestModelMinimal, TestModelWithDefaultFilter, TestModelWithDefaultOrder, ) class TestGetById: """Tests for BaseMixin.get_by_id classmethod.""" def test_get_by_id_returns_object(self, session, sample_full_model): """Test get_by_id returns the correct object.""" result = TestModelFull.get_by_id(session, sample_full_model.id) assert result is not None assert result.id == sample_full_model.id assert result.name == sample_full_model.name def test_get_by_id_none_id_returns_none(self, session): """Test get_by_id returns None when ID is None.""" result = TestModelFull.get_by_id(session, None) assert result is None def test_get_by_id_nonexistent_returns_none(self, session): """Test get_by_id returns None for non-existent ID.""" result = TestModelFull.get_by_id(session, 99999) assert result is None def test_get_by_id_with_connection(self, connection, user_context): """Test get_by_id works with Connection executor.""" # Connection.get() doesn't exist in SQLAlchemy 2.0 # This test verifies the Executor type alias accepts Connection # but actual get_by_id requires Session for the .get() method # Create a Session from connection to test with Session(connection) as session: model = TestModelFull(name='Connection Test') session.add(model) session.flush() model_id = model.id # Test get_by_id with session (which is what works) result = TestModelFull.get_by_id(session, model_id) assert result is not None assert result.name == 'Connection Test' def test_get_by_id_minimal_model(self, session, sample_minimal_model): """Test get_by_id works with minimal model.""" result = TestModelMinimal.get_by_id(session, sample_minimal_model.id) assert result is not None assert result.id == sample_minimal_model.id class TestGetByIdOrError: """Tests for BaseMixin.get_by_id_or_error classmethod.""" def test_get_by_id_or_error_returns_object(self, session, sample_full_model): """Test get_by_id_or_error returns the correct object.""" result = TestModelFull.get_by_id_or_error(session, sample_full_model.id) assert result is not None assert result.id == sample_full_model.id def test_get_by_id_or_error_raises_on_none_id(self, session): """Test get_by_id_or_error raises ValueError when ID is None.""" with pytest.raises(ValueError) as exc_info: TestModelFull.get_by_id_or_error(session, None) error_msg = str(exc_info.value) assert 'TestModelFull' in error_msg assert 'does not exist' in error_msg def test_get_by_id_or_error_raises_on_nonexistent(self, session): """Test get_by_id_or_error raises ValueError for non-existent ID.""" with pytest.raises(ValueError) as exc_info: TestModelFull.get_by_id_or_error(session, 99999) error_msg = str(exc_info.value) assert 'TestModelFull' in error_msg assert '99999' in error_msg def test_get_by_id_or_error_message_format(self, session): """Test get_by_id_or_error error message follows expected format.""" with pytest.raises(ValueError) as exc_info: TestModelFull.get_by_id_or_error(session, 12345) expected = ERROR_ENTITY_DOES_NOT_EXIST.format( object_type='TestModelFull', object_id=12345 ) assert str(exc_info.value) == expected class TestGetClassName: """Tests for BaseMixin.get_class_name classmethod.""" def test_get_class_name_returns_name(self): """Test get_class_name returns the class name.""" assert TestModelFull.get_class_name() == 'TestModelFull' assert TestModelMinimal.get_class_name() == 'TestModelMinimal' assert TestModelCompositeKey.get_class_name() == 'TestModelCompositeKey' def test_get_class_name_on_instance(self, sample_full_model): """Test get_class_name can be called on instance.""" assert sample_full_model.get_class_name() == 'TestModelFull' class TestRepr: """Tests for BaseMixin.__repr__ method.""" def test_repr_single_pk(self, session, sample_full_model): """Test __repr__ with single primary key.""" repr_str = repr(sample_full_model) assert repr_str.startswith('') def test_repr_composite_pk(self, session): """Test __repr__ with composite primary key.""" model = TestModelCompositeKey(id1=1, id2=2, name='Test') session.add(model) session.commit() repr_str = repr(model) assert repr_str.startswith('') def test_repr_minimal_model(self, sample_minimal_model): """Test __repr__ with minimal model.""" repr_str = repr(sample_minimal_model) assert repr_str.startswith('= 2 ) stmt = TestModelWithDefaultOrder.default_order(base_stmt) results = session.execute(stmt).scalars().all() # Should only get priority >= 2, ordered by priority desc, name asc assert len(results) == 2 assert results[0].name == 'Apple' assert results[1].name == 'Mango' def test_default_order_with_none_returns_ordered(self, session): """Test default_order with None stmt parameter.""" # Create test data model1 = TestModelWithDefaultOrder(name='Zebra', priority=5) model2 = TestModelWithDefaultOrder(name='Apple', priority=10) session.add_all([model1, model2]) session.commit() # Explicitly pass None (should behave same as no argument) stmt = TestModelWithDefaultOrder.default_order(None) results = session.execute(stmt).scalars().all() assert len(results) == 2 # Priority desc assert results[0] == model2 assert results[1] == model1 def test_default_order_preserves_filters(self, session): """Test default_order preserves filters from existing statement.""" # Create test data model1 = TestModelWithDefaultOrder(name='Apple', priority=1) model2 = TestModelWithDefaultOrder(name='Zebra', priority=5) model3 = TestModelWithDefaultOrder(name='Mango', priority=10) session.add_all([model1, model2, model3]) session.commit() # Create statement with where clause base_stmt = select(TestModelWithDefaultOrder).where( TestModelWithDefaultOrder.priority > 1 ) stmt = TestModelWithDefaultOrder.default_order(base_stmt) results = session.execute(stmt).scalars().all() # Should only get priority > 1, ordered correctly assert len(results) == 2 assert results[0].name == 'Mango' # priority 10 assert results[1].name == 'Zebra' # priority 5 def test_default_order_can_combine_with_default_filter(self, session): """Test that default_order can be combined with default_filter.""" # Create test data active1 = TestModelWithDefaultFilter(name='Zebra', is_active=1) active2 = TestModelWithDefaultFilter(name='Apple', is_active=1) inactive = TestModelWithDefaultFilter(name='Banana', is_active=0) session.add_all([active1, active2, inactive]) session.commit() # Combine filter and order stmt = TestModelWithDefaultFilter.default_filter() stmt = stmt.order_by(TestModelWithDefaultFilter.name.asc()) results = session.execute(stmt).scalars().all() # Should get only active, ordered by name assert len(results) == 2 assert results[0].name == 'Apple' assert results[1].name == 'Zebra'