# tests/integration/tasks/test_loyalty_tasks.py """ Integration tests for Loyalty background tasks. Tests cover: - Point expiration task - Wallet sync task """ from datetime import UTC, datetime, timedelta import pytest from app.modules.loyalty.models import LoyaltyCard, LoyaltyTransaction from app.modules.loyalty.models.loyalty_transaction import TransactionType from app.modules.loyalty.tasks.point_expiration import ( _expire_points_for_program, _process_point_expiration, ) @pytest.mark.integration @pytest.mark.task class TestPointExpirationTask: """Tests for point expiration background task.""" def test_expire_points_for_inactive_card( self, db, test_loyalty_program, test_loyalty_card_inactive ): """Test that points expire for inactive cards.""" initial_balance = test_loyalty_card_inactive.points_balance assert initial_balance > 0 # Run expiration for the program cards_processed, points_expired = _expire_points_for_program( db, test_loyalty_program ) db.commit() # Refresh the card db.refresh(test_loyalty_card_inactive) assert cards_processed == 1 assert points_expired == initial_balance assert test_loyalty_card_inactive.points_balance == 0 def test_expire_points_creates_transaction( self, db, test_loyalty_program, test_loyalty_card_inactive ): """Test that expiration creates a transaction record.""" initial_balance = test_loyalty_card_inactive.points_balance _expire_points_for_program(db, test_loyalty_program) db.commit() # Check for expiration transaction transaction = ( db.query(LoyaltyTransaction) .filter( LoyaltyTransaction.card_id == test_loyalty_card_inactive.id, LoyaltyTransaction.transaction_type == TransactionType.POINTS_EXPIRED.value, ) .first() ) assert transaction is not None assert transaction.points_delta == -initial_balance assert transaction.balance_after == 0 assert "expired" in transaction.notes.lower() def test_no_expiration_for_active_cards( self, db, test_loyalty_program, test_loyalty_card ): """Test that active cards are not expired.""" # Ensure card has recent activity test_loyalty_card.last_activity_at = datetime.now(UTC) db.commit() initial_balance = test_loyalty_card.points_balance cards_processed, points_expired = _expire_points_for_program( db, test_loyalty_program ) db.commit() db.refresh(test_loyalty_card) # Active card should not be affected assert test_loyalty_card.points_balance == initial_balance def test_no_expiration_for_zero_balance_cards( self, db, test_loyalty_program, test_loyalty_card_inactive ): """Test that cards with zero balance are not processed.""" test_loyalty_card_inactive.points_balance = 0 db.commit() cards_processed, points_expired = _expire_points_for_program( db, test_loyalty_program ) db.commit() assert cards_processed == 0 assert points_expired == 0 def test_no_expiration_when_not_configured( self, db, test_loyalty_program_no_expiration ): """Test that no expiration occurs when not configured.""" # Create a card with old activity for this program card = LoyaltyCard( company_id=test_loyalty_program_no_expiration.company_id, program_id=test_loyalty_program_no_expiration.id, card_number="NO-EXPIRY-CARD", customer_email="noexpiry@test.com", points_balance=1000, is_active=True, last_activity_at=datetime.now(UTC) - timedelta(days=1000), ) db.add(card) db.commit() cards_processed, points_expired = _expire_points_for_program( db, test_loyalty_program_no_expiration ) db.commit() db.refresh(card) # Should not expire because program has no expiration configured assert cards_processed == 0 assert points_expired == 0 assert card.points_balance == 1000 def test_process_all_programs(self, db, test_loyalty_program, test_loyalty_card_inactive): """Test processing all programs.""" result = _process_point_expiration(db) db.commit() assert result["status"] == "success" assert result["programs_processed"] >= 1 def test_expiration_updates_voided_total( self, db, test_loyalty_program, test_loyalty_card_inactive ): """Test that expiration updates total_points_voided.""" initial_balance = test_loyalty_card_inactive.points_balance initial_voided = test_loyalty_card_inactive.total_points_voided or 0 _expire_points_for_program(db, test_loyalty_program) db.commit() db.refresh(test_loyalty_card_inactive) assert test_loyalty_card_inactive.total_points_voided == initial_voided + initial_balance