"""Tests for models base module.""" import datetime from unittest.mock import patch import pytest from flask import g from sqlalchemy import select from abacus_common_logic.constants import error from abacus_common_logic.constants.constants import DEFAULT_USER_ID from abacus_common_logic.models import base from tests.utils.db_test_models import ( ModelForTests, SoftDeletableModel, clear_test_model, create_test_object, ) def test_base_has_created_and_modified(test_app): """Test that object has inherited fields.""" test_object = create_test_object(name='test-1') assert test_object.name == 'test-1' assert isinstance(test_object.created_at, datetime.datetime) assert isinstance(test_object.last_modified, datetime.datetime) assert test_object.created_at == test_object.last_modified assert test_object.created_by == DEFAULT_USER_ID assert test_object.last_modified_by == DEFAULT_USER_ID def test_crudmixin_find_by_name(test_app): """Test mixin's find_by_name method.""" clear_test_model() # success create_test_object(name='mixin-test-1') result = ModelForTests.find_by_name('mixin-test-1') assert result is not None assert result.name == 'mixin-test-1' # fail result = ModelForTests.find_by_name('mixin-test-2') assert result is None def test_crudmixin_get_by_id(test_app): """Test mixin's get_by_id method.""" clear_test_model() # success test_object = create_test_object(name='test-by-id') found_object = ModelForTests.get_by_id(1) assert test_object == found_object # fail another_object = ModelForTests.get_by_id(2) assert another_object is None @patch('abacus_common_logic.models.base.abort') def test_crudmixin_get_by_id_or_error(abort_mock, test_app): """Test mixin's get_by_id_or_error method.""" clear_test_model() # success test_object = create_test_object(name='test-by-id') found_object = ModelForTests.get_by_id_or_error(1) assert test_object == found_object # fail another_object = ModelForTests.get_by_id_or_error(2) assert another_object is None abort_mock.assert_called_once_with( code=400, description='ModelForTests 2 does not exist.' ) @patch('abacus_common_logic.models.base.abort') def test_crudmixin_delete_by_id_or_error(abort_mock, test_app): """Test mixin's get_by_id_or_error method.""" clear_test_model() # success create_test_object(name='test-delete-by-id-1') ModelForTests.delete_by_id_or_error(1) assert not abort_mock.called # fail create_test_object(name='test-by-id-1') abort_mock.side_effect = AttributeError with pytest.raises(AttributeError): ModelForTests.delete_by_id_or_error(2) abort_mock.assert_called_once_with( code=400, description='ModelForTests 2 does not exist.' ) def test_crudmixin_filter_for(test_app): """Test mixin's filter_for method.""" clear_test_model() create_test_object(name='test-1') create_test_object(name='test-1') create_test_object(name='test-1') create_test_object(name='other-2') create_test_object(name='other-2') assert ModelForTests.count() == 5 test_1_filter = ModelForTests.filter_for(query='test-1') other_2_filter = ModelForTests.filter_for(query='other-2') test_1_objects = ( base.db.session.execute(select(ModelForTests).filter(*test_1_filter)) .scalars() .all() ) other_2_objects = ( base.db.session.execute(select(ModelForTests).filter(*other_2_filter)) .scalars() .all() ) assert len(test_1_objects) == 3 assert len(other_2_objects) == 2 def test_crudmixin_count(test_app): """Test mixin's count method.""" clear_test_model() create_test_object(name='test-1') create_test_object(name='test-1') create_test_object(name='other-2') assert ModelForTests.count('test-1') == 2 def test_crudmixin_create(test_app): """Test mixin's create method.""" clear_test_model() new_object = ModelForTests.create(name='awesome-object') assert isinstance(new_object.id, int) base.db.session.rollback() assert ModelForTests.count() == 1 def test_crudmixin_build(test_app): """Test mixin's build method.""" clear_test_model() ModelForTests.build(name='awesome-object') base.db.session.rollback() assert ModelForTests.count() == 0 def test_crudmixin_update_attributes(test_app): """Test mixin's update_attributes method.""" clear_test_model() test_object = create_test_object(name='my-test-object') assert test_object.created_by == DEFAULT_USER_ID assert test_object.last_modified_by == DEFAULT_USER_ID setattr(g, 'user_details', {'id': 'test_admin'}) test_object.update_attributes(name='some-new-object-name') setattr(g, 'user_details', {'id': DEFAULT_USER_ID}) assert test_object.name == 'some-new-object-name' assert test_object.created_by == DEFAULT_USER_ID assert test_object.last_modified_by == 'test_admin' @patch('abacus_common_logic.models.base.abort') @patch('abacus_common_logic.models.base.get_flask_user_id') def test_crudmixin_soft_delete(get_userid_mock, abort_mock, test_app): """Test mixin's soft_delete method.""" get_userid_mock.return_value = 'some-userid' clear_test_model() test_object = SoftDeletableModel.create(name='soft-deletable') assert test_object.deleted_at is None assert test_object.deleted_by is None SoftDeletableModel.delete_by_id_or_error(test_object.id, soft_delete=True) assert isinstance(test_object.deleted_at, datetime.datetime) assert test_object.deleted_by == 'some-userid' another_object = ModelForTests.create(name='non-soft-deletable') abort_mock.side_effect = AttributeError with pytest.raises(AttributeError): ModelForTests.delete_by_id_or_error(another_object.id, soft_delete=True) abort_mock.assert_called_once_with( description=error.ERROR_SOFT_DELETE_UNAVAILABLE, code=400 )