Compare commits
2 Commits
c9b2ecbdff
...
56afb9192b
| Author | SHA1 | Date | |
|---|---|---|---|
| 56afb9192b | |||
| a4519035df |
@@ -29,10 +29,6 @@ class ModuleConfig(BaseSettings):
|
|||||||
# Points configuration
|
# Points configuration
|
||||||
default_points_per_euro: int = 10 # 10 points per euro spent
|
default_points_per_euro: int = 10 # 10 points per euro spent
|
||||||
|
|
||||||
# Google Wallet
|
|
||||||
google_issuer_id: str | None = None
|
|
||||||
google_service_account_json: str | None = None # Path to JSON file
|
|
||||||
|
|
||||||
# Apple Wallet
|
# Apple Wallet
|
||||||
apple_pass_type_id: str | None = None
|
apple_pass_type_id: str | None = None
|
||||||
apple_team_id: str | None = None
|
apple_team_id: str | None = None
|
||||||
|
|||||||
@@ -478,10 +478,14 @@ def list_store_transactions(
|
|||||||
db, merchant_id, skip=skip, limit=limit
|
db, merchant_id, skip=skip, limit=limit
|
||||||
)
|
)
|
||||||
|
|
||||||
return TransactionListResponse(
|
tx_responses = []
|
||||||
transactions=[TransactionResponse.model_validate(t) for t in transactions],
|
for t in transactions:
|
||||||
total=total,
|
tx = TransactionResponse.model_validate(t)
|
||||||
)
|
if t.card and t.card.customer:
|
||||||
|
tx.customer_name = t.card.customer.full_name
|
||||||
|
tx_responses.append(tx)
|
||||||
|
|
||||||
|
return TransactionListResponse(transactions=tx_responses, total=total)
|
||||||
|
|
||||||
|
|
||||||
@store_router.post("/cards/enroll", response_model=CardResponse, status_code=201)
|
@store_router.post("/cards/enroll", response_model=CardResponse, status_code=201)
|
||||||
|
|||||||
@@ -175,6 +175,9 @@ class TransactionResponse(BaseModel):
|
|||||||
reward_description: str | None = None
|
reward_description: str | None = None
|
||||||
notes: str | None = None
|
notes: str | None = None
|
||||||
|
|
||||||
|
# Customer
|
||||||
|
customer_name: str | None = None
|
||||||
|
|
||||||
# Staff
|
# Staff
|
||||||
staff_name: str | None = None
|
staff_name: str | None = None
|
||||||
|
|
||||||
|
|||||||
@@ -655,7 +655,10 @@ class CardService:
|
|||||||
query = (
|
query = (
|
||||||
db.query(LoyaltyTransaction)
|
db.query(LoyaltyTransaction)
|
||||||
.join(LoyaltyCard, LoyaltyTransaction.card_id == LoyaltyCard.id)
|
.join(LoyaltyCard, LoyaltyTransaction.card_id == LoyaltyCard.id)
|
||||||
.options(joinedload(LoyaltyTransaction.store))
|
.options(
|
||||||
|
joinedload(LoyaltyTransaction.store),
|
||||||
|
joinedload(LoyaltyTransaction.card).joinedload(LoyaltyCard.customer),
|
||||||
|
)
|
||||||
.filter(LoyaltyCard.merchant_id == merchant_id)
|
.filter(LoyaltyCard.merchant_id == merchant_id)
|
||||||
)
|
)
|
||||||
if store_id:
|
if store_id:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from typing import Any
|
|||||||
|
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.modules.loyalty.config import config
|
from app.core.config import settings
|
||||||
from app.modules.loyalty.exceptions import (
|
from app.modules.loyalty.exceptions import (
|
||||||
GoogleWalletNotConfiguredException,
|
GoogleWalletNotConfiguredException,
|
||||||
WalletIntegrationException,
|
WalletIntegrationException,
|
||||||
@@ -35,14 +35,14 @@ class GoogleWalletService:
|
|||||||
@property
|
@property
|
||||||
def is_configured(self) -> bool:
|
def is_configured(self) -> bool:
|
||||||
"""Check if Google Wallet is configured."""
|
"""Check if Google Wallet is configured."""
|
||||||
return bool(config.google_issuer_id and config.google_service_account_json)
|
return bool(settings.loyalty_google_issuer_id and settings.loyalty_google_service_account_json)
|
||||||
|
|
||||||
def _get_credentials(self):
|
def _get_credentials(self):
|
||||||
"""Get Google service account credentials."""
|
"""Get Google service account credentials."""
|
||||||
if self._credentials:
|
if self._credentials:
|
||||||
return self._credentials
|
return self._credentials
|
||||||
|
|
||||||
if not config.google_service_account_json:
|
if not settings.loyalty_google_service_account_json:
|
||||||
raise GoogleWalletNotConfiguredException()
|
raise GoogleWalletNotConfiguredException()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -51,7 +51,7 @@ class GoogleWalletService:
|
|||||||
scopes = ["https://www.googleapis.com/auth/wallet_object.issuer"]
|
scopes = ["https://www.googleapis.com/auth/wallet_object.issuer"]
|
||||||
|
|
||||||
self._credentials = service_account.Credentials.from_service_account_file(
|
self._credentials = service_account.Credentials.from_service_account_file(
|
||||||
config.google_service_account_json,
|
settings.loyalty_google_service_account_json,
|
||||||
scopes=scopes,
|
scopes=scopes,
|
||||||
)
|
)
|
||||||
return self._credentials
|
return self._credentials
|
||||||
@@ -92,7 +92,7 @@ class GoogleWalletService:
|
|||||||
if not self.is_configured:
|
if not self.is_configured:
|
||||||
raise GoogleWalletNotConfiguredException()
|
raise GoogleWalletNotConfiguredException()
|
||||||
|
|
||||||
issuer_id = config.google_issuer_id
|
issuer_id = settings.loyalty_google_issuer_id
|
||||||
class_id = f"{issuer_id}.loyalty_program_{program.id}"
|
class_id = f"{issuer_id}.loyalty_program_{program.id}"
|
||||||
|
|
||||||
class_data = {
|
class_data = {
|
||||||
@@ -203,7 +203,7 @@ class GoogleWalletService:
|
|||||||
# Create class first
|
# Create class first
|
||||||
self.create_class(db, program)
|
self.create_class(db, program)
|
||||||
|
|
||||||
issuer_id = config.google_issuer_id
|
issuer_id = settings.loyalty_google_issuer_id
|
||||||
object_id = f"{issuer_id}.loyalty_card_{card.id}"
|
object_id = f"{issuer_id}.loyalty_card_{card.id}"
|
||||||
|
|
||||||
object_data = self._build_object_data(card, object_id)
|
object_data = self._build_object_data(card, object_id)
|
||||||
|
|||||||
@@ -1,9 +1,111 @@
|
|||||||
"""Unit tests for WalletService."""
|
# app/modules/loyalty/tests/unit/test_wallet_service.py
|
||||||
|
"""
|
||||||
|
Unit tests for WalletService — wallet object creation during enrollment.
|
||||||
|
|
||||||
|
Tests that:
|
||||||
|
- Google Wallet objects are created and DB fields populated on enrollment
|
||||||
|
- Apple Wallet serial numbers are set when apple_pass_type_id is configured
|
||||||
|
- Wallet creation failures don't crash enrollment
|
||||||
|
- Wallet creation is skipped when not configured
|
||||||
|
"""
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from app.modules.loyalty.models import LoyaltyCard, LoyaltyProgram
|
||||||
|
from app.modules.loyalty.models.loyalty_program import LoyaltyType
|
||||||
from app.modules.loyalty.services.wallet_service import WalletService
|
from app.modules.loyalty.services.wallet_service import WalletService
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Fixtures
|
||||||
|
# Uses test_merchant and test_customer from shared fixtures (store_fixtures,
|
||||||
|
# customer_fixtures) which handle owner_user_id and other required fields.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def wt_program(db, test_merchant):
|
||||||
|
"""Create a loyalty program for wallet tests."""
|
||||||
|
program = LoyaltyProgram(
|
||||||
|
merchant_id=test_merchant.id,
|
||||||
|
loyalty_type=LoyaltyType.POINTS.value,
|
||||||
|
points_per_euro=10,
|
||||||
|
welcome_bonus_points=0,
|
||||||
|
minimum_redemption_points=100,
|
||||||
|
cooldown_minutes=0,
|
||||||
|
max_daily_stamps=5,
|
||||||
|
require_staff_pin=False,
|
||||||
|
card_name="Wallet Test Rewards",
|
||||||
|
card_color="#4F46E5",
|
||||||
|
is_active=True,
|
||||||
|
)
|
||||||
|
db.add(program)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(program)
|
||||||
|
return program
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def wt_program_with_apple(db, test_merchant):
|
||||||
|
"""Create a loyalty program with Apple Wallet configured."""
|
||||||
|
program = LoyaltyProgram(
|
||||||
|
merchant_id=test_merchant.id,
|
||||||
|
loyalty_type=LoyaltyType.POINTS.value,
|
||||||
|
points_per_euro=10,
|
||||||
|
welcome_bonus_points=0,
|
||||||
|
minimum_redemption_points=100,
|
||||||
|
cooldown_minutes=0,
|
||||||
|
max_daily_stamps=5,
|
||||||
|
require_staff_pin=False,
|
||||||
|
card_name="Apple Wallet Test",
|
||||||
|
card_color="#000000",
|
||||||
|
apple_pass_type_id="pass.com.test.loyalty",
|
||||||
|
is_active=True,
|
||||||
|
)
|
||||||
|
db.add(program)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(program)
|
||||||
|
return program
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def wt_card(db, wt_program, test_customer):
|
||||||
|
"""Create a loyalty card for wallet tests."""
|
||||||
|
card = LoyaltyCard(
|
||||||
|
merchant_id=wt_program.merchant_id,
|
||||||
|
program_id=wt_program.id,
|
||||||
|
customer_id=test_customer.id,
|
||||||
|
card_number=f"WT-{uuid.uuid4().hex[:8].upper()}",
|
||||||
|
is_active=True,
|
||||||
|
)
|
||||||
|
db.add(card)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(card)
|
||||||
|
return card
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def wt_card_apple(db, wt_program_with_apple, test_customer):
|
||||||
|
"""Create a loyalty card for Apple Wallet tests."""
|
||||||
|
card = LoyaltyCard(
|
||||||
|
merchant_id=wt_program_with_apple.merchant_id,
|
||||||
|
program_id=wt_program_with_apple.id,
|
||||||
|
customer_id=test_customer.id,
|
||||||
|
card_number=f"WTA-{uuid.uuid4().hex[:8].upper()}",
|
||||||
|
is_active=True,
|
||||||
|
)
|
||||||
|
db.add(card)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(card)
|
||||||
|
return card
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# WalletService instantiation
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@pytest.mark.loyalty
|
@pytest.mark.loyalty
|
||||||
@@ -16,3 +118,274 @@ class TestWalletService:
|
|||||||
def test_service_instantiation(self):
|
def test_service_instantiation(self):
|
||||||
"""Service can be instantiated."""
|
"""Service can be instantiated."""
|
||||||
assert self.service is not None
|
assert self.service is not None
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Google Wallet object creation
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@pytest.mark.loyalty
|
||||||
|
class TestCreateWalletObjectsGoogle:
|
||||||
|
"""Tests for Google Wallet object creation during enrollment."""
|
||||||
|
|
||||||
|
@patch("app.modules.loyalty.services.google_wallet_service.google_wallet_service")
|
||||||
|
def test_google_wallet_object_created_when_configured(
|
||||||
|
self, mock_gw, db, wt_card
|
||||||
|
):
|
||||||
|
"""Google Wallet object is created when service is configured."""
|
||||||
|
mock_gw.is_configured = True
|
||||||
|
mock_gw.create_object.return_value = "test_object_id"
|
||||||
|
|
||||||
|
service = WalletService()
|
||||||
|
results = service.create_wallet_objects(db, wt_card)
|
||||||
|
|
||||||
|
assert results["google_wallet"] is True
|
||||||
|
mock_gw.create_object.assert_called_once_with(db, wt_card)
|
||||||
|
|
||||||
|
@patch("app.modules.loyalty.services.google_wallet_service.google_wallet_service")
|
||||||
|
def test_google_wallet_skipped_when_not_configured(
|
||||||
|
self, mock_gw, db, wt_card
|
||||||
|
):
|
||||||
|
"""Google Wallet is skipped when not configured."""
|
||||||
|
mock_gw.is_configured = False
|
||||||
|
|
||||||
|
service = WalletService()
|
||||||
|
results = service.create_wallet_objects(db, wt_card)
|
||||||
|
|
||||||
|
assert results["google_wallet"] is False
|
||||||
|
mock_gw.create_object.assert_not_called()
|
||||||
|
|
||||||
|
@patch("app.modules.loyalty.services.google_wallet_service.google_wallet_service")
|
||||||
|
def test_google_wallet_failure_does_not_crash(
|
||||||
|
self, mock_gw, db, wt_card
|
||||||
|
):
|
||||||
|
"""Enrollment continues even if Google Wallet creation fails."""
|
||||||
|
mock_gw.is_configured = True
|
||||||
|
mock_gw.create_object.side_effect = Exception("Google API error")
|
||||||
|
|
||||||
|
service = WalletService()
|
||||||
|
results = service.create_wallet_objects(db, wt_card)
|
||||||
|
|
||||||
|
assert results["google_wallet"] is False
|
||||||
|
# Card should still be valid
|
||||||
|
db.refresh(wt_card)
|
||||||
|
assert wt_card.is_active is True
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Apple Wallet serial number setup
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@pytest.mark.loyalty
|
||||||
|
class TestCreateWalletObjectsApple:
|
||||||
|
"""Tests for Apple Wallet serial number assignment during enrollment."""
|
||||||
|
|
||||||
|
def test_apple_serial_number_set_when_configured(self, db, wt_card_apple):
|
||||||
|
"""Apple serial number is set when program has apple_pass_type_id."""
|
||||||
|
assert wt_card_apple.apple_serial_number is None
|
||||||
|
|
||||||
|
service = WalletService()
|
||||||
|
with patch(
|
||||||
|
"app.modules.loyalty.services.google_wallet_service.google_wallet_service"
|
||||||
|
) as mock_gw:
|
||||||
|
mock_gw.is_configured = False
|
||||||
|
results = service.create_wallet_objects(db, wt_card_apple)
|
||||||
|
|
||||||
|
assert results["apple_wallet"] is True
|
||||||
|
db.refresh(wt_card_apple)
|
||||||
|
assert wt_card_apple.apple_serial_number is not None
|
||||||
|
assert wt_card_apple.apple_serial_number.startswith(f"card_{wt_card_apple.id}_")
|
||||||
|
|
||||||
|
def test_apple_serial_number_not_set_when_unconfigured(self, db, wt_card):
|
||||||
|
"""Apple serial number not set when program lacks apple_pass_type_id."""
|
||||||
|
service = WalletService()
|
||||||
|
with patch(
|
||||||
|
"app.modules.loyalty.services.google_wallet_service.google_wallet_service"
|
||||||
|
) as mock_gw:
|
||||||
|
mock_gw.is_configured = False
|
||||||
|
results = service.create_wallet_objects(db, wt_card)
|
||||||
|
|
||||||
|
assert results["apple_wallet"] is False
|
||||||
|
db.refresh(wt_card)
|
||||||
|
assert wt_card.apple_serial_number is None
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# GoogleWalletService.create_object — DB field population
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@pytest.mark.loyalty
|
||||||
|
class TestGoogleWalletCreateObject:
|
||||||
|
"""Tests that GoogleWalletService.create_object populates DB fields correctly."""
|
||||||
|
|
||||||
|
@patch("app.modules.loyalty.services.google_wallet_service.settings")
|
||||||
|
def test_create_object_sets_google_object_id(self, mock_settings, db, wt_card, wt_program):
|
||||||
|
"""create_object sets card.google_object_id in the database."""
|
||||||
|
from app.modules.loyalty.services.google_wallet_service import (
|
||||||
|
GoogleWalletService,
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_settings.loyalty_google_issuer_id = "1234567890"
|
||||||
|
mock_settings.loyalty_google_service_account_json = "/fake/path.json"
|
||||||
|
|
||||||
|
# Pre-set class_id on program so create_class isn't called
|
||||||
|
wt_program.google_class_id = "1234567890.loyalty_program_1"
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
service = GoogleWalletService()
|
||||||
|
service._credentials = MagicMock() # Skip credential loading
|
||||||
|
|
||||||
|
mock_http = MagicMock()
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.status_code = 201
|
||||||
|
mock_http.post.return_value = mock_response
|
||||||
|
service._http_client = mock_http
|
||||||
|
|
||||||
|
result = service.create_object(db, wt_card)
|
||||||
|
|
||||||
|
expected_object_id = f"1234567890.loyalty_card_{wt_card.id}"
|
||||||
|
assert result == expected_object_id
|
||||||
|
|
||||||
|
db.refresh(wt_card)
|
||||||
|
assert wt_card.google_object_id == expected_object_id
|
||||||
|
|
||||||
|
@patch("app.modules.loyalty.services.google_wallet_service.settings")
|
||||||
|
def test_create_object_handles_409_conflict(self, mock_settings, db, wt_card, wt_program):
|
||||||
|
"""create_object handles 409 (already exists) by still setting the object_id."""
|
||||||
|
from app.modules.loyalty.services.google_wallet_service import (
|
||||||
|
GoogleWalletService,
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_settings.loyalty_google_issuer_id = "1234567890"
|
||||||
|
mock_settings.loyalty_google_service_account_json = "/fake/path.json"
|
||||||
|
|
||||||
|
wt_program.google_class_id = "1234567890.loyalty_program_1"
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
service = GoogleWalletService()
|
||||||
|
service._credentials = MagicMock()
|
||||||
|
|
||||||
|
mock_http = MagicMock()
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.status_code = 409 # Already exists
|
||||||
|
mock_http.post.return_value = mock_response
|
||||||
|
service._http_client = mock_http
|
||||||
|
|
||||||
|
service.create_object(db, wt_card)
|
||||||
|
|
||||||
|
db.refresh(wt_card)
|
||||||
|
assert wt_card.google_object_id == f"1234567890.loyalty_card_{wt_card.id}"
|
||||||
|
|
||||||
|
@patch("app.modules.loyalty.services.google_wallet_service.settings")
|
||||||
|
def test_create_class_sets_google_class_id(self, mock_settings, db, wt_program):
|
||||||
|
"""create_class sets program.google_class_id in the database."""
|
||||||
|
from app.modules.loyalty.services.google_wallet_service import (
|
||||||
|
GoogleWalletService,
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_settings.loyalty_google_issuer_id = "1234567890"
|
||||||
|
mock_settings.loyalty_google_service_account_json = "/fake/path.json"
|
||||||
|
|
||||||
|
assert wt_program.google_class_id is None
|
||||||
|
|
||||||
|
service = GoogleWalletService()
|
||||||
|
service._credentials = MagicMock()
|
||||||
|
|
||||||
|
mock_http = MagicMock()
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.status_code = 201
|
||||||
|
mock_http.post.return_value = mock_response
|
||||||
|
service._http_client = mock_http
|
||||||
|
|
||||||
|
result = service.create_class(db, wt_program)
|
||||||
|
|
||||||
|
expected_class_id = f"1234567890.loyalty_program_{wt_program.id}"
|
||||||
|
assert result == expected_class_id
|
||||||
|
|
||||||
|
db.refresh(wt_program)
|
||||||
|
assert wt_program.google_class_id == expected_class_id
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# End-to-end: enrollment → wallet creation
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@pytest.mark.loyalty
|
||||||
|
class TestEnrollmentWalletCreation:
|
||||||
|
"""Tests that enrollment triggers wallet object creation and populates DB fields."""
|
||||||
|
|
||||||
|
@patch("app.modules.loyalty.services.google_wallet_service.settings")
|
||||||
|
def test_enrollment_creates_google_wallet_object(
|
||||||
|
self, mock_settings, db, wt_program, test_merchant, test_customer
|
||||||
|
):
|
||||||
|
"""Full enrollment flow creates Google Wallet class + object in DB."""
|
||||||
|
from app.modules.loyalty.services.google_wallet_service import (
|
||||||
|
google_wallet_service,
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_settings.loyalty_google_issuer_id = "1234567890"
|
||||||
|
mock_settings.loyalty_google_service_account_json = "/fake/path.json"
|
||||||
|
|
||||||
|
# Mock the HTTP client to simulate Google API success
|
||||||
|
mock_http = MagicMock()
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.status_code = 201
|
||||||
|
mock_http.post.return_value = mock_response
|
||||||
|
google_wallet_service._http_client = mock_http
|
||||||
|
google_wallet_service._credentials = MagicMock()
|
||||||
|
|
||||||
|
# Enroll via card_service
|
||||||
|
from app.modules.loyalty.services.card_service import card_service
|
||||||
|
card = card_service.enroll_customer(db, test_customer.id, test_merchant.id)
|
||||||
|
|
||||||
|
# Verify wallet DB fields are populated
|
||||||
|
db.refresh(card)
|
||||||
|
assert card.google_object_id is not None
|
||||||
|
assert card.google_object_id == f"1234567890.loyalty_card_{card.id}"
|
||||||
|
|
||||||
|
# Verify program class_id is set
|
||||||
|
db.refresh(wt_program)
|
||||||
|
assert wt_program.google_class_id is not None
|
||||||
|
assert wt_program.google_class_id == f"1234567890.loyalty_program_{wt_program.id}"
|
||||||
|
|
||||||
|
# Clean up singleton state
|
||||||
|
google_wallet_service._http_client = None
|
||||||
|
google_wallet_service._credentials = None
|
||||||
|
|
||||||
|
def test_enrollment_succeeds_without_wallet_config(
|
||||||
|
self, db, wt_program, test_merchant, test_customer
|
||||||
|
):
|
||||||
|
"""Enrollment works even when Google Wallet is not configured."""
|
||||||
|
from app.modules.loyalty.services.card_service import card_service
|
||||||
|
card = card_service.enroll_customer(db, test_customer.id, test_merchant.id)
|
||||||
|
|
||||||
|
db.refresh(card)
|
||||||
|
assert card.id is not None
|
||||||
|
assert card.is_active is True
|
||||||
|
# Wallet fields should be None since not configured
|
||||||
|
assert card.google_object_id is None
|
||||||
|
|
||||||
|
@patch("app.modules.loyalty.services.google_wallet_service.settings")
|
||||||
|
def test_enrollment_with_apple_wallet_sets_serial(
|
||||||
|
self, mock_settings, db, wt_program_with_apple, test_customer
|
||||||
|
):
|
||||||
|
"""Enrollment sets apple_serial_number when program has apple_pass_type_id."""
|
||||||
|
mock_settings.loyalty_google_issuer_id = None
|
||||||
|
mock_settings.loyalty_google_service_account_json = None
|
||||||
|
|
||||||
|
from app.modules.loyalty.services.card_service import card_service
|
||||||
|
card = card_service.enroll_customer(
|
||||||
|
db, test_customer.id, wt_program_with_apple.merchant_id
|
||||||
|
)
|
||||||
|
|
||||||
|
db.refresh(card)
|
||||||
|
assert card.apple_serial_number is not None
|
||||||
|
assert card.apple_serial_number.startswith(f"card_{card.id}_")
|
||||||
|
|||||||
Reference in New Issue
Block a user