jam-cloud/ruby/spec/jam_ruby/models/lesson_session_spec.rb

587 lines
23 KiB
Ruby

require 'spec_helper'
describe LessonSession do
let(:user) { FactoryGirl.create(:user, stored_credit_card: true, remaining_free_lessons: 1, remaining_test_drives: 1) }
let(:teacher) { FactoryGirl.create(:teacher_user) }
let(:slot1) { FactoryGirl.build(:lesson_booking_slot_single) }
let(:slot2) { FactoryGirl.build(:lesson_booking_slot_single) }
let(:lesson_booking) { b = LessonBooking.book_normal(user, teacher, [slot1, slot2], "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60); b.card_presumed_ok = true; b.save!; b }
let(:lesson_session) { lesson_booking.lesson_sessions[0] }
describe "counter" do
describe "recurring" do
it "counter madness" do
lesson = monthly_lesson(user, teacher, {accept: false})
# start with the student
invalid = FactoryGirl.build(:lesson_booking_slot_single, update_all: false)
lesson.counter({proposer: user, message: "crumble and bumble", slot: invalid})
lesson.errors.any?.should be_true
lesson.errors[:counter_slot].should eql ["Only 'update all' counter-proposals are allowed for un-approved, recurring lessons", "Only 'recurring' counter-proposals are allowed for un-approved, recurring lessons"]
lesson.reload
lesson.counter_slot.should be_nil
counter1 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: true)
lesson.counter({proposer: user, message: "crumble and bumble take 2", slot: counter1})
lesson.errors.any?.should be_false
lesson.reload
lesson.status.should eql LessonSession::STATUS_REQUESTED
lesson.has_recurring_counter?.should be true
lesson.counter_slot.should be_nil
lesson.lesson_booking.counter_slot.id.should eql counter1.id
counter2 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: true)
lesson.counter({proposer: teacher, message: "crumble and bumble take 3", slot: counter2})
lesson.errors.any?.should be_false
lesson.reload
lesson.status.should eql LessonSession::STATUS_REQUESTED
lesson.counter_slot.should be_nil
lesson.has_recurring_counter?.should be true
lesson.lesson_booking.counter_slot.id.should eql counter2.id
lesson.accept({accepter: user, message: "burp", slot: counter2.id})
lesson.errors.any?.should be_false
lesson.reload
lesson.counter_slot.should be_nil
lesson.status.should eql LessonSession::STATUS_APPROVED
lesson.has_recurring_counter?.should be false
counter3 = FactoryGirl.build(:lesson_booking_slot_recurring)
lesson.counter({proposer: user, message: "crumble and bumble take 4", slot: counter3})
lesson.errors.any?.should be_false
lesson.reload
lesson.counter_slot.should be_nil
lesson.status.should eql LessonSession::STATUS_APPROVED
lesson.lesson_booking.counter_slot.id.should eql counter3.id
lesson.has_recurring_counter?.should be true
counter4 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: true)
lesson.counter({proposer: teacher, message: "crumble and bumble take 5", slot: counter4})
lesson.errors.any?.should be_false
lesson.reload
lesson.counter_slot.should be_nil
lesson.status.should eql LessonSession::STATUS_APPROVED
lesson.lesson_booking.counter_slot.id.should eql counter4.id
lesson.has_recurring_counter?.should be true
end
it "simultaneous recurring and single counter and approve recurring first" do
# first create a single counter, then recurring after, and approve the recurring first without it having any effect of the single counter
lesson1 = monthly_lesson(user, teacher, {accept: true})
lesson2 = lesson1.lesson_booking.lesson_sessions.order(:created_at).last
counter1 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: true)
lesson1.counter({proposer: user, message: "crumble and bumble take 2", slot: counter1})
lesson1.errors.any?.should be_false
lesson1.reload
lesson2.reload
lesson1.status.should eql LessonSession::STATUS_APPROVED
lesson2.status.should eql LessonSession::STATUS_APPROVED
lesson1.has_recurring_counter?.should be true
lesson1.counter_slot.should be_nil
lesson1.lesson_booking.counter_slot.id.should eql counter1.id
lesson1.lesson_booking.is_countered?.should be true
counter2 = FactoryGirl.build(:lesson_booking_slot_single, update_all: true)
lesson2.counter({proposer: user, message: "crumble and bumble take 3", slot: counter2})
lesson1.errors.any?.should be_false
lesson1.reload
lesson2.reload
lesson1.status.should eql LessonSession::STATUS_APPROVED
lesson2.status.should eql LessonSession::STATUS_COUNTERED
lesson1.counter_slot.should be_nil
lesson1.has_recurring_counter?.should be true
lesson1.lesson_booking.counter_slot.id.should eql counter1.id
lesson2.counter_slot.should eql counter2
lesson1.accept({accepter: teacher, message: 'accept recurring', slot: counter1.id})
lesson2.reload
lesson1.reload
lesson1.status.should eql LessonSession::STATUS_APPROVED
lesson2.status.should eql LessonSession::STATUS_COUNTERED
lesson1.lesson_booking.counter_slot.should be_nil
lesson1.lesson_booking.is_countered?.should be false
lesson2.accept({accepter: teacher, message: 'accept single', slot: counter2.id})
lesson2.reload
lesson1.reload
lesson1.status.should eql LessonSession::STATUS_APPROVED
lesson2.status.should eql LessonSession::STATUS_APPROVED
lesson1.lesson_booking.counter_slot.should be_nil
lesson1.lesson_booking.is_countered?.should be false
end
it "simultaneous recurring and single counter and approve single first" do
# first create a single counter, then recurring after, and approve the recurring first without it having any effect of the single counter
lesson1 = monthly_lesson(user, teacher, {accept: true})
lesson2 = lesson1.lesson_booking.lesson_sessions.order(:created_at).last
counter1 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: true)
lesson1.counter({proposer: user, message: "crumble and bumble take 2", slot: counter1})
lesson1.errors.any?.should be_false
lesson1.reload
lesson2.reload
lesson1.status.should eql LessonSession::STATUS_APPROVED
lesson2.status.should eql LessonSession::STATUS_APPROVED
lesson1.lesson_booking.status.should eql LessonBooking::STATUS_COUNTERED
lesson1.has_recurring_counter?.should be true
lesson1.counter_slot.should be_nil
lesson1.lesson_booking.counter_slot.id.should eql counter1.id
lesson1.lesson_booking.is_countered?.should be true
counter2 = FactoryGirl.build(:lesson_booking_slot_single, update_all: true)
lesson2.counter({proposer: user, message: "crumble and bumble take 3", slot: counter2})
lesson1.errors.any?.should be_false
lesson1.reload
lesson2.reload
lesson1.status.should eql LessonSession::STATUS_APPROVED
lesson2.status.should eql LessonSession::STATUS_COUNTERED
lesson1.counter_slot.should be_nil
lesson1.has_recurring_counter?.should be true
lesson1.lesson_booking.counter_slot.id.should eql counter1.id
lesson2.counter_slot.should eql counter2
lesson2.accept({accepter: teacher, message: 'accept single', slot: counter2.id})
lesson2.reload
lesson1.reload
lesson1.status.should eql LessonSession::STATUS_APPROVED
lesson2.status.should eql LessonSession::STATUS_APPROVED
lesson1.lesson_booking.counter_slot.should_not be_nil
lesson1.lesson_booking.is_countered?.should be true
lesson1.accept({accepter: teacher, message: 'accept recurring', slot: counter1.id})
lesson2.reload
lesson1.reload
lesson1.status.should eql LessonSession::STATUS_APPROVED
lesson2.status.should eql LessonSession::STATUS_APPROVED
lesson1.lesson_booking.counter_slot.should be_nil
lesson1.lesson_booking.is_countered?.should be false
lesson1.lesson_booking.status.should eql LessonBooking::STATUS_APPROVED
end
it "countered lesson that goes into past gets autocancelled; affects billing" do
# first create a single counter, then recurring after, and approve the recurring first without it having any effect of the single counter
lesson1 = monthly_lesson(user, teacher, {accept: true})
lesson2 = lesson1.lesson_booking.lesson_sessions.order(:created_at).last
counter1 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: true)
lesson1.counter({proposer: user, message: "crumble and bumble take 2", slot: counter1})
lesson1.errors.any?.should be_false
lesson1.reload
lesson2.reload
lesson1.status.should eql LessonSession::STATUS_APPROVED
lesson2.status.should eql LessonSession::STATUS_APPROVED
lesson1.lesson_booking.status.should eql LessonBooking::STATUS_COUNTERED
lesson1.has_recurring_counter?.should be true
lesson1.counter_slot.should be_nil
lesson1.lesson_booking.counter_slot.id.should eql counter1.id
lesson1.lesson_booking.is_countered?.should be true
Timecop.travel(Date.today + 50)
LessonSession.analyse_sessions
lesson1.reload
lesson2.reload
lesson1.analysed.should be false
lesson2.analysed.should be false
LessonSession.auto_cancel
lesson1.reload
lesson2.reload
lesson1.status.should eql LessonSession::STATUS_UNCONFIRMED
lesson2.status.should eql LessonSession::STATUS_UNCONFIRMED
end
end
end
describe "autocancel" do
it "can't autocancel in the past" do
lesson_session.status.should eql LessonSession::STATUS_REQUESTED
lesson_session.autocancel
lesson_session.reload
lesson_session.status.should eql LessonSession::STATUS_REQUESTED
lesson_session.lesson_booking.status.should eql LessonSession::STATUS_REQUESTED
end
it "can't autocancel approved" do
lesson_session = normal_lesson(user, teacher, {accept: true})
lesson_session.status.should eql LessonSession::STATUS_APPROVED
lesson_session.autocancel
lesson_session.reload
lesson_session.status.should eql LessonSession::STATUS_APPROVED
lesson_session.lesson_booking.status.should eql LessonSession::STATUS_APPROVED
end
it "autocancel works" do
lesson_session.status.should eql LessonSession::STATUS_REQUESTED
Timecop.travel(Date.today + 10)
lesson_session.autocancel
lesson_session.reload
lesson_session.status.should eql LessonSession::STATUS_UNCONFIRMED
lesson_session.lesson_booking.status.should eql LessonSession::STATUS_UNCONFIRMED
end
it "autocancel sweeper works" do
lesson_session.status.should eql LessonSession::STATUS_REQUESTED
Timecop.travel(Date.today + 10)
LessonSession.auto_cancel
lesson_session.reload
lesson_session.lesson_booking.active.should be_false
lesson_session.status.should eql LessonSession::STATUS_UNCONFIRMED
lesson_session.lesson_booking.status.should eql LessonSession::STATUS_UNCONFIRMED
end
end
describe "slow_responses" do
it "sorts correctly" do
lesson_session1 = normal_lesson(user, teacher, {counter: true, counterer: user})
lesson_session2 = normal_lesson(user, teacher, {counter: true, counterer: teacher}) # this shouldn't show up
Timecop.travel(Date.today - 5)
lesson_session3 = normal_lesson(user, teacher, {})
slow = LessonSession.unscoped.slow_responses
slow.count.should eql 2
slow[0].should eql lesson_session3
slow[1].should eql lesson_session1
end
end
describe "least_time_left" do
it "sorts correctly" do
lesson_session1 = normal_lesson(user, teacher, {counter: true, counterer: user})
lesson_session2 = normal_lesson(user, teacher, {counter: true, counterer: teacher}) # this shouldn't show up
Timecop.travel(Date.today - 5)
lesson_session3 = normal_lesson(user, teacher, {})
slow = LessonSession.unscoped.least_time_left
slow.count.should eql 2
slow[0].should eql lesson_session1
slow[1].should eql lesson_session3
end
end
describe "remind_counters" do
it "finds old requested and pokes teacher" do
lesson_session1 = normal_lesson(user, teacher, {})
lesson_session1.sent_counter_reminder.should be_false
lesson_session1.status.should eql LessonSession::STATUS_REQUESTED
mailer = mock
mailer.should_receive(:deliver_now).twice
UserMailer.should_receive(:teacher_counter_reminder).and_return(mailer)
UserMailer.should_receive(:student_no_comm_other).and_return(mailer)
LessonSession.remind_counters
lesson_session1.reload
lesson_session1.sent_counter_reminder_at.should be_nil
Timecop.travel(Date.today + 10)
LessonSession.remind_counters
lesson_session1.reload
lesson_session1.sent_counter_reminder_at.should_not be_nil
LessonSession.remind_counters
end
it "finds old counter and pokes teacher" do
lesson_session1 = normal_lesson(user, teacher, {counter: true, counterer: user})
mailer = mock
mailer.should_receive(:deliver_now).twice
UserMailer.should_receive(:teacher_counter_reminder).and_return(mailer)
UserMailer.should_receive(:student_no_comm_other).and_return(mailer)
LessonSession.remind_counters
lesson_session1.reload
lesson_session1.sent_counter_reminder_at.should be_nil
Timecop.travel(Date.today + 10)
LessonSession.remind_counters
lesson_session1.reload
lesson_session1.sent_counter_reminder_at.should_not be_nil
end
it "finds old counter and pokes teacher" do
lesson_session1 = normal_lesson(user, teacher, {counter: true, counterer: teacher})
mailer = mock
mailer.should_receive(:deliver_now).twice
UserMailer.should_receive(:student_counter_reminder).and_return(mailer)
UserMailer.should_receive(:teacher_no_comm_other).and_return(mailer)
LessonSession.remind_counters
lesson_session1.reload
lesson_session1.sent_counter_reminder_at.should be_nil
Timecop.travel(Date.today + 10)
LessonSession.remind_counters
lesson_session1.reload
lesson_session1.sent_counter_reminder_at.should_not be_nil
end
end
describe "accept" do
it "accepts in the past is errored" do
slot = FactoryGirl.build(:lesson_booking_slot_single, preferred_day: Date.today + 1)
lesson = normal_lesson(user, teacher, {slots: [slot, slot2]})
Timecop.travel(Date.today + 2)
lesson.accept({
message: "Teacher time!",
acceptor: teacher,
slot: slot.id
})
lesson.errors.any?.should be_true
lesson.errors[:slot].should eql ["is in the past"]
end
it "accepts in the past for a recurring is OK" do
slotRecurring1 = FactoryGirl.build(:lesson_booking_slot_recurring)
slotRecurring2 = FactoryGirl.build(:lesson_booking_slot_recurring)
lesson = monthly_lesson(user, teacher, {slots: [slotRecurring1, slotRecurring2]})
Timecop.travel(Date.today + 100)
lesson.accept({
message: "Teacher time!",
accepter: teacher,
slot: slotRecurring1.id
})
lesson.errors.any?.should be_false
end
it "cancel in the past is OK" do
slot = FactoryGirl.build(:lesson_booking_slot_single, preferred_day: Date.today + 1)
lesson = normal_lesson(user, teacher, {slots: [slot, slot2]})
Timecop.travel(Date.today + 2)
lesson.cancel({
message: "Cancel time!",
canceler: teacher,
slot: slot
})
lesson.errors.any?.should be_false
lesson.reload
lesson.status.should eql LessonSession::STATUS_CANCELED
lesson.lesson_booking.status.should eql LessonSession::STATUS_CANCELED
end
end
def day_out(num_days)
safe_day_out = Time.now.wday + num_days
if safe_day_out > 6
safe_day_out -= 6
end
safe_day_out
end
describe "cancel" do
it "cancels both recurring lessons" do
now = Time.now
slotRecurring1 = FactoryGirl.build(:lesson_booking_slot_recurring, day_of_week: day_out(2))
slotRecurring2 = FactoryGirl.build(:lesson_booking_slot_recurring, day_of_week: day_out(3))
lesson = monthly_lesson(user, teacher, {slots: [slotRecurring1, slotRecurring2], accept: true})
lesson.reload
booking = lesson.lesson_booking
# verify ti's all approved and ready to test
booking.is_approved?.should be true
booking.lesson_sessions.count.should eql 2
lessons = booking.lesson_sessions.order(:created_at)
first_lesson = lessons[0]
second_lesson = lessons[1]
first_lesson.is_approved?.should be true
first_lesson.in_no_cancel_window.should be false
second_lesson.is_approved?.should be true
second_lesson.in_no_cancel_window.should be false
first_lesson.cancel({canceler: user, message: "Screw this lesson", update_all: true})
first_lesson.reload
second_lesson.reload
booking.reload
first_lesson.is_canceled?.should be true
second_lesson.is_canceled?.should be true
booking.is_canceled?.should be true
first_lesson.student_short_canceled.should be false
first_lesson.student_canceled.should be true
first_lesson.teacher_canceled.should be false
second_lesson.student_short_canceled.should be false
second_lesson.student_canceled.should be true
second_lesson.teacher_canceled.should be false
end
it "cancels both recurring lessons one within no-cancel window" do
now = Time.now
today = now.wday
slight_future = 6.hours.from_now
slight_future2 = 7.hours.from_now
slotRecurring1 = FactoryGirl.build(:lesson_booking_slot_recurring, day_of_week: slight_future.wday, hour: slight_future.hour)
slotRecurring2 = FactoryGirl.build(:lesson_booking_slot_recurring, day_of_week: slight_future2.wday, hour: slight_future2.hour)
lesson = monthly_lesson(user, teacher, {slots: [slotRecurring1, slotRecurring2], accept: true})
lesson.reload
booking = lesson.lesson_booking
# verify ti's all approved and ready to test
booking.is_approved?.should be true
booking.lesson_sessions.count.should eql 2
lessons = booking.lesson_sessions.order(:created_at)
first_lesson = lessons[0]
second_lesson = lessons[1]
first_lesson.is_approved?.should be true
first_lesson.in_no_cancel_window.should be true
second_lesson.is_approved?.should be true
second_lesson.in_no_cancel_window.should be false
first_lesson.cancel({canceler: user, message: "Screw this lesson", update_all: true})
first_lesson.reload
second_lesson.reload
booking.reload
first_lesson.is_canceled?.should be true
second_lesson.is_canceled?.should be true
booking.is_canceled?.should be true
first_lesson.student_short_canceled.should be true
first_lesson.student_canceled.should be true
first_lesson.teacher_canceled.should be false
second_lesson.student_short_canceled.should be false
second_lesson.student_canceled.should be true
second_lesson.teacher_canceled.should be false
end
end
describe "permissions" do
it "student can join session" do
lesson = normal_lesson(user, teacher)
FactoryGirl.create(:active_music_session, music_session: lesson.music_session, creator: teacher)
connection1 = FactoryGirl.create(:connection, user: user, music_session_id: lesson.music_session.active_music_session.id, as_musician: true)
connection2 = FactoryGirl.create(:connection, user: teacher, music_session_id: lesson.music_session.active_music_session.id, as_musician: true)
connection1.can_join_music_session
connection1.errors.any?.should be_false
connection2.can_join_music_session
connection2.errors.any?.should be_false
end
end
describe "upcoming_sessions_reminder" do
it "succeeds" do
lesson = normal_lesson(user, teacher)
UserMailer.deliveries.clear
LessonSession.upcoming_sessions_reminder
#UserMailer.deliveries.count.should eql 2
lesson.touch
lesson.sent_starting_notice.should be_false
lesson.is_approved?.should be_true
lesson.music_session.scheduled_start = 15.minutes.from_now
lesson.music_session.save!
LessonSession.upcoming_sessions_reminder
UserMailer.deliveries.count.should eql 2
UserMailer.deliveries.clear
lesson.reload
lesson.sent_starting_notice.should be_true
LessonSession.upcoming_sessions_reminder
UserMailer.deliveries.count.should eql 0
end
end
describe "index" do
it "finds single lesson as student" do
lesson_session.touch
lesson_session.music_session.creator.should eql lesson_session.lesson_booking.user
lesson_session.lesson_booking.teacher.should eql teacher
query = LessonSession.index(user)[:query]
query.length.should eq 1
# make sure some random nobody can see this lesson session
query = LessonSession.index(FactoryGirl.create(:user))[:query]
query.length.should eq 0
end
it "finds single lesson as teacher" do
# just sanity check that the lesson_session Factory is doing what it should
lesson_session.music_session.creator.should eql lesson_session.lesson_booking.user
lesson_session.lesson_booking.teacher.should eql teacher
query = LessonSession.index(teacher, {as_teacher: true})[:query]
query.length.should eq 1
# make sure some random nobody can see this lesson session
query = LessonSession.index(FactoryGirl.create(:user), {as_teacher: true})[:query]
query.length.should eq 0
end
describe "schools" do
let (:school) { FactoryGirl.create(:school, scheduling_communication: School::SCHEDULING_COMM_SCHOOL) }
describe "owner" do
it "works when not a teacher" do
query = LessonSession.index(school.owner, {as_teacher: true})[:query]
query.length.should eql 0
teacher.teacher.school = school
teacher.teacher.save!
teacher.reload
school.reload
school.teachers.to_a.should eql [teacher.teacher]
lesson = normal_lesson(user, teacher, {accept: false})
lesson.status.should eql LessonSession::STATUS_REQUESTED
lesson.lesson_booking.school.should eql school
school.owner.reload
query = LessonSession.index(school.owner, {as_teacher: true})[:query]
query.length.should eql 1
query = LessonSession.index(teacher, {as_teacher: true})[:query]
query.length.should eql 0
lesson = normal_lesson(user, teacher, {accept: true})
lesson.status.should eql LessonSession::STATUS_APPROVED
lesson.lesson_booking.school.should eql school
query = LessonSession.index(school.owner, {as_teacher: true})[:query]
query.length.should eql 1
query = LessonSession.index(teacher, {as_teacher: true})[:query]
query.length.should eql 1
end
end
end
end
end