1119 lines
44 KiB
Ruby
1119 lines
44 KiB
Ruby
#
|
|
# Copyright (C) 2011 - 2014 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')
|
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/validates_as_url.rb')
|
|
|
|
describe Submission do
|
|
before(:once) do
|
|
@user = factory_with_protected_attributes(User, :name => "some student", :workflow_state => "registered")
|
|
@course = @context = factory_with_protected_attributes(Course, :name => "some course", :workflow_state => "available")
|
|
@context.enroll_student(@user)
|
|
@assignment = @context.assignments.new(:title => "some assignment")
|
|
@assignment.workflow_state = "published"
|
|
@assignment.save
|
|
@valid_attributes = {
|
|
:assignment_id => @assignment.id,
|
|
:user_id => @user.id,
|
|
:grade => "1.5",
|
|
:url => "www.instructure.com"
|
|
}
|
|
end
|
|
|
|
it "should create a new instance given valid attributes" do
|
|
Submission.create!(@valid_attributes)
|
|
end
|
|
|
|
include_examples "url validation tests"
|
|
it "should check url validity" do
|
|
test_url_validation(Submission.create!(@valid_attributes))
|
|
end
|
|
|
|
it "should add http:// to the body for long urls, too" do
|
|
s = Submission.create!(@valid_attributes)
|
|
expect(s.url).to eq 'http://www.instructure.com'
|
|
|
|
long_url = ("a"*300 + ".com")
|
|
s.url = long_url
|
|
s.save!
|
|
expect(s.url).to eq "http://#{long_url}"
|
|
# make sure it adds the "http://" to the body for long urls, too
|
|
expect(s.body).to eq "http://#{long_url}"
|
|
end
|
|
|
|
it "should offer the context, if one is available" do
|
|
@course = Course.new
|
|
@assignment = Assignment.new(:context => @course)
|
|
@assignment.expects(:context).returns(@course)
|
|
|
|
@submission = Submission.new
|
|
expect{@submission.context}.not_to raise_error
|
|
expect(@submission.context).to be_nil
|
|
@submission.assignment = @assignment
|
|
expect(@submission.context).to eql(@course)
|
|
end
|
|
|
|
it "should have an interesting state machine" do
|
|
submission_spec_model
|
|
expect(@submission.state).to eql(:submitted)
|
|
@submission.grade_it
|
|
expect(@submission.state).to eql(:graded)
|
|
end
|
|
|
|
it "should be versioned" do
|
|
submission_spec_model
|
|
expect(@submission).to be_respond_to(:versions)
|
|
end
|
|
|
|
it "should not save new versions by default" do
|
|
submission_spec_model
|
|
expect {
|
|
@submission.save!
|
|
}.not_to change(@submission.versions, :count)
|
|
end
|
|
|
|
describe "version indexing" do
|
|
it "should create a SubmissionVersion when a new submission is created" do
|
|
expect {
|
|
submission_spec_model
|
|
}.to change(SubmissionVersion, :count)
|
|
end
|
|
|
|
it "should create a SubmissionVersion when a new version is saved" do
|
|
submission_spec_model
|
|
expect {
|
|
@submission.with_versioning(:explicit => true) { @submission.save }
|
|
}.to change(SubmissionVersion, :count)
|
|
end
|
|
end
|
|
|
|
it "should ensure the media object exists" do
|
|
assignment_model
|
|
se = @course.enroll_student(user)
|
|
MediaObject.expects(:ensure_media_object).with("fake", { :context => se.user, :user => se.user })
|
|
@submission = @assignment.submit_homework(se.user, :media_comment_id => "fake", :media_comment_type => "audio")
|
|
end
|
|
|
|
it "should log submissions with grade changes" do
|
|
submission_spec_model
|
|
|
|
Auditors::GradeChange.expects(:record).once
|
|
|
|
@submission.score = 5
|
|
@submission.save!
|
|
|
|
@submission.grader_id = @user.id
|
|
@submission.save!
|
|
end
|
|
|
|
context "Discussion Topic" do
|
|
it "should use correct date for its submitted_at value" do
|
|
course_with_student_logged_in(:active_all => true)
|
|
@topic = @course.discussion_topics.create(:title => "some topic")
|
|
@assignment = @course.assignments.create(:title => "some discussion assignment")
|
|
@assignment.submission_types = 'discussion_topic'
|
|
@assignment.save!
|
|
@entry1 = @topic.discussion_entries.create(:message => "first entry", :user => @user)
|
|
@topic.assignment_id = @assignment.id
|
|
@topic.save!
|
|
@submission = @assignment.submissions.where(:user_id => @entry1.user_id).first
|
|
new_time = Time.now + 30.minutes
|
|
Time.stubs(:now).returns(new_time)
|
|
@entry2 = @topic.discussion_entries.create(:message => "second entry", :user => @user)
|
|
@submission.reload
|
|
expect((@submission.submitted_at.to_i - @submission.created_at.to_i).abs).to be < 1.minute
|
|
end
|
|
end
|
|
|
|
context "broadcast policy" do
|
|
context "Submission Notifications" do
|
|
before :once do
|
|
Notification.create(:name => 'Assignment Submitted')
|
|
Notification.create(:name => 'Assignment Resubmitted')
|
|
Notification.create(:name => 'Assignment Submitted Late')
|
|
Notification.create(:name => 'Group Assignment Submitted Late')
|
|
|
|
@teacher = User.create(:name => "some teacher")
|
|
@student = User.create(:name => "a student")
|
|
@context.enroll_teacher(@teacher)
|
|
@context.enroll_student(@student)
|
|
end
|
|
|
|
it "should send the correct message when an assignment is turned in on-time" do
|
|
@assignment.workflow_state = "published"
|
|
@assignment.update_attributes(:due_at => Time.now + 1000)
|
|
|
|
submission_spec_model(:user => @student)
|
|
expect(@submission.messages_sent.keys).to eq ['Assignment Submitted']
|
|
end
|
|
|
|
it "should send the correct message when an assignment is turned in late" do
|
|
@assignment.workflow_state = "published"
|
|
@assignment.update_attributes(:due_at => Time.now - 1000)
|
|
|
|
submission_spec_model(:user => @student)
|
|
expect(@submission.messages_sent.keys).to eq ['Assignment Submitted Late']
|
|
end
|
|
|
|
it "should send the correct message when an assignment is resubmitted on-time" do
|
|
@assignment.submission_types = ['online_text_entry']
|
|
@assignment.due_at = Time.now + 1000
|
|
@assignment.save!
|
|
|
|
@assignment.submit_homework(@student, :body => "lol")
|
|
resubmission = @assignment.submit_homework(@student, :body => "frd")
|
|
expect(resubmission.messages_sent.keys).to eq ['Assignment Resubmitted']
|
|
end
|
|
|
|
it "should send the correct message when an assignment is resubmitted late" do
|
|
@assignment.submission_types = ['online_text_entry']
|
|
@assignment.due_at = Time.now - 1000
|
|
@assignment.save!
|
|
|
|
@assignment.submit_homework(@student, :body => "lol")
|
|
resubmission = @assignment.submit_homework(@student, :body => "frd")
|
|
expect(resubmission.messages_sent.keys).to eq ['Assignment Submitted Late']
|
|
end
|
|
|
|
it "should send the correct message when a group assignment is submitted late" do
|
|
@a = assignment_model(:course => @context, :group_category => "Study Groups", :due_at => Time.now - 1000, :submission_types => ["online_text_entry"])
|
|
@group1 = @a.context.groups.create!(:name => "Study Group 1", :group_category => @a.group_category)
|
|
@group1.add_user(@student)
|
|
submission = @a.submit_homework @student, :submission_type => "online_text_entry", :body => "blah"
|
|
|
|
expect(submission.messages_sent.keys).to eq ['Group Assignment Submitted Late']
|
|
end
|
|
end
|
|
|
|
context "Submission Graded" do
|
|
before :once do
|
|
Notification.create(:name => 'Submission Graded')
|
|
end
|
|
|
|
it "should create a message when the assignment has been graded and published" do
|
|
submission_spec_model
|
|
@cc = @user.communication_channels.create(:path => "somewhere")
|
|
@submission.reload
|
|
expect(@submission.assignment).to eql(@assignment)
|
|
expect(@submission.assignment.state).to eql(:published)
|
|
@submission.grade_it!
|
|
expect(@submission.messages_sent).to be_include('Submission Graded')
|
|
end
|
|
|
|
it "should not create a message when a muted assignment has been graded and published" do
|
|
submission_spec_model
|
|
@cc = @user.communication_channels.create(:path => "somewhere")
|
|
@assignment.mute!
|
|
@submission.reload
|
|
expect(@submission.assignment).to eql(@assignment)
|
|
expect(@submission.assignment.state).to eql(:published)
|
|
@submission.grade_it!
|
|
expect(@submission.messages_sent).not_to be_include "Submission Graded"
|
|
end
|
|
|
|
it "should not create a message when this is a quiz submission" do
|
|
submission_spec_model
|
|
@cc = @user.communication_channels.create(:path => "somewhere")
|
|
@quiz = Quizzes::Quiz.create!(:context => @course)
|
|
@submission.quiz_submission = @quiz.generate_submission(@user)
|
|
@submission.save!
|
|
@submission.reload
|
|
expect(@submission.assignment).to eql(@assignment)
|
|
expect(@submission.assignment.state).to eql(:published)
|
|
@submission.grade_it!
|
|
expect(@submission.messages_sent).not_to include('Submission Graded')
|
|
end
|
|
|
|
it "should create a hidden stream_item_instance when muted, graded, and published" do
|
|
submission_spec_model
|
|
@cc = @user.communication_channels.create :path => "somewhere"
|
|
@assignment.mute!
|
|
expect {
|
|
@submission = @assignment.grade_student(@user, :grade => 10)[0]
|
|
}.to change StreamItemInstance, :count
|
|
expect(@user.stream_item_instances.last).to be_hidden
|
|
end
|
|
|
|
it "should hide any existing stream_item_instances when muted" do
|
|
submission_spec_model
|
|
@cc = @user.communication_channels.create :path => "somewhere"
|
|
expect {
|
|
@submission = @assignment.grade_student(@user, :grade => 10)[0]
|
|
}.to change StreamItemInstance, :count
|
|
expect(@user.stream_item_instances.last).not_to be_hidden
|
|
@assignment.mute!
|
|
expect(@user.stream_item_instances.last).to be_hidden
|
|
end
|
|
|
|
it "should not create a message for admins and teachers with quiz submissions" do
|
|
course_with_teacher(:active_all => true)
|
|
assignment = @course.assignments.create!(
|
|
:title => 'assignment',
|
|
:points_possible => 10)
|
|
quiz = @course.quizzes.build(
|
|
:assignment_id => assignment.id,
|
|
:title => 'test quiz',
|
|
:points_possible => 10)
|
|
quiz.workflow_state = 'available'
|
|
quiz.save!
|
|
|
|
user = account_admin_user
|
|
channel = user.communication_channels.create!(:path => 'admin@example.com')
|
|
submission = quiz.generate_submission(user, false)
|
|
Quizzes::SubmissionGrader.new(submission).grade_submission
|
|
|
|
channel2 = @teacher.communication_channels.create!(:path => 'chang@example.com')
|
|
submission2 = quiz.generate_submission(@teacher, false)
|
|
Quizzes::SubmissionGrader.new(submission2).grade_submission
|
|
|
|
expect(submission.submission.messages_sent).not_to be_include('Submission Graded')
|
|
expect(submission2.submission.messages_sent).not_to be_include('Submission Graded')
|
|
end
|
|
end
|
|
|
|
it "should create a stream_item_instance when graded and published" do
|
|
Notification.create :name => "Submission Graded"
|
|
submission_spec_model
|
|
@cc = @user.communication_channels.create :path => "somewhere"
|
|
expect {
|
|
@assignment.grade_student(@user, :grade => 10)
|
|
}.to change StreamItemInstance, :count
|
|
end
|
|
|
|
it "should create a stream_item_instance when graded, and then made it visible when unmuted" do
|
|
Notification.create :name => "Submission Graded"
|
|
submission_spec_model
|
|
@cc = @user.communication_channels.create :path => "somewhere"
|
|
@assignment.mute!
|
|
expect {
|
|
@assignment.grade_student(@user, :grade => 10)
|
|
}.to change StreamItemInstance, :count
|
|
|
|
@assignment.unmute!
|
|
stream_item_ids = StreamItem.where(:asset_type => 'Submission', :asset_id => @assignment.submissions.all).pluck(:id)
|
|
stream_item_instances = StreamItemInstance.where(:stream_item_id => stream_item_ids)
|
|
stream_item_instances.each { |sii| expect(sii).not_to be_hidden }
|
|
end
|
|
|
|
|
|
context "Submission Grade Changed" do
|
|
it "should create a message when the score is changed and the grades were already published" do
|
|
Notification.create(:name => 'Submission Grade Changed')
|
|
@assignment.stubs(:score_to_grade).returns("10.0")
|
|
@assignment.stubs(:due_at).returns(Time.now - 100)
|
|
submission_spec_model
|
|
|
|
@cc = @user.communication_channels.create(:path => "somewhere")
|
|
s = @assignment.grade_student(@user, :grade => 10)[0] #@submission
|
|
s.graded_at = Time.parse("Jan 1 2000")
|
|
s.save
|
|
@submission = @assignment.grade_student(@user, :grade => 9)[0]
|
|
expect(@submission).to eql(s)
|
|
expect(@submission.messages_sent).to be_include('Submission Grade Changed')
|
|
end
|
|
|
|
it 'doesnt create a grade changed message when theres a quiz attached' do
|
|
Notification.create(:name => 'Submission Grade Changed')
|
|
@assignment.stubs(:score_to_grade).returns("10.0")
|
|
@assignment.stubs(:due_at).returns(Time.now - 100)
|
|
submission_spec_model
|
|
@quiz = Quizzes::Quiz.create!(:context => @course)
|
|
@submission.quiz_submission = @quiz.generate_submission(@user)
|
|
@submission.save!
|
|
@cc = @user.communication_channels.create(:path => "somewhere")
|
|
s = @assignment.grade_student(@user, :grade => 10)[0] #@submission
|
|
s.graded_at = Time.parse("Jan 1 2000")
|
|
s.save
|
|
@submission = @assignment.grade_student(@user, :grade => 9)[0]
|
|
expect(@submission).to eql(s)
|
|
expect(@submission.messages_sent).not_to include('Submission Grade Changed')
|
|
end
|
|
|
|
it "should create a message when the score is changed and the grades were already published" do
|
|
Notification.create(:name => 'Submission Grade Changed')
|
|
Notification.create(:name => 'Submission Graded')
|
|
@assignment.stubs(:score_to_grade).returns("10.0")
|
|
@assignment.stubs(:due_at).returns(Time.now - 100)
|
|
submission_spec_model
|
|
|
|
@cc = @user.communication_channels.create(:path => "somewhere")
|
|
s = @assignment.grade_student(@user, :grade => 10)[0] #@submission
|
|
@submission = @assignment.grade_student(@user, :grade => 9)[0]
|
|
expect(@submission).to eql(s)
|
|
expect(@submission.messages_sent).not_to be_include('Submission Grade Changed')
|
|
expect(@submission.messages_sent).to be_include('Submission Graded')
|
|
end
|
|
|
|
it "should not create a message when the score is changed and the grades were already published for a muted assignment" do
|
|
Notification.create(:name => 'Submission Grade Changed')
|
|
@assignment.mute!
|
|
@assignment.stubs(:score_to_grade).returns("10.0")
|
|
@assignment.stubs(:due_at).returns(Time.now - 100)
|
|
submission_spec_model
|
|
|
|
@cc = @user.communication_channels.create(:path => "somewhere")
|
|
s = @assignment.grade_student(@user, :grade => 10)[0] #@submission
|
|
s.graded_at = Time.parse("Jan 1 2000")
|
|
s.save
|
|
@submission = @assignment.grade_student(@user, :grade => 9)[0]
|
|
expect(@submission).to eql(s)
|
|
expect(@submission.messages_sent).not_to be_include('Submission Grade Changed')
|
|
|
|
end
|
|
|
|
it "should NOT create a message when the score is changed and the submission was recently graded" do
|
|
Notification.create(:name => 'Submission Grade Changed')
|
|
@assignment.stubs(:score_to_grade).returns("10.0")
|
|
@assignment.stubs(:due_at).returns(Time.now - 100)
|
|
submission_spec_model
|
|
|
|
@cc = @user.communication_channels.create(:path => "somewhere")
|
|
s = @assignment.grade_student(@user, :grade => 10)[0] #@submission
|
|
@submission = @assignment.grade_student(@user, :grade => 9)[0]
|
|
expect(@submission).to eql(s)
|
|
expect(@submission.messages_sent).not_to be_include('Submission Grade Changed')
|
|
end
|
|
end
|
|
end
|
|
|
|
context "turnitin" do
|
|
context "submission" do
|
|
def init_turnitin_api
|
|
@turnitin_api = Turnitin::Client.new('test_account', 'sekret')
|
|
@submission.context.expects(:turnitin_settings).at_least(1).returns([:placeholder])
|
|
Turnitin::Client.expects(:new).at_least(1).with(:placeholder).returns(@turnitin_api)
|
|
end
|
|
|
|
before(:once) do
|
|
@assignment.submission_types = "online_upload,online_text_entry"
|
|
@assignment.turnitin_enabled = true
|
|
@assignment.turnitin_settings = @assignment.turnitin_settings
|
|
@assignment.save!
|
|
@submission = @assignment.submit_homework(@user, { :body => "hello there", :submission_type => 'online_text_entry' })
|
|
end
|
|
|
|
it "should submit to turnitin after a delay" do
|
|
job = Delayed::Job.list_jobs(:future, 100).find { |j| j.tag == 'Submission#submit_to_turnitin' }
|
|
expect(job).not_to be_nil
|
|
expect(job.run_at).to be > Time.now.utc
|
|
end
|
|
|
|
it "should initially set turnitin submission to pending" do
|
|
init_turnitin_api
|
|
@turnitin_api.expects(:createOrUpdateAssignment).with(@assignment, @assignment.turnitin_settings).returns({ :assignment_id => "1234" })
|
|
@turnitin_api.expects(:enrollStudent).with(@context, @user).returns(stub(:success? => true))
|
|
@turnitin_api.expects(:submitPaper).returns({
|
|
@submission.asset_string => {
|
|
:object_id => '12345'
|
|
}
|
|
})
|
|
@submission.submit_to_turnitin
|
|
expect(@submission.reload.turnitin_data[@submission.asset_string][:status]).to eq 'pending'
|
|
end
|
|
|
|
it "should schedule a retry if something fails initially" do
|
|
init_turnitin_api
|
|
@turnitin_api.expects(:createOrUpdateAssignment).with(@assignment, @assignment.turnitin_settings).returns({ :assignment_id => "1234" })
|
|
@turnitin_api.expects(:enrollStudent).with(@context, @user).returns(stub(:success? => false))
|
|
@submission.submit_to_turnitin
|
|
expect(Delayed::Job.list_jobs(:future, 100).find_all { |j| j.tag == 'Submission#submit_to_turnitin' }.size).to eq 2
|
|
end
|
|
|
|
it "should set status as failed if something fails after several attempts" do
|
|
init_turnitin_api
|
|
@assignment.expects(:create_in_turnitin).returns(false)
|
|
@turnitin_api.expects(:enrollStudent).with(@context, @user).returns(stub(:success? => false, :error? => true, :error_hash => {}))
|
|
@turnitin_api.expects(:submitPaper).never
|
|
@submission.submit_to_turnitin(Submission::TURNITIN_RETRY)
|
|
expect(@submission.reload.turnitin_data[:status]).to eq 'error'
|
|
end
|
|
|
|
it "should set status back to pending on retry" do
|
|
init_turnitin_api
|
|
# first a submission, to get us into failed state
|
|
@assignment.expects(:create_in_turnitin).returns(false)
|
|
@turnitin_api.expects(:enrollStudent).with(@context, @user).returns(stub(:success? => false, :error? => true, :error_hash => {}))
|
|
@turnitin_api.expects(:submitPaper).never
|
|
@submission.submit_to_turnitin(Submission::TURNITIN_RETRY)
|
|
expect(@submission.reload.turnitin_data[:status]).to eq 'error'
|
|
|
|
# resubmit
|
|
@submission.resubmit_to_turnitin
|
|
expect(@submission.reload.turnitin_data[:status]).to be_nil
|
|
expect(@submission.turnitin_data[@submission.asset_string][:status]).to eq 'pending'
|
|
end
|
|
|
|
it "should set status to scored on success" do
|
|
init_turnitin_api
|
|
@submission.turnitin_data ||= {}
|
|
@submission.turnitin_data[@submission.asset_string] = { :object_id => '1234', :status => 'pending' }
|
|
@turnitin_api.expects(:generateReport).with(@submission, @submission.asset_string).returns({
|
|
:similarity_score => 56,
|
|
:web_overlap => 22,
|
|
:publication_overlap => 0,
|
|
:student_overlap => 33
|
|
})
|
|
|
|
@submission.check_turnitin_status
|
|
expect(@submission.reload.turnitin_data[@submission.asset_string][:status]).to eq 'scored'
|
|
end
|
|
|
|
it "should set status as failed if something fails after several attempts" do
|
|
init_turnitin_api
|
|
@submission.turnitin_data ||= {}
|
|
@submission.turnitin_data[@submission.asset_string] = { :object_id => '1234', :status => 'pending' }
|
|
@turnitin_api.expects(:generateReport).with(@submission, @submission.asset_string).returns({})
|
|
|
|
expects_job_with_tag('Submission#check_turnitin_status') do
|
|
@submission.check_turnitin_status(Submission::TURNITIN_STATUS_RETRY-1)
|
|
expect(@submission.reload.turnitin_data[@submission.asset_string][:status]).to eq 'pending'
|
|
end
|
|
|
|
@submission.check_turnitin_status(Submission::TURNITIN_STATUS_RETRY)
|
|
@submission.reload
|
|
updated_data = @submission.turnitin_data[@submission.asset_string]
|
|
expect(updated_data[:status]).to eq 'error'
|
|
end
|
|
|
|
it "should check status for all assets" do
|
|
init_turnitin_api
|
|
@submission.turnitin_data ||= {}
|
|
@submission.turnitin_data[@submission.asset_string] = { :object_id => '1234', :status => 'pending' }
|
|
@submission.turnitin_data["other_asset"] = { :object_id => 'xxyy', :status => 'pending' }
|
|
@turnitin_api.expects(:generateReport).with(@submission, @submission.asset_string).returns({
|
|
:similarity_score => 56, :web_overlap => 22, :publication_overlap => 0, :student_overlap => 33
|
|
})
|
|
@turnitin_api.expects(:generateReport).with(@submission, "other_asset").returns({ :similarity_score => 20 })
|
|
|
|
@submission.check_turnitin_status
|
|
@submission.reload
|
|
expect(@submission.turnitin_data[@submission.asset_string][:status]).to eq 'scored'
|
|
expect(@submission.turnitin_data["other_asset"][:status]).to eq 'scored'
|
|
end
|
|
|
|
it "should not blow up if submission_type has changed when job runs" do
|
|
@submission.submission_type = 'online_url'
|
|
@submission.context.expects(:turnitin_settings).never
|
|
expect { @submission.submit_to_turnitin }.not_to raise_error
|
|
end
|
|
end
|
|
|
|
describe "group" do
|
|
before(:once) do
|
|
@teacher = User.create(:name => "some teacher")
|
|
@student = User.create(:name => "a student")
|
|
@student1 = User.create(:name => "student 1")
|
|
@context.enroll_teacher(@teacher)
|
|
@context.enroll_student(@student)
|
|
@context.enroll_student(@student1)
|
|
|
|
@a = assignment_model(:course => @context, :group_category => "Study Groups")
|
|
@a.submission_types = "online_upload,online_text_entry"
|
|
@a.turnitin_enabled = true
|
|
@a.save!
|
|
|
|
@group1 = @a.context.groups.create!(:name => "Study Group 1", :group_category => @a.group_category)
|
|
@group1.add_user(@student)
|
|
@group1.add_user(@student1)
|
|
end
|
|
|
|
it "should submit to turnitin for the original submitter" do
|
|
submission = @a.submit_homework @student, :submission_type => "online_text_entry", :body => "blah"
|
|
Submission.where(assignment_id: @a).each do |s|
|
|
if s.id == submission.id
|
|
expect(s.turnitin_data[:last_processed_attempt]).to be > 0
|
|
else
|
|
expect(s.turnitin_data).to eq({})
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
context "report" do
|
|
before :once do
|
|
@assignment.submission_types = "online_upload,online_text_entry"
|
|
@assignment.turnitin_enabled = true
|
|
@assignment.turnitin_settings = @assignment.turnitin_settings
|
|
@assignment.save!
|
|
@submission = @assignment.submit_homework(@user, { :body => "hello there", :submission_type => 'online_text_entry' })
|
|
@submission.turnitin_data = {
|
|
"submission_#{@submission.id}" => {
|
|
:web_overlap => 92,
|
|
:error => true,
|
|
:publication_overlap => 0,
|
|
:state => "failure",
|
|
:object_id => "123456789",
|
|
:student_overlap => 90,
|
|
:similarity_score => 92
|
|
}
|
|
}
|
|
@submission.save!
|
|
end
|
|
|
|
before :each do
|
|
api = Turnitin::Client.new('test_account', 'sekret')
|
|
Turnitin::Client.expects(:new).at_least(1).returns(api)
|
|
api.expects(:sendRequest).with(:generate_report, 1, has_entries(:oid => "123456789")).at_least(1).returns('http://foo.bar')
|
|
end
|
|
|
|
it "should let teachers view the turnitin report" do
|
|
@teacher = User.create
|
|
@context.enroll_teacher(@teacher)
|
|
expect(@submission).to be_grants_right(@teacher, nil, :view_turnitin_report)
|
|
expect(@submission.turnitin_report_url("submission_#{@submission.id}", @teacher)).not_to be_nil
|
|
end
|
|
|
|
it "should let students view the turnitin report after grading" do
|
|
@assignment.turnitin_settings[:originality_report_visibility] = 'after_grading'
|
|
@assignment.save!
|
|
@submission.reload
|
|
|
|
expect(@submission).not_to be_grants_right(@user, nil, :view_turnitin_report)
|
|
expect(@submission.turnitin_report_url("submission_#{@submission.id}", @user)).to be_nil
|
|
|
|
@submission.score = 1
|
|
@submission.grade_it!
|
|
|
|
expect(@submission).to be_grants_right(@user, nil, :view_turnitin_report)
|
|
expect(@submission.turnitin_report_url("submission_#{@submission.id}", @user)).not_to be_nil
|
|
end
|
|
|
|
it "should let students view the turnitin report immediately if the visibility setting allows it" do
|
|
@assignment.turnitin_settings[:originality_report_visibility] = 'after_grading'
|
|
@assignment.save
|
|
@submission.reload
|
|
|
|
expect(@submission).not_to be_grants_right(@user, nil, :view_turnitin_report)
|
|
expect(@submission.turnitin_report_url("submission_#{@submission.id}", @user)).to be_nil
|
|
|
|
@assignment.turnitin_settings[:originality_report_visibility] = 'immediate'
|
|
@assignment.save
|
|
@submission.reload
|
|
|
|
expect(@submission).to be_grants_right(@user, nil, :view_turnitin_report)
|
|
expect(@submission.turnitin_report_url("submission_#{@submission.id}", @user)).not_to be_nil
|
|
end
|
|
|
|
it "should let students view the turnitin report after the due date if the visibility setting allows it" do
|
|
@assignment.turnitin_settings[:originality_report_visibility] = 'after_due_date'
|
|
@assignment.due_at = Time.now + 1.day
|
|
@assignment.save
|
|
@submission.reload
|
|
|
|
expect(@submission).not_to be_grants_right(@user, nil, :view_turnitin_report)
|
|
expect(@submission.turnitin_report_url("submission_#{@submission.id}", @user)).to be_nil
|
|
|
|
@assignment.due_at = Time.now - 1.day
|
|
@assignment.save
|
|
@submission.reload
|
|
|
|
expect(@submission).to be_grants_right(@user, nil, :view_turnitin_report)
|
|
expect(@submission.turnitin_report_url("submission_#{@submission.id}", @user)).not_to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
it "should return the correct quiz_submission_version" do
|
|
# see redmine #6048
|
|
|
|
# set up the data to have a submission with a quiz submission with multiple versions
|
|
course
|
|
quiz = @course.quizzes.create!
|
|
quiz_submission = quiz.generate_submission @user, false
|
|
quiz_submission.save
|
|
|
|
submission = Submission.create!({
|
|
:assignment_id => @assignment.id,
|
|
:user_id => @user.id,
|
|
:quiz_submission_id => quiz_submission.id
|
|
})
|
|
|
|
submission = @assignment.submit_homework @user, :submission_type => 'online_quiz'
|
|
submission.quiz_submission_id = quiz_submission.id
|
|
|
|
# set the microseconds of the submission.submitted_at to be less than the
|
|
# quiz_submission.finished_at.
|
|
|
|
# first set them to be exactly the same (with microseconds)
|
|
time_to_i = submission.submitted_at.to_i
|
|
usec = submission.submitted_at.usec
|
|
timestamp = "#{time_to_i}.#{usec}".to_f
|
|
|
|
quiz_submission.finished_at = Time.at(timestamp)
|
|
quiz_submission.save
|
|
|
|
# get the data in a strange state where the quiz_submission.finished_at is
|
|
# microseconds older than the submission (caused the bug in #6048)
|
|
quiz_submission.finished_at = Time.at(timestamp + 0.00001)
|
|
quiz_submission.save
|
|
|
|
# verify the data is weird, to_i says they are equal, but the usecs are off
|
|
expect(quiz_submission.finished_at.to_i).to eq submission.submitted_at.to_i
|
|
expect(quiz_submission.finished_at.usec).to be > submission.submitted_at.usec
|
|
|
|
# create the versions that Submission#quiz_submission_version uses
|
|
quiz_submission.with_versioning do
|
|
quiz_submission.save
|
|
quiz_submission.save
|
|
end
|
|
|
|
# the real test, quiz_submission_version shouldn't care about usecs
|
|
expect(submission.reload.quiz_submission_version).to eq 2
|
|
end
|
|
|
|
it "should return only comments readable by the user" do
|
|
course_with_teacher(:active_all => true)
|
|
@student1 = student_in_course(:active_user => true).user
|
|
@student2 = student_in_course(:active_user => true).user
|
|
|
|
@assignment = @course.assignments.new(:title => "some assignment")
|
|
@assignment.submission_types = "online_text_entry"
|
|
@assignment.workflow_state = "published"
|
|
@assignment.save
|
|
|
|
@submission = @assignment.submit_homework(@student1, :body => 'some message')
|
|
sc1 = SubmissionComment.create!(:submission => @submission, :author => @teacher, :comment => "a")
|
|
sc2 = SubmissionComment.create!(:submission => @submission, :author => @teacher, :comment => "b", :hidden => true)
|
|
sc3 = SubmissionComment.create!(:submission => @submission, :author => @student1, :comment => "c")
|
|
sc4 = SubmissionComment.create!(:submission => @submission, :author => @student2, :comment => "d")
|
|
@submission.reload
|
|
|
|
@submission.limit_comments(@teacher)
|
|
expect(@submission.submission_comments.count).to eql 4
|
|
expect(@submission.visible_submission_comments.count).to eql 3
|
|
|
|
@submission.limit_comments(@student1)
|
|
expect(@submission.submission_comments.count).to eql 3
|
|
expect(@submission.visible_submission_comments.count).to eql 3
|
|
|
|
@submission.limit_comments(@student2)
|
|
expect(@submission.submission_comments.count).to eql 1
|
|
expect(@submission.visible_submission_comments.count).to eql 1
|
|
end
|
|
|
|
describe "read/unread state" do
|
|
it "should be read if a submission exists with no grade" do
|
|
@submission = @assignment.submit_homework(@user)
|
|
expect(@submission.read?(@user)).to be_truthy
|
|
end
|
|
|
|
it "should be unread after assignment is graded" do
|
|
@submission = @assignment.grade_student(@user, { :grade => 3 }).first
|
|
expect(@submission.unread?(@user)).to be_truthy
|
|
end
|
|
|
|
it "should be unread after submission is graded" do
|
|
@assignment.submit_homework(@user)
|
|
@submission = @assignment.grade_student(@user, { :grade => 3 }).first
|
|
expect(@submission.unread?(@user)).to be_truthy
|
|
end
|
|
|
|
it "should be unread after submission is commented on by teacher" do
|
|
@student = @user
|
|
course_with_teacher(:course => @context, :active_all => true)
|
|
@submission = @assignment.grade_student(@student, { :grader => @teacher, :comment => "good!" }).first
|
|
expect(@submission.unread?(@user)).to be_truthy
|
|
end
|
|
|
|
it "should be read if other submission fields change" do
|
|
@submission = @assignment.submit_homework(@user)
|
|
@submission.workflow_state = 'graded'
|
|
@submission.graded_at = Time.now
|
|
@submission.save!
|
|
expect(@submission.read?(@user)).to be_truthy
|
|
end
|
|
end
|
|
|
|
describe "mute" do
|
|
let(:submission) { Submission.new }
|
|
|
|
before :each do
|
|
submission.published_score = 100
|
|
submission.published_grade = 'A'
|
|
submission.graded_at = Time.now
|
|
submission.grade = 'B'
|
|
submission.score = 90
|
|
submission.mute
|
|
end
|
|
|
|
specify { expect(submission.published_score).to be_nil }
|
|
specify { expect(submission.published_grade).to be_nil }
|
|
specify { expect(submission.graded_at).to be_nil }
|
|
specify { expect(submission.grade).to be_nil }
|
|
specify { expect(submission.score).to be_nil }
|
|
end
|
|
|
|
describe "muted_assignment?" do
|
|
it "returns true if assignment is muted" do
|
|
assignment = stub(:muted? => true)
|
|
@submission = Submission.new
|
|
@submission.expects(:assignment).returns(assignment)
|
|
expect(@submission.muted_assignment?).to eq true
|
|
end
|
|
|
|
it "returns false if assignment is not muted" do
|
|
assignment = stub(:muted? => false)
|
|
@submission = Submission.new
|
|
@submission.expects(:assignment).returns(assignment)
|
|
expect(@submission.muted_assignment?).to eq false
|
|
end
|
|
end
|
|
|
|
describe "without_graded_submission?" do
|
|
let(:submission) { Submission.new }
|
|
|
|
it "returns false if submission does not has_submission?" do
|
|
submission.stubs(:has_submission?).returns false
|
|
submission.stubs(:graded?).returns true
|
|
expect(submission.without_graded_submission?).to eq false
|
|
end
|
|
|
|
it "returns false if submission does is not graded" do
|
|
submission.stubs(:has_submission?).returns true
|
|
submission.stubs(:graded?).returns false
|
|
expect(submission.without_graded_submission?).to eq false
|
|
end
|
|
|
|
it "returns true if submission is not graded and has no submission" do
|
|
submission.stubs(:has_submission?).returns false
|
|
submission.stubs(:graded?).returns false
|
|
expect(submission.without_graded_submission?).to eq true
|
|
end
|
|
end
|
|
|
|
describe "autograded" do
|
|
let(:submission) { Submission.new }
|
|
|
|
it "returns false when its not autograded" do
|
|
assignment = stub(:muted? => false)
|
|
@submission = Submission.new
|
|
expect(@submission.autograded?).to eq false
|
|
|
|
@submission.grader_id = Shard.global_id_for(@user.id)
|
|
expect(@submission.autograded?).to eq false
|
|
end
|
|
|
|
it "returns true when its autograded" do
|
|
assignment = stub(:muted? => false)
|
|
@submission = Submission.new
|
|
@submission.grader_id = -1
|
|
expect(@submission.autograded?).to eq true
|
|
end
|
|
end
|
|
|
|
describe "past_due" do
|
|
before :once do
|
|
u1 = @user
|
|
submission_spec_model
|
|
@submission1 = @submission
|
|
|
|
add_section('overridden section')
|
|
u2 = student_in_section(@course_section, :active_all => true)
|
|
submission_spec_model(:user => u2)
|
|
@submission2 = @submission
|
|
|
|
@assignment.update_attribute(:due_at, Time.zone.now - 1.day)
|
|
@submission1.reload
|
|
@submission2.reload
|
|
end
|
|
|
|
it "should update when an assignment's due date is changed" do
|
|
expect(@submission1).to be_past_due
|
|
@assignment.reload.update_attribute(:due_at, Time.zone.now + 1.day)
|
|
expect(@submission1.reload).not_to be_past_due
|
|
end
|
|
|
|
it "should update when an applicable override is changed" do
|
|
expect(@submission1).to be_past_due
|
|
expect(@submission2).to be_past_due
|
|
|
|
assignment_override_model :assignment => @assignment,
|
|
:due_at => Time.zone.now + 1.day,
|
|
:set => @course_section
|
|
expect(@submission1.reload).to be_past_due
|
|
expect(@submission2.reload).not_to be_past_due
|
|
end
|
|
|
|
it "should give a quiz submission 30 extra seconds before making it past due" do
|
|
quiz_with_graded_submission([{:question_data => {:name => 'question 1', :points_possible => 1, 'question_type' => 'essay_question'}}]) do
|
|
{
|
|
"text_after_answers" => "",
|
|
"question_#{@questions[0].id}" => "<p>Lorem ipsum answer.</p>",
|
|
"context_id" => "#{@course.id}",
|
|
"context_type" => "Course",
|
|
"user_id" => "#{@user.id}",
|
|
"quiz_id" => "#{@quiz.id}",
|
|
"course_id" => "#{@course.id}",
|
|
"question_text" => "Lorem ipsum question",
|
|
}
|
|
end
|
|
@assignment.due_at = "20130101T23:59Z"
|
|
@assignment.save!
|
|
|
|
submission = @quiz_submission.submission.reload
|
|
submission.write_attribute(:submitted_at, @assignment.due_at + 3.days)
|
|
expect(submission).to be_past_due
|
|
|
|
submission.write_attribute(:submitted_at, @assignment.due_at + 30.seconds)
|
|
expect(submission).not_to be_past_due
|
|
end
|
|
end
|
|
|
|
describe "late" do
|
|
before :once do
|
|
submission_spec_model
|
|
end
|
|
|
|
it "should be false if not past due" do
|
|
@submission.submitted_at = 2.days.ago
|
|
@submission.cached_due_date = 1.day.ago
|
|
expect(@submission).not_to be_late
|
|
end
|
|
|
|
it "should be false if not submitted, even if past due" do
|
|
@submission.submission_type = nil # forces submitted_at to be nil
|
|
@submission.cached_due_date = 1.day.ago
|
|
expect(@submission).not_to be_late
|
|
end
|
|
|
|
it "should be true if submitted and past due" do
|
|
@submission.submitted_at = 1.day.ago
|
|
@submission.cached_due_date = 2.days.ago
|
|
expect(@submission).to be_late
|
|
end
|
|
end
|
|
|
|
describe "missing" do
|
|
before :once do
|
|
submission_spec_model
|
|
end
|
|
|
|
it "should be false if not past due" do
|
|
@submission.submitted_at = 2.days.ago
|
|
@submission.cached_due_date = 1.day.ago
|
|
expect(@submission).not_to be_missing
|
|
end
|
|
|
|
it "should be false if submitted, even if past due" do
|
|
@submission.submitted_at = 1.day.ago
|
|
@submission.cached_due_date = 2.days.ago
|
|
expect(@submission).not_to be_missing
|
|
end
|
|
|
|
it "should be true if not submitted, past due, and expects a submission" do
|
|
@submission.assignment.submission_types = "online_quiz"
|
|
@submission.submission_type = nil # forces submitted_at to be nil
|
|
@submission.cached_due_date = 1.day.ago
|
|
|
|
# Regardless of score
|
|
@submission.score = 0.00000001
|
|
@submission.graded_at = Time.zone.now + 1.day
|
|
|
|
expect(@submission).to be_missing
|
|
end
|
|
|
|
it "should be true if not submitted, score of zero, and does not expect a submission" do
|
|
@submission.assignment.submission_types = "on_paper"
|
|
@submission.submission_type = nil # forces submitted_at to be nil
|
|
@submission.cached_due_date = 1.day.ago
|
|
@submission.score = 0
|
|
@submission.graded_at = Time.zone.now + 1.day
|
|
expect(@submission).to be_missing
|
|
end
|
|
|
|
it "should be false if not submitted, score greater than zero, and does not expect a submission" do
|
|
@submission.assignment.submission_types = "on_paper"
|
|
@submission.submission_type = nil # forces submitted_at to be nil
|
|
@submission.cached_due_date = 1.day.ago
|
|
@submission.score = 0.00000001
|
|
@submission.graded_at = Time.zone.now + 1.day
|
|
expect(@submission).to be_missing
|
|
end
|
|
end
|
|
|
|
describe "cached_due_date" do
|
|
it "should get initialized during submission creation" do
|
|
@assignment.update_attribute(:due_at, Time.zone.now - 1.day)
|
|
|
|
override = @assignment.assignment_overrides.build
|
|
override.title = "Some Title"
|
|
override.set = @course.default_section
|
|
override.override_due_at(Time.zone.now + 1.day)
|
|
override.save!
|
|
# mysql just truncated the timestamp
|
|
override.reload
|
|
|
|
submission = @assignment.submissions.create(:user => @user)
|
|
expect(submission.cached_due_date).to eq override.due_at
|
|
end
|
|
end
|
|
|
|
describe "update_attachment_associations" do
|
|
before do
|
|
course_with_student active_all: true
|
|
@assignment = @course.assignments.create!
|
|
end
|
|
|
|
it "doesn't include random attachment ids" do
|
|
f = Attachment.create! uploaded_data: StringIO.new('blah'),
|
|
context: @course,
|
|
filename: 'blah.txt'
|
|
sub = @assignment.submit_homework(@user, attachments: [f])
|
|
expect(sub.attachments).to eq []
|
|
end
|
|
end
|
|
|
|
describe "versioned_attachments" do
|
|
it "should include user attachments" do
|
|
student_in_course(active_all: true)
|
|
att = attachment_model(filename: "submission.doc", :context => @student)
|
|
sub = @assignment.submit_homework(@student, attachments: [att])
|
|
expect(sub.versioned_attachments).to eq [att]
|
|
end
|
|
|
|
it "should not include attachments with a context of Submission" do
|
|
student_in_course(active_all: true)
|
|
att = attachment_model(filename: "submission.doc", :context => @student)
|
|
sub = @assignment.submit_homework(@student, attachments: [att])
|
|
sub.attachments.update_all(:context_type => "Submission", :context_id => sub.id)
|
|
expect(sub.reload.versioned_attachments).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#bulk_load_versioned_attachments" do
|
|
def ensure_attachments_arent_queried
|
|
Attachment.expects(:where).never
|
|
end
|
|
|
|
it "loads attachments for many submissions at once" do
|
|
attachments = []
|
|
|
|
submissions = 3.times.map { |i|
|
|
student_in_course(active_all: true)
|
|
attachments << [
|
|
attachment_model(filename: "submission#{i}-a.doc", :context => @student),
|
|
attachment_model(filename: "submission#{i}-b.doc", :context => @student)
|
|
]
|
|
|
|
@assignment.submit_homework @student, attachments: attachments[i]
|
|
}
|
|
|
|
Submission.bulk_load_versioned_attachments(submissions)
|
|
ensure_attachments_arent_queried
|
|
submissions.each_with_index { |s, i|
|
|
expect(s.versioned_attachments).to eq attachments[i]
|
|
}
|
|
end
|
|
|
|
def submission_for_some_user
|
|
student_in_course active_all: true
|
|
@assignment.submit_homework(@student,
|
|
submission_type: "online_url",
|
|
url: "http://example.com")
|
|
end
|
|
|
|
it "includes url submission attachments" do
|
|
s = submission_for_some_user
|
|
s.attachment = attachment_model(filename: "screenshot.jpg",
|
|
context: @student)
|
|
|
|
Submission.bulk_load_versioned_attachments([s])
|
|
ensure_attachments_arent_queried
|
|
expect(s.versioned_attachments).to eq [s.attachment]
|
|
end
|
|
|
|
it "handles bad data" do
|
|
s = submission_for_some_user
|
|
s.update_attribute(:attachment_ids, '99999999')
|
|
Submission.bulk_load_versioned_attachments([s])
|
|
expect(s.versioned_attachments).to eq []
|
|
end
|
|
end
|
|
|
|
describe "#assign_assessor" do
|
|
def peer_review_assignment
|
|
assignment = @course.assignments.build(title: 'Peer review',
|
|
due_at: Time.now - 1.day,
|
|
points_possible: 5,
|
|
submission_types: 'online_text_entry')
|
|
assignment.peer_reviews_assigned = true
|
|
assignment.peer_reviews = true
|
|
assignment.automatic_peer_reviews = true
|
|
assignment.save!
|
|
|
|
assignment
|
|
end
|
|
|
|
before(:each) do
|
|
student_in_course(active_all: true)
|
|
@student2 = user
|
|
@course.enroll_student(@student2).accept!
|
|
@assignment = peer_review_assignment
|
|
@assignment.submit_homework(@student, body: 'Lorem ipsum dolor')
|
|
@assignment.submit_homework(@student2, body: 'Sit amet consectetuer')
|
|
end
|
|
|
|
it "should send a reminder notification" do
|
|
AssessmentRequest.any_instance.expects(:send_reminder!).once
|
|
submission1, submission2 = @assignment.submissions
|
|
submission1.assign_assessor(submission2)
|
|
end
|
|
end
|
|
|
|
describe "#get_web_snapshot" do
|
|
it "should not blow up if web snapshotting fails" do
|
|
sub = Submission.new(@valid_attributes)
|
|
CutyCapt.expects(:enabled?).returns(true)
|
|
CutyCapt.expects(:snapshot_attachment_for_url).with(sub.url).returns(nil)
|
|
sub.get_web_snapshot
|
|
end
|
|
end
|
|
|
|
describe '#submit_attachments_to_canvadocs' do
|
|
it 'creates crocodoc documents' do
|
|
Canvas::Crocodoc.stubs(:enabled?).returns true
|
|
s = @assignment.submit_homework(@user,
|
|
submission_type: "online_text_entry",
|
|
body: "hi")
|
|
|
|
# creates crocodoc documents
|
|
a1 = crocodocable_attachment_model context: @user
|
|
s.attachments = [a1]
|
|
s.save
|
|
cd = a1.crocodoc_document
|
|
expect(cd).not_to be_nil
|
|
|
|
# shouldn't mess with existing crocodoc documents
|
|
a2 = crocodocable_attachment_model context: @user
|
|
s.attachments = [a1, a2]
|
|
s.save
|
|
expect(a1.crocodoc_document(true)).to eq cd
|
|
expect(a2.crocodoc_document).to eq a2.crocodoc_document
|
|
end
|
|
end
|
|
end
|
|
|
|
def submission_spec_model(opts={})
|
|
@submission = Submission.new(@valid_attributes.merge(opts))
|
|
expect(@submission.assignment).to eql(@assignment)
|
|
expect(@assignment.context).to eql(@context)
|
|
expect(@submission.assignment.context).to eql(@context)
|
|
@submission.save!
|
|
end
|