489 lines
19 KiB
Ruby
Executable File
489 lines
19 KiB
Ruby
Executable File
#
|
|
# Copyright (C) 2012 Instructure, Inc.
|
|
#
|
|
# This file is part of Canvas.
|
|
#
|
|
# Canvas is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Affero General Public License as published by the Free
|
|
# Software Foundation, version 3 of the License.
|
|
#
|
|
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License along
|
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
|
|
|
describe Alert do
|
|
context "Alerts" do
|
|
before(:all) do
|
|
class Alert
|
|
class << self
|
|
alias :original_send_alert :send_alert
|
|
def send_alert(alert, user_id, student_enrollment)
|
|
@sent_alerts ||= []
|
|
@sent_alerts << student_enrollment.user_id
|
|
end
|
|
|
|
attr_accessor :sent_alerts
|
|
end
|
|
end
|
|
end
|
|
|
|
before(:each) do
|
|
Alert.sent_alerts = []
|
|
end
|
|
|
|
after(:all) do
|
|
class Alert
|
|
class << self
|
|
alias :send_alert :original_send_alert
|
|
end
|
|
end
|
|
end
|
|
|
|
context "mass assignment" do
|
|
it "should translate string-symbols to symbols when assigning to recipients" do
|
|
alert = Alert.new
|
|
alert.recipients = [':student', :teachers, 'AccountAdmin']
|
|
alert.recipients.should == [:student, :teachers, 'AccountAdmin']
|
|
end
|
|
|
|
it "should accept mass assignment of criteria" do
|
|
alert = Alert.new(:context => Account.default, :recipients => [:student])
|
|
alert.criteria = [{:criterion_type => 'Interaction', :threshold => 1}]
|
|
alert.criteria.length.should == 1
|
|
alert.criteria.first.criterion_type.should == 'Interaction'
|
|
alert.criteria.first.threshold.should == 1
|
|
alert.save!
|
|
original_criterion_id = alert.criteria.first.id
|
|
|
|
alert.criteria = [{:criterion_type => 'Interaction', :threshold => 7, :id => alert.criteria.first.id},
|
|
{:criterion_type => 'UserNote', :threshold => 6}]
|
|
alert.criteria.length.should == 2
|
|
alert.criteria.first.id.should == original_criterion_id
|
|
alert.criteria.first.threshold.should == 7
|
|
alert.criteria.last.should be_new_record
|
|
|
|
alert.criteria = []
|
|
alert.criteria be_empty
|
|
|
|
AlertCriterion.find_by_id(original_criterion_id).should be_nil
|
|
end
|
|
end
|
|
|
|
context "validation" do
|
|
it "should require a context" do
|
|
alert = Alert.new(:recipients => [:student], :criteria => [{:criterion_type => 'Interaction', :threshold => 7}])
|
|
alert.save.should be_false
|
|
end
|
|
|
|
it "should require recipients" do
|
|
alert = Account.default.alerts.build(:criteria => [{:criterion_type => 'Interaction', :threshold => 7}])
|
|
alert.save.should be_false
|
|
end
|
|
|
|
it "should require criteria" do
|
|
alert = Account.default.alerts.build(:recipients => [:student])
|
|
alert.save.should be_false
|
|
end
|
|
end
|
|
|
|
context "basic evaluation" do
|
|
it "should not trigger any alerts for unpublished courses" do
|
|
course = mock('Course')
|
|
course.stubs(:available?, false)
|
|
Alert.evaluate_for_course(course)
|
|
Alert.sent_alerts.should be_blank
|
|
end
|
|
|
|
it "should not trigger any alerts for courses with no alerts" do
|
|
course = mock('Course')
|
|
course.stubs(:available?).returns(true)
|
|
course.stubs(:alerts).returns(stub(:all => []))
|
|
Alert.evaluate_for_course(course)
|
|
Alert.sent_alerts.should be_blank
|
|
end
|
|
|
|
it "should not trigger any alerts when there are no students in the class" do
|
|
course = Course.new
|
|
course.offer!
|
|
course.alerts.create!(:recipients => [:student], :criteria => [{:criterion_type => 'Interaction', :threshold => 7}])
|
|
Alert.evaluate_for_course(course)
|
|
Alert.sent_alerts.should be_blank
|
|
end
|
|
|
|
it "should not trigger any alerts when there are no teachers in the class" do
|
|
course_with_student(:active_course => 1)
|
|
@course.alerts.create!(:recipients => [:student], :criteria => [{:criterion_type => 'Interaction', :threshold => 7}])
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should be_blank
|
|
end
|
|
end
|
|
|
|
context 'repetition' do
|
|
it "should not keep sending alerts when repetition is nil" do
|
|
enable_cache do
|
|
course_with_teacher(:active_all => 1)
|
|
student_in_course(:active_all => 1)
|
|
alert = @course.alerts.create!(:recipients => [:student], :criteria => [{:criterion_type => 'Interaction', :threshold => 7}])
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should == [ @user.id ]
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should == [ @user.id ]
|
|
end
|
|
end
|
|
|
|
it "should not keep sending alerts when run on the same day" do
|
|
enable_cache do
|
|
course_with_teacher(:active_all => 1)
|
|
student_in_course(:active_all => 1)
|
|
alert = @course.alerts.create!(:recipients => [:student], :repetition => 1, :criteria => [{:criterion_type => 'Interaction', :threshold => 7}])
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should == [ @user.id ]
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should == [ @user.id ]
|
|
end
|
|
end
|
|
|
|
it "should keep sending alerts for daily repetition" do
|
|
enable_cache do
|
|
course_with_teacher(:active_all => 1)
|
|
student_in_course(:active_all => 1)
|
|
alert = @course.alerts.create!(:recipients => [:student], :repetition => 1, :criteria => [{:criterion_type => 'Interaction', :threshold => 7}])
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should == [ @user.id ]
|
|
# update sent_at
|
|
Rails.cache.write([alert, @user.id].cache_key, (Time.now - 1.day).beginning_of_day)
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should == [ @user.id, @user.id ]
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'interaction' do
|
|
it "should not alert for new courses" do
|
|
course_with_teacher(:active_all => 1)
|
|
student_in_course(:active_all => 1)
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'Interaction', :threshold => 7)
|
|
alert.save!
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should be_blank
|
|
end
|
|
|
|
it "should alert for old courses" do
|
|
course_with_teacher(:active_all => 1)
|
|
student_in_course(:active_all => 1)
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'Interaction', :threshold => 7)
|
|
alert.save!
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.sort.should == [ @user.id ]
|
|
end
|
|
|
|
it "should not alert for submission comments" do
|
|
course_with_teacher(:active_all => 1)
|
|
@teacher = @user
|
|
@user = nil
|
|
student_in_course(:active_all => 1)
|
|
@assignment = @course.assignments.new(:title => "some assignment")
|
|
@assignment.workflow_state = "published"
|
|
@assignment.save
|
|
@submission = @assignment.submit_homework(@user)
|
|
SubmissionComment.create!(:submission => @submission, :comment => 'some comment', :author => @teacher, :recipient => @user)
|
|
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'Interaction', :threshold => 7)
|
|
alert.save!
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should be_blank
|
|
end
|
|
|
|
it "should alert for old submission comments" do
|
|
course_with_teacher(:active_all => 1)
|
|
@teacher = @user
|
|
@user = nil
|
|
student_in_course(:active_all => 1)
|
|
@assignment = @course.assignments.new(:title => "some assignment")
|
|
@assignment.workflow_state = "published"
|
|
@assignment.save
|
|
@submission = @assignment.submit_homework(@user)
|
|
SubmissionComment.create!(:submission => @submission, :comment => 'some comment', :author => @teacher, :recipient => @user) do |sc|
|
|
sc.created_at = Time.now - 30.days
|
|
end
|
|
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'Interaction', :threshold => 7)
|
|
alert.save!
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should == [ @user.id ]
|
|
end
|
|
|
|
it "should not alert for conversation messages" do
|
|
course_with_teacher(:active_all => 1)
|
|
@teacher = @user
|
|
@user = nil
|
|
student_in_course(:active_all => 1)
|
|
@conversation = @teacher.initiate_conversation([@user.id])
|
|
@conversation.add_message("hello")
|
|
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'Interaction', :threshold => 7)
|
|
alert.save!
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should be_blank
|
|
end
|
|
|
|
it "should alert for old conversation messages" do
|
|
course_with_teacher(:active_all => 1)
|
|
@teacher = @user
|
|
@user = nil
|
|
student_in_course(:active_all => 1)
|
|
@conversation = @teacher.initiate_conversation([@student.id, user.id])
|
|
message = @conversation.add_message("hello")
|
|
message.created_at = Time.now - 30.days
|
|
message.save!
|
|
|
|
alert = @course.alerts.build(:recipients => [:student], :repetition => 1)
|
|
alert.criteria.build(:criterion_type => 'Interaction', :threshold => 7)
|
|
alert.save!
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should == [ @student.id ]
|
|
|
|
# create a generated message
|
|
@conversation.add_participants([user.id])
|
|
@conversation.messages.length.should == 2
|
|
|
|
# it should still alert, ignoring the new message
|
|
Alert.sent_alerts = []
|
|
# update sent_at so it will send again
|
|
Rails.cache.write([alert, @student.id].cache_key, (Time.now - 5.days).beginning_of_day)
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should == [ @student.id ]
|
|
end
|
|
end
|
|
|
|
context 'ungraded count' do
|
|
it "should not alert for no submissions" do
|
|
course_with_teacher(:active_all => 1)
|
|
student_in_course(:active_all => 1)
|
|
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UngradedCount', :threshold => 1)
|
|
alert.save!
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should be_blank
|
|
end
|
|
|
|
it "should alert" do
|
|
course_with_teacher(:active_all => 1)
|
|
@teacher = @user
|
|
@user = nil
|
|
student_in_course(:active_all => 1)
|
|
@assignment = @course.assignments.new(:title => "some assignment")
|
|
@assignment.workflow_state = "published"
|
|
@assignment.save
|
|
@submission = @assignment.submit_homework(@user, :body => 'body')
|
|
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UngradedCount', :threshold => 1)
|
|
alert.save!
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should == [ @user.id ]
|
|
end
|
|
end
|
|
|
|
context 'ungraded timespan' do
|
|
it "should not alert for no submissions" do
|
|
course_with_teacher(:active_all => 1)
|
|
student_in_course(:active_all => 1)
|
|
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UngradedTimespan', :threshold => 1)
|
|
alert.save!
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should be_blank
|
|
end
|
|
|
|
it "should not alert for submission within the threshold" do
|
|
course_with_teacher(:active_all => 1)
|
|
@teacher = @user
|
|
@user = nil
|
|
student_in_course(:active_all => 1)
|
|
@assignment = @course.assignments.new(:title => "some assignment")
|
|
@assignment.workflow_state = "published"
|
|
@assignment.save
|
|
@submission = @assignment.submit_homework(@user, :body => 'body')
|
|
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UngradedTimespan', :threshold => 7)
|
|
alert.save!
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should be_blank
|
|
end
|
|
|
|
it "should alert" do
|
|
course_with_teacher(:active_all => 1)
|
|
@teacher = @user
|
|
@user = nil
|
|
student_in_course(:active_all => 1)
|
|
@assignment = @course.assignments.new(:title => "some assignment")
|
|
@assignment.workflow_state = "published"
|
|
@assignment.save
|
|
@submission = @assignment.submit_homework(@user, :body => 'body')
|
|
@submission.update_attribute(:submitted_at, Time.now - 30.days);
|
|
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UngradedTimespan', :threshold => 7)
|
|
alert.save!
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should == [ @user.id ]
|
|
end
|
|
|
|
it "should alert for multiple submissions when one matches and one doesn't" do
|
|
course_with_teacher(:active_all => 1)
|
|
@teacher = @user
|
|
@user = nil
|
|
student_in_course(:active_all => 1)
|
|
@assignment = @course.assignments.new(:title => "some assignment")
|
|
@assignment.workflow_state = "published"
|
|
@assignment.save
|
|
@submission = @assignment.submit_homework(@user, :body => 'body')
|
|
@submission.update_attribute(:submitted_at, Time.now - 30.days);
|
|
@assignment = @course.assignments.new(:title => "some assignment")
|
|
@assignment.workflow_state = "published"
|
|
@assignment.save
|
|
@submission = @assignment.submit_homework(@user, :body => 'body')
|
|
@submission.update_attribute(:submitted_at, Time.now - 30.days);
|
|
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UngradedTimespan', :threshold => 7)
|
|
alert.save!
|
|
Alert.evaluate_for_course(@course)
|
|
Alert.sent_alerts.should == [ @user.id ]
|
|
end
|
|
end
|
|
|
|
context 'user notes' do
|
|
it "should not alert for new courses" do
|
|
course_with_teacher(:active_all => 1)
|
|
student_in_course(:active_all => 1)
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UserNote', :threshold => 7)
|
|
alert.save!
|
|
Alert.evaluate_for_course(@course, nil, true)
|
|
Alert.sent_alerts.should be_blank
|
|
end
|
|
|
|
it "should alert for old courses" do
|
|
course_with_teacher(:active_all => 1)
|
|
student_in_course(:active_all => 1)
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UserNote', :threshold => 7)
|
|
alert.save!
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course, nil, true)
|
|
Alert.sent_alerts.sort.should == [ @user.id ]
|
|
end
|
|
|
|
it "should not alert when a note exists" do
|
|
course_with_teacher(:active_all => 1)
|
|
@teacher = @user
|
|
@user = nil
|
|
student_in_course(:active_all => 1)
|
|
UserNote.create!(:creator => @teacher, :user => @user)
|
|
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UserNote', :threshold => 7)
|
|
alert.save!
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course, nil, true)
|
|
Alert.sent_alerts.should be_blank
|
|
end
|
|
|
|
it "should alert when an old note exists" do
|
|
course_with_teacher(:active_all => 1)
|
|
@teacher = @user
|
|
@user = nil
|
|
student_in_course(:active_all => 1)
|
|
UserNote.create!(:creator => @teacher, :user => @user) { |un| un.created_at = Time.now - 30.days }
|
|
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UserNote', :threshold => 7)
|
|
alert.save!
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course, nil, true)
|
|
Alert.sent_alerts.should == [ @user.id ]
|
|
end
|
|
end
|
|
end
|
|
|
|
context "notification alert info" do
|
|
before do
|
|
Notification.create!(:name => 'Alert')
|
|
course_with_teacher(:active_all => 1)
|
|
@teacher = @user
|
|
@user = nil
|
|
student_in_course(:active_all => 1)
|
|
@pseudonym = mock('Pseudonym')
|
|
@pseudonym.stubs(:destroyed?).returns(false)
|
|
Pseudonym.stubs(:find_by_user_id).returns(@pseudonym)
|
|
a = @user.communication_channels.create(:path => "a@example.com")
|
|
a.confirm!
|
|
@assignment = @course.assignments.new(:title => "some assignment")
|
|
@assignment.workflow_state = "published"
|
|
@assignment.save
|
|
@submission = @assignment.submit_homework(@user, :body => 'body')
|
|
end
|
|
|
|
it "should tell you what the alert is about timespan" do
|
|
@submission.update_attribute(:submitted_at, Time.now - 30.days);
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UngradedTimespan', :threshold => 7)
|
|
alert.save!
|
|
Alert.evaluate_for_course(@course)
|
|
DelayedMessage.last.summary.should =~ /One or more submissions have been left ungraded for/
|
|
end
|
|
|
|
it "should tell you what the alert is about count" do
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UngradedCount', :threshold => 1)
|
|
alert.save!
|
|
Alert.evaluate_for_course(@course)
|
|
DelayedMessage.last.summary.should =~ /or more assignments have not been graded/
|
|
end
|
|
|
|
it "should tell you what the alert is about note" do
|
|
UserNote.create!(:creator => @teacher, :user => @user) { |un| un.created_at = Time.now - 30.days }
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'UserNote', :threshold => 7)
|
|
alert.save!
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course, nil, true)
|
|
DelayedMessage.last.summary.should =~ /No faculty journal entries for/
|
|
end
|
|
|
|
it "should tell you what the alert is about interaction" do
|
|
alert = @course.alerts.build(:recipients => [:student])
|
|
alert.criteria.build(:criterion_type => 'Interaction', :threshold => 7)
|
|
alert.save!
|
|
@course.start_at = Time.now - 30.days
|
|
Alert.evaluate_for_course(@course)
|
|
DelayedMessage.last.summary.should =~ /No student\/teacher interaction for/
|
|
end
|
|
end
|
|
end
|