Files
orion/tests/integration/tasks/test_loyalty_tasks.py
Samir Boulahtit df784d718b test(loyalty): add comprehensive test suite for loyalty module
Add tests for the loyalty module Phase 2 implementation:

Fixtures (tests/fixtures/loyalty_fixtures.py):
- test_loyalty_program: Points-based program with rewards
- test_loyalty_program_no_expiration: Program without point expiry
- test_loyalty_card: Active customer card
- test_loyalty_card_inactive: Card for expiration testing
- test_loyalty_transaction: Sample transaction
- test_staff_pin: Staff PIN for verification tests

Unit Tests (tests/unit/services/test_loyalty_services.py):
- ProgramService: Company queries, listing, filtering
- CardService: Lookup, enrollment, balance operations
- PointsService: Earn, redeem, void, adjust operations
- PinService: Creation, verification, lockout

Integration Tests (tests/integration/api/v1/loyalty/):
- Vendor API: Program settings, card management, PIN operations
- Storefront API: Endpoint existence verification

Task Tests (tests/integration/tasks/test_loyalty_tasks.py):
- Point expiration for inactive cards
- Transaction record creation on expiration
- No expiration for active cards or zero balances
- Voided total tracking

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 22:16:05 +01:00

156 lines
5.1 KiB
Python

# 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