require 'spec_helper' # collissions with teacher's schedule? describe LessonBooking do let(:user) { FactoryGirl.create(:user, stored_credit_card: false, remaining_free_lessons: 1, remaining_test_drives: 1) } let(:teacher_user) { 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] } describe "suspend!" do it "should set status as well as update status of all associated lesson_sessions" do booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60) booking.lesson_sessions[0].accept({accepter: teacher_user, message: "got it", slot: booking.lesson_booking_slots[0].id}) booking.reload booking.active.should eql true booking.suspend! booking.errors.any?.should be false booking.reload booking.active.should eql false booking.status.should eql LessonBooking::STATUS_SUSPENDED booking.lesson_sessions.count.should eql 2 booking.lesson_sessions.each do |lesson_session| lesson_session.status.should eql LessonBooking::STATUS_SUSPENDED end end end describe "bill_monthlies" do after do Timecop.return end it "empty" do LessonBooking.count.should eql 0 LessonBooking.bill_monthlies LessonPackagePurchase.count.should eql 0 end it "one" do day = Date.new(2016, 1, 1) time = day.to_time Timecop.freeze(time) booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60) booking.accept(booking.lesson_sessions[0], booking.lesson_booking_slots[0], teacher_user) booking.errors.any?.should be false LessonBooking.count.should eql 1 LessonPackagePurchase.count.should eql 0 LessonBooking.bill_monthlies LessonPackagePurchase.count.should eql 1 purchase = LessonPackagePurchase.first purchase.billing_error_reason.should eql "stripe" purchase.billing_error_detail.should include("Cannot charge a customer that has no active card") purchase.month.should eql 1 purchase.year.should eql 2016 purchase.lesson_booking.should eql booking purchase.teacher.should eql teacher_user purchase.user.should eql user purchase.billed.should eql false purchase.billed_at.should be_nil purchase.billing_attempts.should eql 1 purchase.post_processed.should eql false purchase.post_processed_at.should be_nil purchase.last_billing_attempt_at.should eql time purchase.sent_billing_notices.should eql false # don't advance time, but nothing should happen because last_billing time hasn't elapsed LessonBooking.bill_monthlies LessonPackagePurchase.count.should eql 1 purchase.reload purchase.billing_error_reason.should eql "stripe" purchase.billing_error_detail.should include("Cannot charge a customer that has no active card") purchase.month.should eql 1 purchase.year.should eql 2016 purchase.lesson_booking.should eql booking purchase.teacher.should eql teacher_user purchase.user.should eql user purchase.billed.should eql false purchase.billed_at.should eql nil purchase.billing_attempts.should eql 1 purchase.post_processed.should eql false purchase.post_processed_at.should be_nil purchase.last_billing_attempt_at.should eql time purchase.sent_billing_notices.should eql false day = day + 1 time = day.to_time Timecop.freeze(time) LessonBooking.bill_monthlies LessonPackagePurchase.count.should eql 1 purchase.reload purchase.billing_error_reason.should eql "stripe" purchase.billing_error_detail.should include("Cannot charge a customer that has no active card") purchase.month.should eql 1 purchase.year.should eql 2016 purchase.lesson_booking.should eql booking purchase.teacher.should eql teacher_user purchase.user.should eql user purchase.billed.should eql false purchase.billed_at.should eql nil purchase.billing_attempts.should eql 2 purchase.post_processed.should eql false purchase.post_processed_at.should be_nil purchase.last_billing_attempt_at.should eql time purchase.sent_billing_notices.should eql false user.card_approved(create_stripe_token, '78759', booking.id) user.save! day = day + 1 time = day.to_time Timecop.freeze(time) LessonBooking.bill_monthlies LessonPackagePurchase.count.should eql 1 purchase.reload purchase.month.should eql 1 purchase.year.should eql 2016 purchase.lesson_booking.should eql booking purchase.teacher.should eql teacher_user purchase.user.should eql user purchase.billed.should eql true purchase.billed_at.should eql time # purchase.billing_error_reason.should be nil purchase.billing_attempts.should eql 3 purchase.post_processed.should eql true purchase.post_processed_at.should eql time purchase.last_billing_attempt_at.should eql time purchase.sent_billing_notices.should eql true end it "advances to next month" do user.card_approved(create_stripe_token, '78759', nil) user.save! day = Date.new(2016, 1, 20) time = day.to_time Timecop.freeze(time) booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60) booking.accept(booking.lesson_sessions[0], booking.lesson_booking_slots[0], teacher_user) booking.errors.any?.should be false LessonBooking.count.should eql 1 LessonPackagePurchase.count.should eql 0 LessonBooking.bill_monthlies LessonPackagePurchase.count.should eql 1 LessonSession.count.should eql 2 purchase = LessonPackagePurchase.first purchase.month.should eql 1 purchase.year.should eql 2016 purchase.lesson_booking.should eql booking purchase.teacher.should eql teacher_user purchase.user.should eql user purchase.billed.should eql true purchase.billed_at.should eql time # purchase.billing_error_reason.should be nil purchase.billing_attempts.should eql 1 purchase.post_processed.should eql true purchase.post_processed_at.should eql time purchase.last_billing_attempt_at.should eql time purchase.sent_billing_notices.should eql true day = Date.new(2016, 1, 27) time = day.to_time Timecop.freeze(time) LessonBooking.schedule_upcoming_lessons LessonSession.count.should eql 3 LessonPackagePurchase.count.should eql 1 LessonBooking.bill_monthlies LessonPackagePurchase.count.should eql 2 purchase = LessonPackagePurchase.order(:month).last purchase.month.should eql 2 purchase.year.should eql 2016 purchase.lesson_booking.should eql booking purchase.teacher.should eql teacher_user purchase.user.should eql user purchase.billed.should eql true purchase.billed_at.should eql time # purchase.billing_error_reason.should be nil purchase.billing_attempts.should eql 1 purchase.post_processed.should eql true purchase.post_processed_at.should eql time purchase.last_billing_attempt_at.should eql time purchase.sent_billing_notices.should eql true end it "will suspend after enough tries" do day = Date.new(2016, 1, 1) time = day.to_time Timecop.freeze(time) booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60) booking.accept(booking.lesson_sessions[0], booking.lesson_booking_slots[0], teacher_user) booking.errors.any?.should be false LessonBooking.count.should eql 1 LessonPackagePurchase.count.should eql 0 LessonBooking.bill_monthlies LessonPackagePurchase.count.should eql 1 purchase = LessonPackagePurchase.first purchase.billing_error_reason.should eql "stripe" purchase.billing_error_detail.should include("Cannot charge a customer that has no active card") purchase.billing_attempts.should eql 1 booking.is_suspended?.should be_false purchase.billed.should be false day = day + 1 time = day.to_time Timecop.freeze(time) LessonBooking.bill_monthlies LessonPackagePurchase.count.should eql 1 purchase.reload purchase.billing_attempts.should eql 2 booking.reload booking.is_suspended?.should be_false purchase.billed.should be false day = day + 1 time = day.to_time Timecop.freeze(time) LessonBooking.bill_monthlies LessonPackagePurchase.count.should eql 1 purchase.reload purchase.billing_attempts.should eql 3 booking.reload booking.is_suspended?.should be_false purchase.billed.should be false day = day + 1 time = day.to_time Timecop.freeze(time) LessonBooking.bill_monthlies LessonPackagePurchase.count.should eql 1 purchase.reload purchase.billing_attempts.should eql 4 booking.reload booking.is_suspended?.should be_false purchase.billed.should be false day = day + 1 time = day.to_time Timecop.freeze(time) LessonBooking.bill_monthlies LessonPackagePurchase.count.should eql 1 purchase.reload purchase.billing_attempts.should eql 5 booking.reload booking.is_suspended?.should be_true purchase.billed.should be false # now that it's suspended, let's unsuspend it user.card_approved(create_stripe_token, '78759', booking.id) user.save! day = day + 1 time = day.to_time Timecop.freeze(time) purchase.bill_monthly(true) LessonPackagePurchase.count.should eql 1 purchase.reload purchase.month.should eql 1 purchase.year.should eql 2016 purchase.lesson_booking.should eql booking purchase.teacher.should eql teacher_user purchase.user.should eql user purchase.billed.should eql true purchase.billed_at.should eql time # purchase.billing_error_reason.should be nil purchase.billing_attempts.should eql 6 purchase.post_processed.should eql true purchase.post_processed_at.should eql time purchase.last_billing_attempt_at.should eql time purchase.sent_billing_notices.should eql true booking.reload booking.is_suspended?.should be_false end it "missed meetings deduct on next month" do # TODO. Discuss with David a little more end end describe "billable_monthlies" do after do Timecop.return end it "empty" do LessonBooking.billable_monthlies(Time.now).count.should eql 0 end it "one" do time = Date.new(2016, 1, 1) Timecop.freeze(time) booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60) booking.accept(booking.lesson_sessions[0], booking.lesson_booking_slots[0], teacher_user) booking.errors.any?.should be false now = Time.now billables = LessonBooking.billable_monthlies(now) billables.all.to_a.should eql [booking] LessonPackagePurchase.where(lesson_booking_id: booking.id).count.should eql 0 # to make this billable monthly go away, we will need to create one LessonPackagePurchase; one for this month (because it's only one we have lessons in) # and further, mark them both as post_processed package = LessonPackagePurchase.create(user, booking, LessonPackageType.single, 2016, 1) LessonBooking.billable_monthlies(now).count.should eql 1 package.post_processed = true package.save! LessonBooking.billable_monthlies(now).count.should eql 0 end end describe "predicted_times_for_month" do after do Timecop.return end it "fills up month" do next_year = Time.now.year + 1 jan1 = Date.new(next_year, 1, 1) jan31 = Date.new(next_year, 1, -1) Timecop.freeze(jan1) slot = valid_recurring_slots[0] slot.day_of_week = jan1.wday booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60) times = booking.predicted_times_for_month(next_year, 1)[:times] times.length.should eql 5 times[0].to_date.should eql (jan1) times[1].to_date.should eql (Date.new(next_year, 1, 8)) times[2].to_date.should eql (Date.new(next_year, 1, 15)) times[3].to_date.should eql (Date.new(next_year, 1, 22)) times[4].to_date.should eql (Date.new(next_year, 1, 29)) end it "fills up partial month" do next_year = Time.now.year + 1 jan1 = Date.new(next_year, 1, 1) jan15 = Date.new(next_year, 1, 15) jan31 = Date.new(next_year, 1, -1) Timecop.freeze(jan15) slot = valid_recurring_slots[0] slot.day_of_week = jan1.wday booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60) times = booking.predicted_times_for_month(next_year, 1)[:times] times.length.should eql 3 times[0].to_date.should eql (Date.new(next_year, 1, 15)) times[1].to_date.should eql (Date.new(next_year, 1, 22)) times[2].to_date.should eql (Date.new(next_year, 1, 29)) end it "let's assume JamKazam is messed up for a few weeks" do next_year = Time.now.year + 1 jan1 = Date.new(next_year, 1, 1) jan15 = Date.new(next_year, 1, 15) jan31 = Date.new(next_year, 1, -1) Timecop.freeze(jan1) slot = valid_recurring_slots[0] slot.day_of_week = jan1.wday # book a session on jan1 booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60) # but don't run the computation of times per month for weeks Timecop.freeze(Date.new(next_year, 1, 23)) times = booking.predicted_times_for_month(next_year, 1) times[:times].length.should eql 2 times[:times][0].to_date.should eql (Date.new(next_year, 1, 1)) times[:times][1].to_date.should eql (Date.new(next_year, 1, 29)) end end describe "book_free" do it "allows long message to flow through chat" do user.has_free_lessons?.should be_true booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, Faker::Lorem.characters(10000)) booking.errors.any?.should be false chat_message = ChatMessage.where(lesson_session_id: booking.next_lesson.id).first chat_message.should_not be_nil chat_message.message.should eq booking.description end it "prevents user without free lesson" do pending "free not supported" booking = LessonBooking.book_free(user, teacher_user, valid_single_slots, "Hey I've heard of you before.") booking.errors.any?.should be false ChatMessage.count.should eq 1 booking = LessonBooking.book_free(user, teacher_user, valid_single_slots, "Hey I've heard of you before.") booking.errors.any?.should be true booking.errors[:user].should eq ["have no remaining free lessons"] ChatMessage.count.should eq 1 end it "must have 2 lesson booking slots" do booking = LessonBooking.book_test_drive(user, teacher_user, [], "Hey I've heard of you before.") booking.errors.any?.should be true booking.errors[:lesson_booking_slots].should eq ["must have two times specified"] end it "must have well-formed booking slots" do bad_slot = FactoryGirl.build(:lesson_booking_slot_single, minute: nil) booking = LessonBooking.book_free(user, teacher_user, [lesson_booking_slot_single1, bad_slot], "Hey I've heard of you before.") booking.errors.any?.should be true booking.errors[:lesson_booking_slots].should eq ["is invalid"] bad_slot = booking.lesson_booking_slots[1] bad_slot.errors[:minute].should eq ["is not a number"] end end describe "book_test_drive" do it "works" do user.stored_credit_card = true user.save! 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.user.should eq user booking.teacher.should eq teacher_user booking.description.should eq ("Hey I've heard of you before.") booking.payment_style.should eq LessonBooking::PAYMENT_STYLE_ELSEWHERE booking.recurring.should eq false booking.lesson_length.should eq 30 booking.lesson_type.should eq LessonBooking::LESSON_TYPE_TEST_DRIVE booking.lesson_booking_slots.length.should eq 2 chat_message = ChatMessage.where(lesson_session_id: booking.next_lesson.id).first chat_message.should_not be_nil chat_message.message.should eq booking.description user.reload user.remaining_free_lessons.should eq 1 user.remaining_test_drives.should eq 0 end it "allows long message to flow through chat" do booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, Faker::Lorem.characters(10000)) booking.errors.any?.should be false chat_message = ChatMessage.where(lesson_session_id: booking.next_lesson.id).first chat_message.should_not be_nil chat_message.message.should eq booking.description end it "prevents user without remaining test drives" do user.stored_credit_card = true user.save! booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.") booking.errors.any?.should be false ChatMessage.count.should eq 1 user.reload user.remaining_test_drives.should eql 0 booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.") booking.errors.any?.should be true booking.errors[:user].should eq ["have an in-progress or successful TestDrive with this teacher already"] ChatMessage.count.should eq 1 end it "must have well-formed booking slots" do bad_slot = FactoryGirl.build(:lesson_booking_slot_single, minute: nil) booking = LessonBooking.book_test_drive(user, teacher_user, [lesson_booking_slot_single1, bad_slot], "Hey I've heard of you before.") booking.errors.any?.should be true booking.errors[:lesson_booking_slots].should eq ["is invalid"] bad_slot = booking.lesson_booking_slots[1] bad_slot.errors[:minute].should eq ["is not a number"] end end describe "book_normal" do it "works" do booking = LessonBooking.book_normal(user, teacher_user, valid_single_slots, "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60) booking.errors.any?.should be false booking.user.should eq user booking.teacher.should eq teacher_user booking.description.should eq ("Hey I've heard of you before.") booking.payment_style.should eq LessonBooking::PAYMENT_STYLE_SINGLE booking.recurring.should eq false booking.lesson_length.should eq 60 booking.lesson_type.should eq LessonBooking::LESSON_TYPE_PAID booking.lesson_booking_slots.length.should eq 2 chat_message = ChatMessage.where(lesson_session_id: booking.next_lesson.id).first chat_message.should_not be_nil chat_message.message.should eq booking.description user.reload user.remaining_free_lessons.should eq 1 user.remaining_test_drives.should eq 1 end it "works with recurring slots" do booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60) booking.errors.any?.should be false booking.user.should eq user booking.teacher.should eq teacher_user booking.description.should eq ("Hey I've heard of you before.") booking.payment_style.should eq LessonBooking::PAYMENT_STYLE_WEEKLY booking.recurring.should eq true booking.lesson_length.should eq 60 booking.lesson_type.should eq LessonBooking::LESSON_TYPE_PAID booking.lesson_booking_slots.length.should eq 2 chat_message = ChatMessage.where(lesson_session_id: booking.next_lesson.id).first chat_message.should_not be_nil chat_message.message.should eq booking.description user.reload user.remaining_free_lessons.should eq 1 user.remaining_test_drives.should eq 1 end it "allows long message to flow through chat" do booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, Faker::Lorem.characters(10000), true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60) booking.errors.any?.should be false chat_message = ChatMessage.where(lesson_session_id: booking.next_lesson.id).first chat_message.should_not be_nil chat_message.message.should eq booking.description end it "does not prevent user without remaining test drives" do booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.") booking.errors.any?.should be false ChatMessage.count.should eq 1 booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60) booking.errors.any?.should be false ChatMessage.count.should eq 2 end it "does not prevents user without free lesson" do booking = LessonBooking.book_free(user, teacher_user, valid_single_slots, "Hey I've heard of you before.") booking.errors.any?.should be false ChatMessage.count.should eq 1 booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60) booking.errors.any?.should be false ChatMessage.count.should eq 2 end it "does not prevent user without a stored credit card" do user.stored_credit_card = false user.save! booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60) booking.errors.any?.should be false booking.card_presumed_ok.should eq false booking.sent_notices.should eq false end it "must have well-formed booking slots" do bad_slot = FactoryGirl.build(:lesson_booking_slot_recurring, day_of_week: nil) booking = LessonBooking.book_test_drive(user, teacher_user, [lesson_booking_slot_recurring1, bad_slot], "Hey I've heard of you before.") booking.errors.any?.should be true booking.errors[:lesson_booking_slots].should eq ["is invalid"] bad_slot = booking.lesson_booking_slots[1] bad_slot.errors[:day_of_week].should eq ["must be specified"] end end describe "find_bookings_needing_sessions" do after do Timecop.return end it "can detect missing lesson and schedules it 1 week out" do booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60) booking.lesson_sessions.length.should eql 1 booking.accept(booking.lesson_sessions[0], booking.lesson_booking_slots[0], teacher_user) booking.errors.any?.should be false booking.reload booking.lesson_sessions.length.should eql 2 lesson_session = booking.lesson_sessions[0] Timecop.freeze(lesson_session.music_session.scheduled_start) # causes find_bookings_needing_sessions to re-run booking.sync_lessons booking.reload booking.lesson_sessions.length.should eql 3 # check that all the times make sense lesson1 = booking.lesson_sessions[0] lesson2 = booking.lesson_sessions[1] lesson3 = booking.lesson_sessions[2] lesson1.music_session.scheduled_start.to_i.should eql (lesson2.music_session.scheduled_start.to_i - (60 * 60 * 24 * 7)) lesson1.music_session.scheduled_start.to_i.should eql (lesson3.music_session.scheduled_start.to_i - (60 * 60 * 24 * 14)) Timecop.freeze(lesson2.music_session.scheduled_start) # causes find_bookings_needing_sessions to re-run booking.sync_lessons booking.reload booking.lesson_sessions.length.should eql 4 # check that all the times make sense lesson4 = booking.lesson_sessions[3] lesson1.music_session.scheduled_start.to_i.should eql (lesson4.music_session.scheduled_start.to_i - (60 * 60 * 24 * 21)) end it "ignores non-recurring" do booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60) booking.lesson_sessions.length.should eql 1 booking.accept(booking.lesson_sessions[0], booking.lesson_booking_slots[0], user) booking.errors.any?.should be false booking.reload booking.lesson_sessions.length.should eql 1 booking.accepter.should eql user end end describe "canceling" do after do Timecop.return end it "single session gets canceled before accepted" do booking = LessonBooking.book_normal(user, teacher_user, valid_single_slots, "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60) lesson_session = booking.lesson_sessions[0] lesson_session.status.should eql LessonSession::STATUS_REQUESTED lesson_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) # avoid 24 hour problem UserMailer.deliveries.clear Timecop.freeze(7.days.ago) lesson_session.cancel({canceler: teacher_user, message: 'meh', slot: booking.default_slot.id, update_all: false}) lesson_session.errors.any?.should be_false lesson_session.reload lesson_session.status.should eql LessonSession::STATUS_CANCELED lesson_session.reload booking.reload booking.status.should eql LessonSession::STATUS_CANCELED UserMailer.deliveries.length.should eql 1 end it "single session gets canceled after accepted" do booking = LessonBooking.book_normal(user, teacher_user, valid_single_slots, "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60) lesson_session = booking.lesson_sessions[0] lesson_session.status.should eql LessonSession::STATUS_REQUESTED lesson_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) lesson_session.accept({accepter: teacher_user, message: 'Yeah I got this', slot: booking.default_slot.id, update_all: false}) lesson_session.errors.any?.should be_false lesson_session.status.should eql LessonSession::STATUS_APPROVED lesson_session.reload lesson_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) UserMailer.deliveries.clear Timecop.freeze(7.days.ago) lesson_session.cancel({canceler: user, message: 'meh', slot: booking.default_slot.id, update_all: false}) lesson_session.errors.any?.should be_false lesson_session.reload lesson_session.status.should eql LessonSession::STATUS_CANCELED booking.reload booking.status.should eql LessonSession::STATUS_CANCELED UserMailer.deliveries.length.should eql 2 end it "recurring session gets canceled after accepted" do booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60) booking.active.should eql false lesson_session = booking.lesson_sessions[0] lesson_session.status.should eql LessonSession::STATUS_REQUESTED lesson_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) lesson_session.accept({accepter: teacher_user, message: 'Yeah I got this', slot: booking.default_slot.id, update_all: false}) lesson_session.errors.any?.should be_false lesson_session.status.should eql LessonSession::STATUS_APPROVED lesson_session.reload lesson_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) booking.reload booking.active.should eql true UserMailer.deliveries.clear Timecop.freeze(7.days.ago) lesson_session.cancel({canceler: user, message: 'meh', slot: booking.default_slot.id, update_all: false}) lesson_session.errors.any?.should be_false lesson_session.status.should eql LessonSession::STATUS_CANCELED lesson_session.reload lesson_session.canceler.should eql user booking.reload booking.active.should eql true booking.status.should eql LessonSession::STATUS_APPROVED booking.canceler.should be_nil UserMailer.deliveries.length.should eql 2 end it "recurring booking gets canceled after accepted" do booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60) lesson_session = booking.lesson_sessions[0] lesson_session.status.should eql LessonSession::STATUS_REQUESTED lesson_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) lesson_session.accept({accepter: teacher_user, message: 'Yeah I got this', slot: booking.default_slot.id, update_all: false}) lesson_session.errors.any?.should be_false lesson_session.status.should eql LessonSession::STATUS_APPROVED lesson_session.reload lesson_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) UserMailer.deliveries.clear Timecop.freeze(7.days.ago) mailer = mock mailer.should_receive(:deliver_now).exactly(2).times UserMailer.should_receive(:student_lesson_booking_canceled).and_return(mailer) UserMailer.should_receive(:teacher_lesson_booking_canceled).and_return(mailer) UserMailer.should_receive(:student_lesson_canceled).exactly(0).times UserMailer.should_receive(:teacher_lesson_canceled).exactly(0).times lesson_session.cancel({canceler: user, message: 'meh', slot: booking.default_slot.id, update_all: true}) lesson_session.errors.any?.should be_false lesson_session.reload lesson_session.status.should eql LessonSession::STATUS_CANCELED booking.reload booking.status.should eql LessonSession::STATUS_CANCELED booking.canceler.should eql user end end describe "rescheduling" do it "initial slot is in the past" do booking = LessonBooking.book_normal(user, teacher_user, valid_single_slots, "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60) lesson_session = booking.lesson_sessions[0] initial_scheduled_time = lesson_session.scheduled_start counter = FactoryGirl.build(:lesson_booking_slot_single, preferred_day: Date.today + 20) lesson_session.counter({proposer: user, slot: counter, message: 'ACtually, let\'s do this instead for just this one'}) Timecop.travel(initial_scheduled_time + 1) lesson_session.accept({accepter: teacher_user, message: 'Yeah I got this', slot: counter.id, update_all: false}) booking.reload booking.status.should eql LessonBooking::STATUS_APPROVED booking.lesson_sessions.count.should eql 1 lesson_session.errors.any?.should be_false lesson_session.reload lesson_session.status.should eql LessonSession::STATUS_APPROVED lesson_session.scheduled_start.should eql counter.scheduled_time(0) end it "non-recurring, accepted with new slot" do booking = LessonBooking.book_normal(user, teacher_user, valid_single_slots, "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60) lesson_session = booking.lesson_sessions[0] lesson_session.status.should eql LessonSession::STATUS_REQUESTED lesson_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) counter = FactoryGirl.build(:lesson_booking_slot_single, hour: 16) lesson_session.counter({proposer: user, slot: counter, message: 'Does this work better?'}) lesson_session.errors.any?.should be false lesson_session.status.should eql LessonSession::STATUS_COUNTERED lesson_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) booking.reload booking.counter_slot.should eql counter lesson_session.accept({accepter: teacher_user, message: 'Yeah I got this', slot: counter.id, update_all: false}) lesson_session.errors.any?.should be_false lesson_session.status.should eql LessonSession::STATUS_APPROVED lesson_session.reload lesson_session.scheduled_start.should eql counter.scheduled_time(0) booking.reload booking.accepter.should eql teacher_user booking.counter_slot.should be_nil end it "recurring" do Timecop.freeze(Time.new(2016, 03, 4, 5, 0, 0)) booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_WEEKLY, 60) lesson_session = booking.lesson_sessions[0] lesson_session.status.should eql LessonSession::STATUS_REQUESTED lesson_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) counter = FactoryGirl.build(:lesson_booking_slot_recurring, day_of_week: 2, update_all: true) lesson_session.counter({proposer: user, slot: counter, message: 'Does this work better?'}) lesson_session.errors.any?.should be false lesson_session.status.should eql LessonSession::STATUS_COUNTERED lesson_session.scheduled_start.should eql booking.default_slot.scheduled_time(0) # to help scoot out the 'created_at' of the lessons Timecop.freeze(Time.now + 10) lesson_session.accept({accepter: teacher_user, message: 'Yeah I got this', slot: counter.id, update_all: false}) lesson_session.errors.any?.should be_false lesson_session.status.should eql LessonSession::STATUS_APPROVED booking.reload booking.status.should eql LessonSession::STATUS_APPROVED lesson_session.reload lesson_session.scheduled_start.should eql counter.scheduled_time(0) lesson_session2 = booking.lesson_sessions.order(:created_at)[1] lesson_session2.scheduled_start.should eql counter.scheduled_time(1) # now it's approved, we have 2 sessions that are not yet completed with a time # we should be able to reschedule just one of the lessons counter2 = FactoryGirl.build(:lesson_booking_slot_recurring, day_of_week: 4, update_all: false) lesson_session.counter({proposer: user, slot: counter2, message: 'ACtually, let\'s do this instead for just this one'}) lesson_session.errors.any?.should be false lesson_session.status.should eql LessonSession::STATUS_COUNTERED lesson_session.scheduled_start.should eql counter.scheduled_time(0) lesson_session.accept({accepter: teacher_user, message: 'OK, lets fix just this one', slot: counter2.id}) lesson_session.errors.any?.should be_false lesson_session.status.should eql LessonSession::STATUS_APPROVED booking.reload lesson_session.reload lesson_session2.reload lesson_session.scheduled_start.should eql counter2.scheduled_time(0) lesson_session2.scheduled_start.should eql counter.scheduled_time(1) # STILL ORIGINAL COUNTER! # we should be able to reschedule all of the lessons counter3 = FactoryGirl.build(:lesson_booking_slot_recurring, day_of_week: 5, update_all: true) lesson_session.counter({proposer: user, slot: counter3, message: 'ACtually, let\'s do this instead for just this one... again'}) lesson_session.errors.any?.should be false lesson_session.status.should eql LessonSession::STATUS_COUNTERED lesson_session.reload lesson_session2.reload lesson_session.scheduled_start.should eql counter2.scheduled_time(0) lesson_session2.scheduled_start.should eql counter.scheduled_time(1) booking.reload booking.counter_slot.should eql counter3 lesson_session.accept({accepter: teacher_user, message: 'OK, lets fix all of them', slot: counter3.id}) lesson_session.errors.any?.should be_false lesson_session.status.should eql LessonSession::STATUS_APPROVED booking.reload booking.counter_slot.should be_nil lesson_session.reload lesson_session2.reload lesson_session.created_at.should be < lesson_session2.created_at lesson_session.scheduled_start.should eql counter3.scheduled_time(0) lesson_session2.scheduled_start.should eql counter3.scheduled_time(1 ) end end end