jam-cloud/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb

582 lines
24 KiB
Ruby

require 'spec_helper'
describe "TestDrive Lesson Flow" do
let(:user) { FactoryGirl.create(:user, remaining_test_drives: 0) }
let(:teacher_user) { FactoryGirl.create(:teacher_user) }
let(:teacher_user2) { FactoryGirl.create(:teacher_user) }
let(:teacher) { teacher_user.teacher }
let(:lesson_booking_slot_single1) { FactoryGirl.build(:lesson_booking_slot_single) }
let(:lesson_booking_slot_single2) { FactoryGirl.build(:lesson_booking_slot_single) }
let(:lesson_booking_slot_recurring1) { FactoryGirl.build(:lesson_booking_slot_recurring) }
let(:lesson_booking_slot_recurring2) { FactoryGirl.build(:lesson_booking_slot_recurring) }
let(:valid_single_slots) { [lesson_booking_slot_single1, lesson_booking_slot_single2] }
let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
let(:affiliate_partner) { FactoryGirl.create(:affiliate_partner) }
let(:affiliate_partner2) { FactoryGirl.create(:affiliate_partner, lesson_rate: 0.30) }
let(:school) { FactoryGirl.create(:school) }
let(:card_lessons) {FactoryGirl.create(:posa_card, card_type: JamRuby::PosaCardType::JAM_CLASS_4)}
let(:retailer) {FactoryGirl.create(:retailer)}
before {
teacher.stripe_account_id = stripe_account1_id
teacher.save!
}
after {
Timecop.return
}
it "works" do
user.desired_package = LessonPackageType.test_drive_2
user.save!
# user has no test drives, no credit card on file, but attempts to book a lesson
booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.")
booking.errors.any?.should be_false
booking.card_presumed_ok.should be_false
booking.user.should eql user
booking.card_presumed_ok.should be_false
booking.should eql user.unprocessed_test_drive
booking.sent_notices.should be_false
teacher_user.has_booked_test_drive_with_student?(user).should be_true
user.reload
user.remaining_test_drives.should eql 0
########## Need validate their credit card
token = create_stripe_token
result = user.payment_update({token: token, zip: '78759', test_drive: true, booking_id: booking.id})
booking = result[:lesson]
booking.errors.any?.should be_false
lesson = booking.lesson_sessions[0]
lesson.errors.any?.should be_false
test_drive = result[:test_drive]
test_drive.errors.any?.should be_false
user.reload
user.remaining_test_drives.should eql 1
booking.card_presumed_ok.should be_true
booking.sent_notices.should be_true
lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
lesson.reload
test_drive.stripe_charge_id.should_not be_nil
test_drive.recurly_tax_in_cents.should be 247
test_drive.recurly_total_in_cents.should eql 2999 + 247
test_drive.recurly_subtotal_in_cents.should eql 2999
test_drive.recurly_currency.should eql 'USD'
line_item = test_drive.sale_line_items[0]
line_item.quantity.should eql 1
line_item.product_type.should eql SaleLineItem::LESSON
line_item.product_id.should eq LessonPackageType.test_drive_2.id
user.reload
user.stripe_customer_id.should_not be nil
user.lesson_purchases.length.should eql 1
user.remaining_test_drives.should eql 1
lesson_purchase = user.lesson_purchases[0]
lesson_purchase.price.should eql 29.99
lesson_purchase.lesson_package_type.is_test_drive?.should eql true
customer = Stripe::Customer.retrieve(user.stripe_customer_id)
customer.email.should eql user.email
booking.lesson_sessions.length.should eql 1
lesson_session = booking.lesson_sessions[0]
lesson_session.status.should eql LessonBooking::STATUS_REQUESTED
booking.status.should eql LessonBooking::STATUS_REQUESTED
######### Teacher counters with new slot
teacher_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 14)
UserMailer.deliveries.clear
lesson_session.counter({proposer: teacher_user, slot: teacher_countered_slot, message: 'Does this work?'})
booking.reload
booking.errors.any?.should be false
lesson_session.lesson_booking.errors.any?.should be false
lesson_session.lesson_booking_slots.length.should eql 1
lesson_session.lesson_booking_slots[0].proposer.should eql teacher_user
teacher_counter = lesson_session.lesson_booking_slots.order(:created_at).last
teacher_counter.should eql teacher_countered_slot
teacher_counter.proposer.should eql teacher_user
booking.lesson_booking_slots.length.should eql 3
UserMailer.deliveries.length.should eql 1
chat = ChatMessage.unscoped.order(:created_at).last
chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.message.should eql 'Does this work?'
chat.user.should eql teacher_user
chat.target_user.should eql user
notification = Notification.unscoped.order(:created_at).last
notification.session_id.should eql lesson_session.music_session.id
notification.student_directed.should eql true
notification.purpose.should eql 'counter'
notification.description.should eql NotificationTypes::LESSON_MESSAGE
######### Student counters with new slot
student_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 16)
UserMailer.deliveries.clear
lesson_session.counter({proposer: user, slot: student_countered_slot, message: 'Does this work better?'})
lesson_session.errors.any?.should be false
lesson_session.lesson_booking.errors.any?.should be false
lesson_session.lesson_booking_slots.length.should eql 2
student_counter = booking.lesson_booking_slots.order(:created_at).last
student_counter.proposer.should eql user
booking.reload
booking.lesson_booking_slots.length.should eql 4
UserMailer.deliveries.length.should eql 1
chat = ChatMessage.unscoped.order(:created_at).last
chat.message.should eql 'Does this work better?'
chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.user.should eql user
chat.target_user.should eql teacher_user
notification = Notification.unscoped.order(:created_at).last
notification.session_id.should eql lesson_session.music_session.id
notification.student_directed.should eql false
notification.purpose.should eql 'counter'
notification.description.should eql NotificationTypes::LESSON_MESSAGE
######## Teacher accepts slot
UserMailer.deliveries.clear
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false, accepter: teacher_user})
lesson_session.errors.any?.should be_false
lesson_session.reload
lesson_session.slot.should eql student_counter
lesson_session.status.should eql LessonSession::STATUS_APPROVED
booking.reload
booking.default_slot.should eql student_counter
lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
booking.status.should eql LessonBooking::STATUS_APPROVED
UserMailer.deliveries.length.should eql 2
chat = ChatMessage.unscoped.order(:created_at).last
chat.message.should eql 'Yeah I got this'
chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.user.should eql teacher_user
chat.target_user.should eql user
notification = Notification.unscoped.order(:created_at).last
notification.session_id.should eql lesson_session.music_session.id
notification.student_directed.should eql true
notification.purpose.should eql 'accept'
notification.description.should eql NotificationTypes::LESSON_MESSAGE
notification.message.should be_nil
# teacher & student get into session
start = lesson_session.scheduled_start
end_time = lesson_session.scheduled_start + (60 * lesson_session.duration)
uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson_session.music_session, created_at: start, session_removed_at: end_time)
# artificially end the session, which is covered by other background jobs
lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save!
Timecop.travel(end_time + 1)
UserMailer.deliveries.clear
# background code comes around and analyses the session
lesson.analyse
lesson.session_completed
lesson_session.reload
lesson_session.analysed.should be_true
analysis = lesson_session.analysis
analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT
analysis["student"].should eql LessonSessionAnalyser::NO_SHOW
lesson_session.billed.should be false
if lesson_session.billing_error_detail
puts "testdrive flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
end
lesson_session.billing_error_reason.should be_nil
lesson_session.sent_notices.should be true
purchase = lesson_session.lesson_package_purchase
purchase.should_not be_nil
purchase.price_in_cents.should eql 2999
purchase.lesson_package_type.is_test_drive?.should be true
user.reload
user.remaining_test_drives.should eql 1
UserMailer.deliveries.length.should eql 2 # one for student, one for teacher
found_student_email = false
UserMailer.deliveries.each do |d|
if d.subject == "You have used 1 of 2 TestDrive lesson credits"
found_student_email = true
end
end
found_student_email.should be_true
teacher_distribution = lesson_session.teacher_distribution
teacher_distribution.amount_in_cents.should eql 1000
teacher_distribution.ready.should be_true
teacher_distribution.distributed.should be_false
LessonBooking.hourly_check
LessonSession.hourly_check
teacher_distribution.reload
teacher_distribution.amount_in_cents.should eql 1000
teacher_distribution.ready.should be_true
teacher_distribution.distributed.should be_false
TeacherPayment.count.should eql 0
TeacherPayment.hourly_check
TeacherPayment.count.should eql 1
lesson_session.reload
purchase.reload
purchase.teacher_distribution.should be_nil
teacher_payment = TeacherPayment.first
teacher_payment.amount_in_cents.should eql 1000
teacher_payment.fee_in_cents.should eql 0
teacher_payment.teacher.should eql teacher_user
teacher_distribution.reload
teacher_distribution.amount_in_cents.should eql 1000
teacher_distribution.ready.should be_true
teacher_distribution.distributed.should be_true
teacher_payment.teacher_payment_charge.amount_in_cents.should eql (1000 + 1000 * APP_CONFIG.stripe[:ach_pct]).round
teacher_payment.teacher_payment_charge.fee_in_cents.should eql 0
user.sales.count.should eql 1
sale = user.sales[0]
sale.sale_line_items.count.should eql 1
sale.sale_line_items[0].affiliate_distributions.count.should eql 0
lesson_session.lesson_booking.status.should eql LessonBooking::STATUS_COMPLETED
lesson_session.lesson_booking.success.should be_true
LessonBooking.bookings(user, teacher_user, nil).count.should eql 1
LessonBooking.engaged_bookings(user, teacher_user, nil).count.should eql 1
teacher_user.has_booked_test_drive_with_student?(user).should be_true
end
it "works using posa card" do
PosaCard.activate(card_lessons, retailer)
card_lessons.reload
card_lessons.claim(user)
card_lessons.errors.any?.should be false
user.reload
user.jamclass_credits.should eql 4
# user has no test drives, no credit card on file, but attempts to book a lesson
booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.")
booking.errors.any?.should be_false
booking.card_presumed_ok.should be_false
booking.user.should eql user
booking.sent_notices.should be_true
booking.posa_card.should eql card_lessons
user.unprocessed_test_drive.should be_nil
teacher_user.has_booked_test_drive_with_student?(user).should be_true
user.reload
user.jamclass_credits.should eql 3
lesson_session = booking.lesson_sessions[0]
lesson_session.posa_card.should eql card_lessons
lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
lesson_session.reload
#booking.lesson_package_purchases.should eql [card_lessons.lesson_package_purchase]
user.stripe_customer_id.should be nil
user.lesson_purchases.length.should eql 1
lesson_purchase = user.lesson_purchases[0]
lesson_purchase.price.should eql 49.99
lesson_purchase.lesson_package_type.is_test_drive?.should eql true
lesson_purchase.posa_card.should eql card_lessons
lesson_session.status.should eql LessonBooking::STATUS_REQUESTED
booking.status.should eql LessonBooking::STATUS_REQUESTED
######### Teacher counters with new slot
teacher_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 14)
UserMailer.deliveries.clear
lesson_session.counter({proposer: teacher_user, slot: teacher_countered_slot, message: 'Does this work?'})
booking.reload
booking.errors.any?.should be false
lesson_session.lesson_booking.errors.any?.should be false
lesson_session.lesson_booking_slots.length.should eql 1
lesson_session.lesson_booking_slots[0].proposer.should eql teacher_user
teacher_counter = lesson_session.lesson_booking_slots.order(:created_at).last
teacher_counter.should eql teacher_countered_slot
teacher_counter.proposer.should eql teacher_user
booking.lesson_booking_slots.length.should eql 3
UserMailer.deliveries.length.should eql 1
chat = ChatMessage.unscoped.order(:created_at).last
chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.message.should eql 'Does this work?'
chat.user.should eql teacher_user
chat.target_user.should eql user
notification = Notification.unscoped.order(:created_at).last
notification.session_id.should eql lesson_session.music_session.id
notification.student_directed.should eql true
notification.purpose.should eql 'counter'
notification.description.should eql NotificationTypes::LESSON_MESSAGE
######### Student counters with new slot
student_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 16)
UserMailer.deliveries.clear
lesson_session.counter({proposer: user, slot: student_countered_slot, message: 'Does this work better?'})
lesson_session.errors.any?.should be false
lesson_session.lesson_booking.errors.any?.should be false
lesson_session.lesson_booking_slots.length.should eql 2
student_counter = booking.lesson_booking_slots.order(:created_at).last
student_counter.proposer.should eql user
booking.reload
booking.lesson_booking_slots.length.should eql 4
UserMailer.deliveries.length.should eql 1
chat = ChatMessage.unscoped.order(:created_at).last
chat.message.should eql 'Does this work better?'
chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.user.should eql user
chat.target_user.should eql teacher_user
notification = Notification.unscoped.order(:created_at).last
notification.session_id.should eql lesson_session.music_session.id
notification.student_directed.should eql false
notification.purpose.should eql 'counter'
notification.description.should eql NotificationTypes::LESSON_MESSAGE
######## Teacher accepts slot
UserMailer.deliveries.clear
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false, accepter: teacher_user})
lesson_session.errors.any?.should be_false
lesson_session.reload
lesson_session.slot.should eql student_counter
lesson_session.status.should eql LessonSession::STATUS_APPROVED
booking.reload
booking.default_slot.should eql student_counter
lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
booking.status.should eql LessonBooking::STATUS_APPROVED
UserMailer.deliveries.length.should eql 2
chat = ChatMessage.unscoped.order(:created_at).last
chat.message.should eql 'Yeah I got this'
chat.channel.should eql ChatMessage::CHANNEL_LESSON
chat.user.should eql teacher_user
chat.target_user.should eql user
notification = Notification.unscoped.order(:created_at).last
notification.session_id.should eql lesson_session.music_session.id
notification.student_directed.should eql true
notification.purpose.should eql 'accept'
notification.description.should eql NotificationTypes::LESSON_MESSAGE
notification.message.should be_nil
# teacher & student get into session
start = lesson_session.scheduled_start
end_time = lesson_session.scheduled_start + (60 * lesson_session.duration)
uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson_session.music_session, created_at: start, session_removed_at: end_time)
# artificially end the session, which is covered by other background jobs
lesson_session.music_session.session_removed_at = end_time
lesson_session.music_session.save!
Timecop.travel(end_time + 1)
UserMailer.deliveries.clear
# background code comes around and analyses the session
lesson_session.analyse
lesson_session.session_completed
lesson_session.reload
lesson_session.analysed.should be_true
analysis = lesson_session.analysis
analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT
analysis["student"].should eql LessonSessionAnalyser::NO_SHOW
lesson_session.billed.should be false
if lesson_session.billing_error_detail
puts "testdrive flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
end
lesson_session.billing_error_reason.should be_nil
lesson_session.sent_notices.should be true
purchase = lesson_session.lesson_package_purchase
purchase.should_not be_nil
purchase.price_in_cents.should eql 4999
purchase.lesson_package_type.is_test_drive?.should be true
user.reload
user.remaining_test_drives.should eql 0
UserMailer.deliveries.length.should eql 2 # one for student, one for teacher
found_student_email = false
UserMailer.deliveries.each do |d|
puts d.subject
if d.subject == "You have used 1 of 4 TestDrive lesson credits"
found_student_email = true
end
end
found_student_email.should be_true
teacher_distribution = lesson_session.teacher_distribution
teacher_distribution.amount_in_cents.should eql 1000
teacher_distribution.ready.should be_true
teacher_distribution.distributed.should be_false
LessonBooking.hourly_check
LessonSession.hourly_check
teacher_distribution.reload
teacher_distribution.amount_in_cents.should eql 1000
teacher_distribution.ready.should be_true
teacher_distribution.distributed.should be_false
TeacherPayment.count.should eql 0
TeacherPayment.hourly_check
TeacherPayment.count.should eql 1
lesson_session.reload
purchase.reload
purchase.teacher_distribution.should be_nil
teacher_payment = TeacherPayment.first
teacher_payment.amount_in_cents.should eql 1000
teacher_payment.fee_in_cents.should eql 0
teacher_payment.teacher.should eql teacher_user
teacher_distribution.reload
teacher_distribution.amount_in_cents.should eql 1000
teacher_distribution.ready.should be_true
teacher_distribution.distributed.should be_true
teacher_payment.teacher_payment_charge.amount_in_cents.should eql (1000 + 1000 * APP_CONFIG.stripe[:ach_pct]).round
teacher_payment.teacher_payment_charge.fee_in_cents.should eql 0
user.sales.count.should eql 1
sale = user.sales[0]
sale.sale_line_items.count.should eql 1
sale.sale_line_items[0].affiliate_distributions.count.should eql 0
lesson_session.lesson_booking.status.should eql LessonBooking::STATUS_COMPLETED
lesson_session.lesson_booking.success.should be_true
LessonBooking.bookings(user, teacher_user, nil).count.should eql 1
LessonBooking.engaged_bookings(user, teacher_user, nil).count.should eql 1
teacher_user.has_booked_test_drive_with_student?(user).should be_true
end
# VRFS-4069
it "cancels with no credit card" do
slots = []
slots << FactoryGirl.build(:lesson_booking_slot_single)
slots << FactoryGirl.build(:lesson_booking_slot_single)
booking = LessonBooking.book_test_drive(user, teacher_user, slots, "Hey I've heard of you before.")
booking.errors.any?.should be_false
lesson = booking.lesson_sessions[0]
booking.card_presumed_ok.should be_false
user.reload
user.remaining_test_drives.should eql 0
lesson.cancel({canceler: user, message: "sorry about that"})
user.reload
user.remaining_test_drives.should eql 0
booking.reload
booking.status.should eql LessonBooking::STATUS_CANCELED
teacher_user.has_booked_test_drive_with_student?(user).should be_false
end
it "cancels with credit card" do
lesson = testdrive_lesson(user, teacher_user)
user.reload
user.remaining_test_drives.should eql 3
lesson.cancel({canceler: user, message: "sorry about that"})
user.reload
user.remaining_test_drives.should eql 4
end
it "post-lesson emails" do
UserMailer.deliveries.clear
lesson = testdrive_lesson(user, teacher_user, {accept:true, success: true})
user.reload
user.remaining_test_drives.should eql 3
found_student_email = false
UserMailer.deliveries.each do |d|
if d.subject == "You have used 1 of 4 TestDrive lesson credits"
found_student_email = true
end
end
found_student_email.should be_true
end
# tests that the correct emails are sent out after test drives are done
it "both no show, then student no shows" do
UserMailer.deliveries.clear
lesson = testdrive_lesson(user, teacher_user, {accept: true, miss: true, package_count: 2})
found_no_credit_email = false
found_no_success_email = false
UserMailer.deliveries.each do |d|
if d.subject == "Your TestDrive with #{teacher_user.name} will not use a credit"
found_no_credit_email = true
end
if d.subject == "Your TestDrive with #{user.name} was not successful"
found_no_success_email = true
end
end
found_no_credit_email.should be_true
found_no_success_email.should be_true
UserMailer.deliveries.clear
user.reload
user.remaining_test_drives.should eql 2
lesson = testdrive_lesson(user, teacher_user, {accept: true, finish: true})
user.reload
user.remaining_test_drives.should eql 1
completed_test_drive = false
done_not_completed = false
paid_but_no_show = false
UserMailer.deliveries.each do |d|
if d.subject == "You have used #{user.remaining_test_drives} of #{user.total_test_drives} TestDrive lesson credits"
done_not_completed = true
end
if d.subject == "You have used all TestDrive lesson credits"
completed_test_drive = true
end
if d.subject == "You successfully completed a lesson with #{user.name}"
paid_but_no_show = true
end
end
done_not_completed.should be_true
paid_but_no_show.should be_true
completed_test_drive.should be_false
UserMailer.deliveries.clear
# now use last credit
lesson = testdrive_lesson(user, teacher_user2, {accept: true, finish: true, student_show: true})
user.reload
user.remaining_test_drives.should eql 0
completed_test_drive = true
paid = false
UserMailer.deliveries.each do |d|
if d.subject == "You have used all TestDrive lesson credits"
completed_test_drive = true
end
if d.subject == "You successfully completed a lesson with #{user.name}"
paid = true
end
end
# You successfully completed a lesson with #{@student.name}"
completed_test_drive.should be_true
paid.should be_true
end
end