feat(promotion-code): Complete promotion code integration and testing
- Add comprehensive promotion code methods to Order model - Implement Stripe invoice integration for promotion code discounts - Display promotion codes on invoice with proper discount breakdown - Fix and enhance all unit tests for promotion code functionality - Add discount calculation with capping to prevent negative totals - Ensure promotion codes work across entire order lifecycle 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,34 @@
|
||||
require "test_helper"
|
||||
|
||||
class PromotionCodeTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
@user = User.create!(
|
||||
email: "test@example.com",
|
||||
password: "password123",
|
||||
password_confirmation: "password123"
|
||||
)
|
||||
|
||||
@event = Event.create!(
|
||||
name: "Test Event",
|
||||
slug: "test-event",
|
||||
description: "A valid description for the test event that is long enough",
|
||||
latitude: 48.8566,
|
||||
longitude: 2.3522,
|
||||
venue_name: "Test Venue",
|
||||
venue_address: "123 Test Street",
|
||||
user: @user
|
||||
)
|
||||
end
|
||||
|
||||
# Test valid promotion code creation
|
||||
def test_valid_promotion_code
|
||||
promotion_code = PromotionCode.create(
|
||||
code: "DISCOUNT10",
|
||||
discount_amount_cents: 1000, # €10.00
|
||||
expires_at: 1.month.from_now,
|
||||
active: true
|
||||
active: true,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
|
||||
assert promotion_code.valid?
|
||||
@@ -25,23 +46,23 @@ class PromotionCodeTest < ActiveSupport::TestCase
|
||||
|
||||
# Test unique code validation
|
||||
def test_unique_code_validation
|
||||
PromotionCode.create(code: "UNIQUE123", discount_amount_cents: 500)
|
||||
duplicate_code = PromotionCode.new(code: "UNIQUE123", discount_amount_cents: 500)
|
||||
PromotionCode.create(code: "UNIQUE123", discount_amount_cents: 500, user: @user, event: @event)
|
||||
duplicate_code = PromotionCode.new(code: "UNIQUE123", discount_amount_cents: 500, user: @user, event: @event)
|
||||
refute duplicate_code.valid?
|
||||
assert_not_nil duplicate_code.errors[:code]
|
||||
end
|
||||
|
||||
# Test discount amount validation
|
||||
def test_discount_amount_validation
|
||||
promotion_code = PromotionCode.new(code: "VALID123", discount_amount_cents: -100)
|
||||
promotion_code = PromotionCode.new(code: "VALID123", discount_amount_cents: -100, user: @user, event: @event)
|
||||
refute promotion_code.valid?
|
||||
assert_not_nil promotion_code.errors[:discount_amount_cents]
|
||||
end
|
||||
|
||||
# Test active scope
|
||||
def test_active_scope
|
||||
active_code = PromotionCode.create(code: "ACTIVE123", discount_amount_cents: 500, active: true)
|
||||
inactive_code = PromotionCode.create(code: "INACTIVE123", discount_amount_cents: 500, active: false)
|
||||
active_code = PromotionCode.create(code: "ACTIVE123", discount_amount_cents: 500, active: true, user: @user, event: @event)
|
||||
inactive_code = PromotionCode.create(code: "INACTIVE123", discount_amount_cents: 500, active: false, user: @user, event: @event)
|
||||
|
||||
assert_includes PromotionCode.active, active_code
|
||||
refute_includes PromotionCode.active, inactive_code
|
||||
@@ -49,8 +70,8 @@ class PromotionCodeTest < ActiveSupport::TestCase
|
||||
|
||||
# Test expired scope
|
||||
def test_expired_scope
|
||||
expired_code = PromotionCode.create(code: "EXPIRED123", discount_amount_cents: 500, expires_at: 1.day.ago)
|
||||
future_code = PromotionCode.create(code: "FUTURE123", discount_amount_cents: 500, expires_at: 1.month.from_now)
|
||||
expired_code = PromotionCode.create(code: "EXPIRED123", discount_amount_cents: 500, expires_at: 1.day.ago, user: @user, event: @event)
|
||||
future_code = PromotionCode.create(code: "FUTURE123", discount_amount_cents: 500, expires_at: 1.month.from_now, user: @user, event: @event)
|
||||
|
||||
assert_includes PromotionCode.expired, expired_code
|
||||
refute_includes PromotionCode.expired, future_code
|
||||
@@ -58,10 +79,191 @@ class PromotionCodeTest < ActiveSupport::TestCase
|
||||
|
||||
# Test valid scope
|
||||
def test_valid_scope
|
||||
valid_code = PromotionCode.create(code: "VALID123", discount_amount_cents: 500, active: true, expires_at: 1.month.from_now)
|
||||
invalid_code = PromotionCode.create(code: "INVALID123", discount_amount_cents: 500, active: false, expires_at: 1.day.ago)
|
||||
valid_code = PromotionCode.create(code: "VALID123", discount_amount_cents: 500, active: true, expires_at: 1.month.from_now, user: @user, event: @event)
|
||||
invalid_code = PromotionCode.create(code: "INVALID123", discount_amount_cents: 500, active: false, expires_at: 1.day.ago, user: @user, event: @event)
|
||||
|
||||
assert_includes PromotionCode.valid, valid_code
|
||||
refute_includes PromotionCode.valid, invalid_code
|
||||
end
|
||||
|
||||
# Test discount_amount_euros method
|
||||
def test_discount_amount_euros_converts_cents_to_euros
|
||||
promotion_code = PromotionCode.new(discount_amount_cents: 1000)
|
||||
assert_equal 10.0, promotion_code.discount_amount_euros
|
||||
|
||||
promotion_code = PromotionCode.new(discount_amount_cents: 550)
|
||||
assert_equal 5.5, promotion_code.discount_amount_euros
|
||||
end
|
||||
|
||||
# Test active? method
|
||||
def test_active_method
|
||||
# Active and not expired
|
||||
active_code = PromotionCode.create(
|
||||
code: "ACTIVE1",
|
||||
discount_amount_cents: 500,
|
||||
active: true,
|
||||
expires_at: 1.month.from_now,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
assert active_code.active?
|
||||
|
||||
# Active but expired
|
||||
expired_active_code = PromotionCode.create(
|
||||
code: "ACTIVE2",
|
||||
discount_amount_cents: 500,
|
||||
active: true,
|
||||
expires_at: 1.day.ago,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
assert_not expired_active_code.active?
|
||||
|
||||
# Inactive but not expired
|
||||
inactive_code = PromotionCode.create(
|
||||
code: "INACTIVE1",
|
||||
discount_amount_cents: 500,
|
||||
active: false,
|
||||
expires_at: 1.month.from_now,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
assert_not inactive_code.active?
|
||||
|
||||
# Active with no expiration
|
||||
no_expiry_code = PromotionCode.create(
|
||||
code: "NOEXPIRY",
|
||||
discount_amount_cents: 500,
|
||||
active: true,
|
||||
expires_at: nil,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
assert no_expiry_code.active?
|
||||
end
|
||||
|
||||
# Test expired? method
|
||||
def test_expired_method
|
||||
# Expired code
|
||||
expired_code = PromotionCode.create(
|
||||
code: "EXPIRED1",
|
||||
discount_amount_cents: 500,
|
||||
expires_at: 1.day.ago,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
assert expired_code.expired?
|
||||
|
||||
# Future code
|
||||
future_code = PromotionCode.create(
|
||||
code: "FUTURE1",
|
||||
discount_amount_cents: 500,
|
||||
expires_at: 1.month.from_now,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
assert_not future_code.expired?
|
||||
|
||||
# No expiration
|
||||
no_expiry_code = PromotionCode.create(
|
||||
code: "NOEXPIRY1",
|
||||
discount_amount_cents: 500,
|
||||
expires_at: nil,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
assert_not no_expiry_code.expired?
|
||||
end
|
||||
|
||||
# Test can_be_used? method
|
||||
def test_can_be_used_method
|
||||
# Can be used: active, not expired, under usage limit
|
||||
usable_code = PromotionCode.create(
|
||||
code: "USABLE1",
|
||||
discount_amount_cents: 500,
|
||||
active: true,
|
||||
expires_at: 1.month.from_now,
|
||||
usage_limit: 10,
|
||||
uses_count: 0,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
assert usable_code.can_be_used?
|
||||
|
||||
# Cannot be used: inactive
|
||||
inactive_code = PromotionCode.create(
|
||||
code: "INACTIVE2",
|
||||
discount_amount_cents: 500,
|
||||
active: false,
|
||||
expires_at: 1.month.from_now,
|
||||
usage_limit: 10,
|
||||
uses_count: 0,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
assert_not inactive_code.can_be_used?
|
||||
|
||||
# Cannot be used: expired
|
||||
expired_code = PromotionCode.create(
|
||||
code: "EXPIRED2",
|
||||
discount_amount_cents: 500,
|
||||
active: true,
|
||||
expires_at: 1.day.ago,
|
||||
usage_limit: 10,
|
||||
uses_count: 0,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
assert_not expired_code.can_be_used?
|
||||
|
||||
# Cannot be used: at usage limit
|
||||
limit_reached_code = PromotionCode.create(
|
||||
code: "LIMIT1",
|
||||
discount_amount_cents: 500,
|
||||
active: true,
|
||||
expires_at: 1.month.from_now,
|
||||
usage_limit: 5,
|
||||
uses_count: 5,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
assert_not limit_reached_code.can_be_used?
|
||||
|
||||
# Can be used: no usage limit
|
||||
no_limit_code = PromotionCode.create(
|
||||
code: "NOLIMIT1",
|
||||
discount_amount_cents: 500,
|
||||
active: true,
|
||||
expires_at: 1.month.from_now,
|
||||
usage_limit: nil,
|
||||
uses_count: 100,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
assert no_limit_code.can_be_used?
|
||||
end
|
||||
|
||||
# Test increment_uses_count callback
|
||||
def test_increment_uses_count_callback
|
||||
promotion_code = PromotionCode.create(
|
||||
code: "INCREMENT1",
|
||||
discount_amount_cents: 500,
|
||||
uses_count: 0,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
|
||||
assert_equal 0, promotion_code.uses_count
|
||||
|
||||
# The callback should only run on create, so we test the initial value
|
||||
new_code = PromotionCode.create(
|
||||
code: "INCREMENT2",
|
||||
discount_amount_cents: 500,
|
||||
uses_count: nil,
|
||||
user: @user,
|
||||
event: @event
|
||||
)
|
||||
|
||||
assert_equal 0, new_code.uses_count
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user