Add model methods for accurate net calculations (€0.50 + 1.5% fees), eligibility, refund handling Update promoter/payouts controller for index (pending events), create (eligibility checks) Integrate admin processing via Stripe::Transfer, webhook for status sync Enhance views: index pending cards, events/show preview/form Add comprehensive tests (models, controllers, service, integration); run migrations
459 lines
20 KiB
Ruby
Executable File
459 lines
20 KiB
Ruby
Executable File
require "test_helper"
|
|
require "timecop"
|
|
|
|
class EventTest < ActiveSupport::TestCase
|
|
# Test that Event model exists
|
|
test "should be a class" do
|
|
assert_kind_of Class, Event
|
|
end
|
|
|
|
# Test validations
|
|
test "should not save event without name" do
|
|
event = Event.new(description: "Test event description")
|
|
assert_not event.save
|
|
end
|
|
|
|
test "should not save event without description" do
|
|
event = Event.new(name: "Test Event")
|
|
assert_not event.save
|
|
end
|
|
|
|
test "should not save event with name less than 3 characters" do
|
|
event = Event.new(name: "AB", description: "Valid description for the event")
|
|
assert_not event.save
|
|
end
|
|
|
|
test "should not save event with description less than 10 characters" do
|
|
event = Event.new(name: "Valid Event Name", description: "Too short")
|
|
assert_not event.save
|
|
end
|
|
|
|
test "should not save event without latitude" do
|
|
event = Event.new(
|
|
name: "Valid Event Name",
|
|
description: "Valid description for the event that is long enough",
|
|
longitude: 2.3522
|
|
)
|
|
assert_not event.save
|
|
end
|
|
|
|
test "should not save event without longitude" do
|
|
event = Event.new(
|
|
name: "Valid Event Name",
|
|
description: "Valid description for the event that is long enough",
|
|
latitude: 48.8566
|
|
)
|
|
assert_not event.save
|
|
end
|
|
|
|
test "should not save event with invalid latitude" do
|
|
event = Event.new(
|
|
name: "Valid Event Name",
|
|
description: "Valid description for the event that is long enough",
|
|
latitude: 95.0,
|
|
longitude: 2.3522,
|
|
venue_name: "Test Venue",
|
|
venue_address: "123 Test Street"
|
|
)
|
|
assert_not event.save
|
|
end
|
|
|
|
test "should not save event with invalid longitude" do
|
|
event = Event.new(
|
|
name: "Valid Event Name",
|
|
description: "Valid description for the event that is long enough",
|
|
latitude: 48.8566,
|
|
longitude: 190.0,
|
|
venue_name: "Test Venue",
|
|
venue_address: "123 Test Street"
|
|
)
|
|
assert_not event.save
|
|
end
|
|
|
|
test "should not save event without slug" do
|
|
event = Event.new(
|
|
name: "Valid Event Name",
|
|
description: "Valid description for the event that is long enough",
|
|
latitude: 48.8566,
|
|
longitude: 2.3522,
|
|
venue_name: "Test Venue",
|
|
venue_address: "123 Test Street"
|
|
)
|
|
assert_not event.save
|
|
end
|
|
|
|
test "should not save event with slug less than 3 characters" do
|
|
event = Event.new(
|
|
name: "Valid Event Name",
|
|
description: "Valid description for the event that is long enough",
|
|
latitude: 48.8566,
|
|
longitude: 2.3522,
|
|
venue_name: "Test Venue",
|
|
venue_address: "123 Test Street",
|
|
slug: "ab"
|
|
)
|
|
assert_not event.save
|
|
end
|
|
|
|
test "should save valid event" do
|
|
user = User.create!(
|
|
email: "test@example.com",
|
|
password: "password123",
|
|
password_confirmation: "password123"
|
|
)
|
|
|
|
event = Event.new(
|
|
name: "Valid Event Name",
|
|
slug: "valid-event-name",
|
|
description: "Valid description for the event that is long enough",
|
|
latitude: 48.8566,
|
|
longitude: 2.3522,
|
|
venue_name: "Test Venue",
|
|
venue_address: "123 Test Street",
|
|
user: user,
|
|
)
|
|
assert event.save
|
|
end
|
|
|
|
# Test enum states
|
|
test "should have valid states" do
|
|
assert_equal %w[draft published canceled sold_out], Event.states.keys
|
|
end
|
|
|
|
test "should default to draft state" do
|
|
event = Event.new(
|
|
name: "Valid Event Name",
|
|
description: "Valid description for the event that is long enough",
|
|
latitude: 48.8566,
|
|
longitude: 2.3522,
|
|
venue_name: "Test Venue",
|
|
venue_address: "123 Test Street"
|
|
)
|
|
assert_equal "draft", event.state
|
|
end
|
|
|
|
# Test associations
|
|
test "should belong to user" do
|
|
association = Event.reflect_on_association(:user)
|
|
assert_equal :belongs_to, association.macro
|
|
end
|
|
|
|
test "should have many ticket_types" do
|
|
association = Event.reflect_on_association(:ticket_types)
|
|
assert_equal :has_many, association.macro
|
|
end
|
|
|
|
test "should have many tickets through ticket_types" do
|
|
association = Event.reflect_on_association(:tickets)
|
|
assert_equal :has_many, association.macro
|
|
assert_equal :ticket_types, association.options[:through]
|
|
end
|
|
|
|
# Test scopes
|
|
test "should respond to featured scope" do
|
|
assert_respond_to Event, :featured
|
|
end
|
|
|
|
test "should respond to published scope" do
|
|
assert_respond_to Event, :published
|
|
end
|
|
|
|
test "should respond to search_by_name scope" do
|
|
assert_respond_to Event, :search_by_name
|
|
end
|
|
|
|
test "upcoming scope should return only published future events" do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
future_published = Event.create!(name: "Future", slug: "future", description: "Valid description for the event", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.day.from_now, state: :published)
|
|
past_published = Event.create!(name: "Past", slug: "past", description: "Valid description for the event", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.day.ago, state: :published)
|
|
future_draft = Event.create!(name: "Draft", slug: "draft", description: "Valid description for the event", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.day.from_now, state: :draft)
|
|
|
|
upcoming = Event.upcoming
|
|
assert_includes upcoming, future_published
|
|
assert_not_includes upcoming, past_published
|
|
assert_not_includes upcoming, future_draft
|
|
end
|
|
|
|
test "geocoding_successful? should return true for valid coordinates" do
|
|
event = Event.new(latitude: 48.8566, longitude: 2.3522, name: "Test", slug: "test", description: "A description that is sufficiently long", venue_name: "v", venue_address: "a")
|
|
assert event.geocoding_successful?
|
|
end
|
|
|
|
test "geocoding_successful? should return false for fallback coordinates" do
|
|
event = Event.new(latitude: 46.603354, longitude: 1.888334, name: "Test", slug: "test", description: "Valid description for the event", venue_name: "v", venue_address: "a")
|
|
assert_not event.geocoding_successful?
|
|
end
|
|
|
|
test "geocoding_status_message should return message when not successful" do
|
|
event = Event.new(latitude: 46.603354, longitude: 1.888334, name: "Test", slug: "test", description: "Valid description for the event", venue_name: "v", venue_address: "a")
|
|
assert_match(/coordonnées/, event.geocoding_status_message)
|
|
end
|
|
|
|
test "geocoding_status_message should return nil when successful" do
|
|
event = Event.new(latitude: 48.8566, longitude: 2.3522, name: "Test", slug: "test", description: "Valid description for the event", venue_name: "v", venue_address: "a")
|
|
assert_nil event.geocoding_status_message
|
|
end
|
|
|
|
test "booking_allowed? should be true for published future event" do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
event = Event.create!(name: "Test", slug: "test", description: "A description that is sufficiently long", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.day.from_now, state: :published)
|
|
assert event.booking_allowed?
|
|
end
|
|
|
|
test "booking_allowed? should be false for draft event" do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
event = Event.create!(name: "Test", slug: "test", description: "Valid description for the event", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.day.from_now, state: :draft)
|
|
assert_not event.booking_allowed?
|
|
end
|
|
|
|
test "booking_allowed? should be false for canceled event" do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
event = Event.create!(name: "Test", slug: "test", description: "Valid description for the event", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.day.from_now, state: :canceled)
|
|
assert_not event.booking_allowed?
|
|
end
|
|
|
|
test "booking_allowed? should be false for sold_out event" do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
event = Event.create!(name: "Test", slug: "test", description: "A description that is sufficiently long", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.day.from_now, state: :sold_out)
|
|
assert_not event.booking_allowed?
|
|
end
|
|
|
|
test "booking_allowed? should be false during event without allow_booking_during_event" do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
event = Event.create!(name: "Test", slug: "test", description: "Valid description for the event", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.hour.ago, end_time: 2.hours.from_now, state: :published, allow_booking_during_event: false)
|
|
assert_not event.booking_allowed?
|
|
end
|
|
|
|
test "booking_allowed? should be true during event with allow_booking_during_event" do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
event = Event.create!(name: "Test", slug: "test", description: "Valid description for the event", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.hour.ago, end_time: 2.hours.from_now, state: :published, allow_booking_during_event: true)
|
|
assert event.booking_allowed?
|
|
end
|
|
|
|
test "event_started? should be true after start_time" do
|
|
Timecop.freeze(1.hour.from_now) do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
event = Event.create!(name: "Test", slug: "test", description: "A description that is sufficiently long", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.hour.ago)
|
|
assert event.event_started?
|
|
end
|
|
end
|
|
|
|
test "event_started? should be false before start_time" do
|
|
Timecop.freeze(1.hour.ago) do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
event = Event.create!(name: "Test", slug: "test", description: "Valid description for the event", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.hour.from_now)
|
|
assert_not event.event_started?
|
|
end
|
|
end
|
|
|
|
test "event_ended? should be true after end_time" do
|
|
Timecop.freeze(1.hour.from_now) do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
event = Event.create!(name: "Test", slug: "test", description: "Valid description for the event", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.hour.ago, end_time: 30.minutes.ago)
|
|
assert event.event_ended?
|
|
end
|
|
end
|
|
|
|
test "event_ended? should be false before end_time" do
|
|
Timecop.freeze(1.hour.ago) do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
event = Event.create!(name: "Test", slug: "test", description: "A description that is sufficiently long", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.hour.ago, end_time: 1.hour.from_now)
|
|
assert_not event.event_ended?
|
|
end
|
|
end
|
|
|
|
test "allow_booking_during_event? should return true when set to true" do
|
|
event = Event.new(allow_booking_during_event: true)
|
|
assert event.allow_booking_during_event?
|
|
end
|
|
|
|
test "allow_booking_during_event? should return false when nil" do
|
|
event = Event.new
|
|
assert_not event.allow_booking_during_event?
|
|
end
|
|
|
|
test "should duplicate event with ticket types" do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
event = Event.create!(name: "Original Event", slug: "original", description: "A description that is sufficiently long", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.week.from_now, state: :published)
|
|
|
|
# Create ticket types
|
|
ticket_type1 = TicketType.create!(name: "Standard", description: "A standard ticket for the event", price_cents: 2000, quantity: 100, sale_start_at: 1.day.ago, sale_end_at: event.start_time - 1.hour, event: event)
|
|
ticket_type2 = TicketType.create!(name: "VIP", description: "A VIP ticket for the event", price_cents: 5000, quantity: 50, sale_start_at: 1.day.ago, sale_end_at: event.start_time - 1.hour, event: event)
|
|
|
|
# Duplicate the event
|
|
duplicated_event = event.duplicate
|
|
|
|
# Check that duplication was successful
|
|
assert_not_nil duplicated_event
|
|
assert_equal "Copie de #{event.name}", duplicated_event.name
|
|
assert_equal "draft", duplicated_event.state
|
|
assert_equal event.venue_name, duplicated_event.venue_name
|
|
assert_equal event.venue_address, duplicated_event.venue_address
|
|
|
|
# Check that ticket types were duplicated
|
|
assert_equal 2, duplicated_event.ticket_types.count
|
|
assert_equal "Standard", duplicated_event.ticket_types.find_by(name: "Standard").name
|
|
assert_equal "VIP", duplicated_event.ticket_types.find_by(name: "VIP").name
|
|
end
|
|
|
|
test "should duplicate event without ticket types" do
|
|
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
|
|
event = Event.create!(name: "Original Event", slug: "original", description: "A description that is sufficiently long", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.week.from_now, state: :published)
|
|
|
|
# Create ticket types
|
|
ticket_type1 = TicketType.create!(name: "Standard", description: "A standard ticket for the event", price_cents: 2000, quantity: 100, sale_start_at: 1.day.ago, sale_end_at: event.start_time - 1.hour, event: event)
|
|
ticket_type2 = TicketType.create!(name: "VIP", description: "A VIP ticket for the event", price_cents: 5000, quantity: 50, sale_start_at: 1.day.ago, sale_end_at: event.start_time - 1.hour, event: event)
|
|
|
|
# Duplicate the event without ticket types
|
|
duplicated_event = event.duplicate(clone_ticket_types: false)
|
|
|
|
# Check that duplication was successful
|
|
assert_not_nil duplicated_event
|
|
assert_equal "Copie de #{event.name}", duplicated_event.name
|
|
assert_equal "draft", duplicated_event.state
|
|
assert_equal event.venue_name, duplicated_event.venue_name
|
|
assert_equal event.venue_address, duplicated_event.venue_address
|
|
|
|
# Check that ticket types were NOT duplicated
|
|
assert_equal 0, duplicated_event.ticket_types.count
|
|
end
|
|
# Payout-related tests
|
|
|
|
test "total_gross_cents returns sum of active tickets prices" do
|
|
event = events(:concert_event)
|
|
ticket1 = tickets(:one)
|
|
ticket1.status = "active"
|
|
ticket1.price_cents = 1000
|
|
ticket1.save!
|
|
|
|
ticket2 = Ticket.create!(order: orders(:one), ticket_type: ticket_types(:one), qr_code: "qr2", price_cents: 2000, status: "active", first_name: "Test", last_name: "User")
|
|
ticket2.event = event
|
|
ticket2.save!
|
|
|
|
assert_equal 3000, event.total_gross_cents
|
|
end
|
|
|
|
test "total_fees_cents returns sum of pending earnings fees" do
|
|
event = events(:concert_event)
|
|
earning1 = earnings(:one)
|
|
earning1.status = "pending"
|
|
earning1.fee_cents = 100
|
|
earning1.save!
|
|
|
|
earning2 = Earning.create!(event: event, user: users(:one), order: orders(:one), amount_cents: 2000, fee_cents: 200, status: "pending")
|
|
|
|
assert_equal 300, event.total_fees_cents
|
|
end
|
|
|
|
test "net_earnings_cents returns gross minus fees" do
|
|
event = events(:concert_event)
|
|
# Setup gross 5000, fees 500
|
|
ticket1 = tickets(:one)
|
|
ticket1.status = "active"
|
|
ticket1.price_cents = 2500
|
|
ticket1.save!
|
|
|
|
ticket2 = Ticket.create!(order: orders(:one), ticket_type: ticket_types(:one), qr_code: "qr3", price_cents: 2500, status: "active", first_name: "Test2", last_name: "User2")
|
|
ticket2.event = event
|
|
ticket2.save!
|
|
|
|
earning1 = earnings(:one)
|
|
earning1.status = "pending"
|
|
earning1.fee_cents = 250
|
|
earning1.save!
|
|
|
|
earning2 = Earning.create!(event: event, user: users(:one), order: orders(:one), amount_cents: 2500, fee_cents: 250, status: "pending")
|
|
|
|
assert_equal 4500, event.net_earnings_cents
|
|
end
|
|
|
|
test "can_request_payout? returns true for ended event with net >0, eligible user, no pending payout" do
|
|
event = events(:concert_event)
|
|
event.update!(end_time: 1.day.ago) # ended
|
|
# Setup net >0
|
|
ticket = Ticket.create!(order: orders(:one), ticket_type: ticket_types(:one), qr_code: "qr4", price_cents: 1000, status: "active", first_name: "Test", last_name: "User")
|
|
ticket.event = event
|
|
ticket.save!
|
|
|
|
earning = Earning.create!(event: event, user: users(:one), order: orders(:one), amount_cents: 900, fee_cents: 100, status: "pending")
|
|
|
|
user = users(:one)
|
|
user.update!(is_professionnal: true) # eligible
|
|
|
|
# No pending payout
|
|
assert event.can_request_payout?(user)
|
|
end
|
|
|
|
test "can_request_payout? returns false for not ended event" do
|
|
event = events(:concert_event)
|
|
event.update!(end_time: 1.day.from_now) # not ended
|
|
user = users(:one)
|
|
user.update!(is_professionnal: true)
|
|
|
|
assert_not event.can_request_payout?(user)
|
|
end
|
|
|
|
test "can_request_payout? returns false if net <=0" do
|
|
event = events(:concert_event)
|
|
event.update!(end_time: 1.day.ago)
|
|
user = users(:one)
|
|
user.update!(is_professionnal: true)
|
|
|
|
assert_not event.can_request_payout?(user)
|
|
end
|
|
|
|
test "can_request_payout? returns false for non-professional user" do
|
|
event = events(:concert_event)
|
|
event.update!(end_time: 1.day.ago)
|
|
# Setup net >0
|
|
ticket = Ticket.create!(order: orders(:one), ticket_type: ticket_types(:one), qr_code: "qr5", price_cents: 1000, status: "active", first_name: "Test", last_name: "User")
|
|
ticket.event = event
|
|
ticket.save!
|
|
|
|
earning = Earning.create!(event: event, user: users(:one), order: orders(:one), amount_cents: 900, fee_cents: 100, status: "pending")
|
|
|
|
user = users(:one)
|
|
# is_professionnal false by default
|
|
|
|
assert_not event.can_request_payout?(user)
|
|
end
|
|
|
|
test "can_request_payout? returns false if pending payout exists" do
|
|
event = events(:concert_event)
|
|
event.update!(end_time: 1.day.ago)
|
|
# Setup net >0
|
|
ticket = Ticket.create!(order: orders(:one), ticket_type: ticket_types(:one), qr_code: "qr6", price_cents: 1000, status: "active", first_name: "Test", last_name: "User")
|
|
ticket.event = event
|
|
ticket.save!
|
|
|
|
earning = Earning.create!(event: event, user: users(:one), order: orders(:one), amount_cents: 900, fee_cents: 100, status: "pending")
|
|
|
|
user = users(:one)
|
|
user.update!(is_professionnal: true)
|
|
|
|
Payout.create!(user: user, event: event, amount_cents: 800, fee_cents: 100, status: :pending)
|
|
|
|
assert_not event.can_request_payout?(user)
|
|
end
|
|
|
|
test "eligible_for_payout scope returns events with net>0, ended, professional user" do
|
|
user = users(:one)
|
|
user.update!(is_professionnal: true)
|
|
|
|
eligible = Event.create!(name: "Eligible", slug: "eligible", description: "desc", venue_name: "v", venue_address: "a", latitude: 48.0, longitude: 2.0, start_time: 1.day.ago, end_time: 2.days.ago, user: user, state: :published)
|
|
# Setup net >0
|
|
ticket = Ticket.create!(order: orders(:one), ticket_type: ticket_types(:one), qr_code: "qr7", price_cents: 1000, status: "active", first_name: "Test", last_name: "User")
|
|
ticket.event = eligible
|
|
ticket.save!
|
|
|
|
earning = Earning.create!(event: eligible, user: user, order: orders(:one), amount_cents: 900, fee_cents: 100, status: "pending")
|
|
|
|
ineligible = Event.create!(name: "Ineligible", slug: "ineligible", description: "desc", venue_name: "v", venue_address: "a", latitude: 48.0, longitude: 2.0, start_time: 1.day.from_now, end_time: 2.days.from_now, user: user, state: :published)
|
|
# net =0
|
|
|
|
eligible_events = Event.eligible_for_payout
|
|
assert_includes eligible_events, eligible
|
|
assert_not_includes eligible_events, ineligible
|
|
end
|
|
end
|