"""Tests for CreateMixin.""" from datetime import datetime import pytest from abacus_models.core import constants from abacus_models.core.contexts import ctx_user_id, set_user_context from abacus_models.core.mixins.create_mixin import ( disable_on_create, enable_on_create, ) from abacus_models.core.utils import current_timestamp from tests.conftest import TestModelCreateOnly, TestModelFull class TestCreateMixinAutomatic: """Tests for automatic creation field tracking.""" def test_create_sets_created_at(self, session, user_context): """Test creating a model automatically sets created_at.""" before = current_timestamp() model = TestModelFull(name='Test') session.add(model) session.flush() after = current_timestamp() assert model.created_at is not None # Check timestamp is recent (within the test execution window) assert before <= model.created_at <= after def test_create_sets_created_by(self, session, user_context): """Test creating a model automatically sets created_by.""" model = TestModelFull(name='Test') session.add(model) session.flush() assert model.created_by == 'test_user_123' def test_create_sets_both_fields(self, session, user_context): """Test creating a model sets both created_at and created_by.""" model = TestModelFull(name='Test') session.add(model) session.flush() assert model.created_at is not None assert model.created_by == 'test_user_123' def test_create_does_not_override_created_at(self, session, user_context): """Test create does not override explicitly set created_at.""" custom_time = datetime(2020, 1, 1, 0, 0, 0) model = TestModelFull(name='Test', created_at=custom_time) session.add(model) session.flush() assert model.created_at == custom_time def test_create_does_not_override_created_by(self, session, user_context): """Test create does not override explicitly set created_by.""" model = TestModelFull(name='Test', created_by='custom_user') session.add(model) session.flush() assert model.created_by == 'custom_user' def test_create_does_not_override(self, session, user_context): """Test create does not override explicitly set created_by.""" custom_time = datetime(2020, 1, 1, 0, 0, 0) model = TestModelFull( name='Test', created_at=custom_time, created_by='custom_user' ) session.add(model) session.flush() assert model.created_at == custom_time assert model.created_by == 'custom_user' def test_create_multiple_instances(self, session, user_context): """Test creating multiple instances tracks each separately.""" model1 = TestModelFull(name='Test 1') model2 = TestModelFull(name='Test 2') session.add(model1) session.flush() time1 = model1.created_at session.add(model2) session.flush() time2 = model2.created_at assert model1.created_at is not None assert model2.created_at is not None assert model1.created_by == 'test_user_123' assert model2.created_by == 'test_user_123' # Times should be close but model2 should be equal or after model1 assert time2 >= time1 def test_create_with_different_users(self, session): """Test creation tracking with different users.""" with set_user_context('user_1'): model1 = TestModelFull(name='Test 1') session.add(model1) session.flush() with set_user_context('user_2'): model2 = TestModelFull(name='Test 2') session.add(model2) session.flush() assert model1.created_by == 'user_1' assert model2.created_by == 'user_2' def test_create_only_model(self, session, user_context): """Test CreateMixin works with model having only create fields.""" model = TestModelCreateOnly(name='Test') session.add(model) session.flush() assert model.created_at is not None assert model.created_by == 'test_user_123' class TestCreateMixinEnableDisable: """Tests for enable/disable functionality.""" def test_disable_on_create_prevents_auto_tracking(self, session, user_context): """Test disable_on_create prevents automatic field population.""" try: disable_on_create() model = TestModelFull(name='Test') session.add(model) session.flush() # Fields should not be set automatically assert model.created_at is None assert model.created_by is None finally: # Re-enable for other tests enable_on_create() def test_enable_on_create_restores_auto_tracking(self, session, user_context): """Test enable_on_create restores automatic field population.""" try: disable_on_create() enable_on_create() model = TestModelFull(name='Test') session.add(model) session.flush() # Fields should be set automatically again assert model.created_at is not None assert model.created_by == 'test_user_123' finally: # Ensure enabled for other tests enable_on_create() def test_disable_enable_cycle(self, session, user_context): """Test multiple disable/enable cycles.""" try: # First create - should work model1 = TestModelFull(name='Test 1') session.add(model1) session.flush() assert model1.created_at is not None # Disable disable_on_create() model2 = TestModelFull(name='Test 2') session.add(model2) session.flush() assert model2.created_at is None # Re-enable enable_on_create() model3 = TestModelFull(name='Test 3') session.add(model3) session.flush() assert model3.created_at is not None finally: # Ensure enabled for other tests enable_on_create() class TestCreateMixinEdgeCases: """Tests for edge cases and error handling.""" def test_create_without_user_context(self, session): """Test creating a model without user context.""" # Without user context and with strict validation, should raise # Ensure strict mode is enabled original_strict = constants.STRICT_CONTEXT_VALIDATION constants.STRICT_CONTEXT_VALIDATION = True try: ctx_user_id.set(None) with pytest.raises(ValueError, match='context user_id is not set'): model = TestModelFull(name='Test') session.add(model) session.flush() finally: constants.STRICT_CONTEXT_VALIDATION = original_strict def test_create_without_user_context_non_strict(self, session): """Test creating with non-strict context allows None user.""" # Temporarily disable strict validation original_strict = constants.STRICT_CONTEXT_VALIDATION constants.STRICT_CONTEXT_VALIDATION = False try: ctx_user_id.set(None) model = TestModelFull(name='Test') session.add(model) session.flush() # created_at should be set, created_by should be None assert model.created_at is not None assert model.created_by is None finally: constants.STRICT_CONTEXT_VALIDATION = original_strict def test_create_updates_only_on_insert(self, session, user_context): """Test create fields are only set on insert, not update.""" # Create model model = TestModelFull(name='Test') session.add(model) session.flush() original_created_at = model.created_at original_created_by = model.created_by # Update model with different user with set_user_context('different_user'): model.name = 'Updated' session.flush() # created_at and created_by should not change assert model.created_at == original_created_at assert model.created_by == original_created_by assert model.created_by != 'different_user'