canvas-lms/spec/models/grading_period_spec.rb

999 lines
38 KiB
Ruby

#
# Copyright (C) 2014 - present 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_relative '../spec_helper'
describe GradingPeriod do
subject(:grading_period) { grading_period_group.grading_periods.create!(params) }
let(:group_helper) { Factories::GradingPeriodGroupHelper.new }
let(:account) { Account.create! }
let(:course) { account.courses.create! }
let(:grading_period_group) do
group = account.grading_period_groups.create!(title: "A Group")
term = course.enrollment_term
group.enrollment_terms << term
group
end
let(:now) { Time.zone.now }
let(:params) do
{
title: 'A Grading Period',
start_date: now,
end_date: 1.day.from_now(now),
close_date: 5.days.from_now(now)
}
end
it { is_expected.to be_valid }
it "requires a start_date" do
grading_period = GradingPeriod.new(params.except(:start_date))
expect(grading_period).not_to be_valid
end
it "requires an end_date" do
grading_period = GradingPeriod.new(params.except(:end_date))
expect(grading_period).not_to be_valid
end
it "requires start_date to be before end_date" do
subject.assign_attributes(start_date: now.change(sec: 59), end_date: now.change(sec: 0))
is_expected.not_to be_valid
end
it "requires a title" do
grading_period = GradingPeriod.new(params.except(:title))
expect(grading_period).not_to be_valid
end
it { is_expected.to validate_numericality_of(:weight) }
describe ".in_closed_grading_period?" do
let(:in_closed_grading_period) { closed_period.start_date + 1.day }
let(:in_not_closed_grading_period) { not_closed_period.start_date + 1.day }
let(:outside_of_any_grading_period) { not_closed_period.end_date + 1.week }
let!(:closed_period) do
grading_period_group.grading_periods.create!(
title: "closed",
start_date: 2.weeks.ago(now),
end_date: 1.week.ago(now),
close_date: 3.days.ago(now)
)
end
let!(:another_closed_period) do
grading_period_group.grading_periods.create!(
title: "another closed period",
start_date: 4.weeks.ago(now),
end_date: 3.weeks.ago(now),
close_date: 2.weeks.ago(now)
)
end
let!(:not_closed_period) do
grading_period_group.grading_periods.create!(
title: "a period",
start_date: 3.days.ago(now),
end_date: 3.days.from_now(now),
close_date: 5.days.from_now(now)
)
end
it "returns true if the submission is due in a closed grading period" do
result = GradingPeriod.date_in_closed_grading_period?(
course: course,
date: in_closed_grading_period
)
expect(result).to be true
end
it "returns false if the submission is due in a not closed grading period" do
result = GradingPeriod.date_in_closed_grading_period?(
course: course,
date: in_not_closed_grading_period
)
expect(result).to be false
end
it "returns false if the submission is due outside of any grading period" do
result = GradingPeriod.date_in_closed_grading_period?(
course: course,
date: outside_of_any_grading_period
)
expect(result).to be false
end
it "returns true if the due date is null and the last grading period is closed" do
not_closed_period.destroy
result = GradingPeriod.date_in_closed_grading_period?(
course: course,
date: nil
)
expect(result).to be true
end
it "returns false if the due date is null and the last grading period is not closed" do
result = GradingPeriod.date_in_closed_grading_period?(
course: course,
date: nil
)
expect(result).to be false
end
end
describe ".current" do
subject { grading_period_group.grading_periods.current }
context "no periods" do
it "finds no current periods" do
is_expected.to be_empty
end
end
context "one current period" do
let!(:period) do
grading_period_group.grading_periods.create!(
title: "a period",
start_date: 1.day.ago(now),
end_date: 1.day.from_now(now),
close_date: 2.days.from_now(now)
)
end
it "finds one current period" do
is_expected.to eq [period]
end
it "includes the period if the current time is the same as the grading period end date" do
Timecop.freeze(now) do
period.update!(end_date: now)
is_expected.to be_present
end
end
it "includes the period if the current time is past the end date but the minute has not passed" do
Timecop.freeze(now.change(sec: 59)) do
period.update!(end_date: now.change(sec: 0))
is_expected.to be_present
end
end
it "does not include the period if the current time is past the end date and the minute has passed" do
Timecop.freeze(1.minute.from_now(now)) do
period.update!(end_date: now)
is_expected.to be_empty
end
end
it "does not include the period if start_date equals Time.now" do
Timecop.freeze(now) do
period.update!(start_date: now)
is_expected.to be_empty
end
end
it "does not include period if the current time is past the start date and the minute has not passed" do
Timecop.freeze(now.change(sec: 59)) do
period.update!(start_date: now.change(sec: 0))
is_expected.to be_empty
end
end
it "includes the period if the current time is past the start date and the minute has passed" do
Timecop.freeze(1.minute.from_now(now)) do
period.update!(start_date: now)
is_expected.to be_present
end
end
end
end
describe "#as_json_with_user_permissions" do
it "includes the close_date in the returned object" do
json = grading_period.as_json_with_user_permissions(User.new)
expect(json).to have_key("close_date")
end
it "includes the weight in the returned object" do
json = grading_period.as_json_with_user_permissions(User.new)
expect(json).to have_key("weight")
end
end
describe "close_date" do
context "grading period group belonging to an account" do
it "allows setting a close_date that is after the end_date" do
grading_period = grading_period_group.grading_periods.create!(params)
expect(grading_period.close_date).not_to eq(grading_period.end_date)
end
it "sets the close_date to the end_date if no close_date is provided" do
grading_period = grading_period_group.grading_periods.create!(params.except(:close_date))
expect(grading_period.close_date).to eq(grading_period.end_date)
end
it "is invalid if the close date is before the end date" do
period_params = params.merge(close_date: 1.day.ago(params[:end_date]))
grading_period = grading_period_group.grading_periods.build(period_params)
expect(grading_period).to be_invalid
end
it "considers the grading period valid if the close date is equal to the end date" do
period_params = params.merge(close_date: params.fetch(:end_date))
grading_period = grading_period_group.grading_periods.build(period_params)
expect(grading_period).to be_valid
end
end
context "grading period group belonging to a course" do
let(:course_grading_period_group) { group_helper.legacy_create_for_course(course) }
it "does not allow setting a close_date that is different from the end_date" do
grading_period = course_grading_period_group.grading_periods.create!(params)
expect(grading_period.close_date).to eq(params[:end_date])
end
it "sets the close_date to the end_date if no close_date is provided" do
grading_period = course_grading_period_group.grading_periods.create!(params.except(:close_date))
expect(grading_period.close_date).to eq(grading_period.end_date)
end
it "sets the close_date to the end_date when the grading period is updated" do
grading_period = course_grading_period_group.grading_periods.create!(params.except(:close_date))
new_end_date = 5.weeks.from_now(now)
grading_period.end_date = new_end_date
grading_period.save!
expect(grading_period.close_date).to eq(new_end_date)
end
end
end
describe "scope: closed" do
around { |example| Timecop.freeze(now, &example) }
it "includes grading period if the current date is past the close date" do
period = grading_period_group.grading_periods.create(
title: "Closed Period",
start_date: 10.days.ago(now),
end_date: 5.days.ago(now),
close_date: 3.days.ago(now)
)
expect(GradingPeriod.closed).to include period
end
it "excludes grading period if the current date is before the close date" do
period = grading_period_group.grading_periods.create(
title: "A Period",
start_date: 10.days.ago(now),
end_date: 5.days.ago(now),
close_date: 2.days.from_now(now)
)
expect(GradingPeriod.closed).not_to include period
end
it "excludes grading period if the current date matches the close date" do
period = grading_period_group.grading_periods.create(
title: "A Period",
start_date: 10.days.ago(now),
end_date: 5.days.ago(now),
close_date: now
)
expect(GradingPeriod.closed).not_to include period
end
end
describe "scope: open" do
around { |example| Timecop.freeze(now, &example) }
it "excludes grading period if the current date is past the close date" do
period = grading_period_group.grading_periods.create(
title: "Closed Period",
start_date: 10.days.ago(now),
end_date: 5.days.ago(now),
close_date: 3.days.ago(now)
)
expect(GradingPeriod.open).not_to include period
end
it "includes grading period if the current date is before the close date" do
period = grading_period_group.grading_periods.create(
title: "A Period",
start_date: 10.days.ago(now),
end_date: 5.days.ago(now),
close_date: 2.days.from_now(now)
)
expect(GradingPeriod.open).to include period
end
it "includes grading period if the current date matches the close date" do
period = grading_period_group.grading_periods.create(
title: "A Period",
start_date: 10.days.ago(now),
end_date: 5.days.ago(now),
close_date: now
)
expect(GradingPeriod.open).to include period
end
end
describe "#closed?" do
around { |example| Timecop.freeze(now, &example) }
it "returns true if the current date is past the close date" do
period = grading_period_group.grading_periods.build(
title: "Closed Period",
start_date: 10.days.ago(now),
end_date: 5.days.ago(now),
close_date: 3.days.ago(now)
)
expect(period).to be_closed
end
it "returns false if the current date is before the close date" do
period = grading_period_group.grading_periods.build(
title: "A Period",
start_date: 10.days.ago(now),
end_date: 5.days.ago(now),
close_date: 2.days.from_now(now)
)
expect(period).not_to be_closed
end
it "returns false if the current date matches the close date" do
period = grading_period_group.grading_periods.build(
title: "A Period",
start_date: 10.days.ago(now),
end_date: 5.days.ago(now),
close_date: now
)
expect(period).not_to be_closed
end
end
describe '#destroy' do
it_behaves_like 'soft deletion' do
let(:creation_arguments) { params }
subject { grading_period_group.grading_periods }
end
it 'destroys associated scores' do
course = Course.create!
enrollment = student_in_course(course: course)
score = enrollment.scores.create!(grading_period: grading_period)
grading_period.destroy
expect(score.reload).to be_deleted
end
it 'recalculates course scores if the grading period group is weighted' do
course = Course.create!
grading_period_group.enrollment_terms << course.enrollment_term
enrollment = student_in_course(course: course)
enrollment.scores.create!(grading_period: grading_period)
grading_period_group.update_column(:weighted, true)
expect(GradeCalculator).to receive(:recompute_final_score)
grading_period.destroy
end
it 'runs DueDateCacher for courses from the same enrollment term when the grading period is deleted' do
course2 = account.courses.create!
course2.enrollment_term = account.enrollment_terms.create!
course2.save!
student_in_course(course: course2)
a = course2.assignments.create!
a.submissions.find_by(user_id: @student).update(grading_period_id: grading_period.id)
expect(DueDateCacher).to receive(:recompute_course).with(course, any_args)
expect(DueDateCacher).not_to receive(:recompute_course).with(course2, any_args)
grading_period.destroy
end
it 'runs DueDateCacher for courses from the same enrollment term when the grading period set is deleted' do
course2 = account.courses.create!
course2.enrollment_term = account.enrollment_terms.create!
course2.save!
student_in_course(course: course2)
a = course2.assignments.create!
a.submissions.find_by(user_id: @student).update(grading_period_id: grading_period.id)
expect(DueDateCacher).to receive(:recompute_course).with(course, any_args)
expect(DueDateCacher).not_to receive(:recompute_course).with(course2, any_args)
grading_period_group.destroy
end
it 'does not destroy the set when the last grading period is destroyed (account grading periods)' do
grading_period.save!
grading_period.destroy
expect(grading_period_group).not_to be_deleted
end
it 'updates the grading_period_id to nil on submissions that were in the deleted grading period' do
student = User.create!
course.enroll_student(student, enrollment_state: :active)
grading_period.save!
assignment = course.assignments.create!(due_at: 2.hours.from_now(grading_period.start_date))
submission = assignment.submissions.find_by(user_id: student)
expect { grading_period.destroy }.to change {
submission.reload.grading_period_id
}.from(grading_period.id).to(nil)
end
it 'places submissions without due dates in the new "last" period if the "last" period was deleted' do
student = User.create!
course.enroll_student(student, enrollment_state: :active)
grading_period.save!
other_period = grading_period_group.grading_periods.create!(
title: "I will be the last period when the other one is deleted",
start_date: 2.days.ago(grading_period.start_date),
end_date: 1.day.ago(grading_period.start_date)
)
assignment = course.assignments.create!
submission = assignment.submissions.find_by(user_id: student)
expect { grading_period.destroy }.to change {
submission.reload.grading_period_id
}.from(grading_period.id).to(other_period.id)
end
context 'course grading periods (legacy support)' do
before(:once) do
@grading_period_set = course.grading_period_groups.create!
@period = @grading_period_set.grading_periods.create!(
title: 'Grading Period',
start_date: 5.days.ago,
end_date: 2.days.ago
)
end
it 'destroys the set when the last grading period is destroyed' do
@period.destroy
expect(@grading_period_set).to be_deleted
end
it 'does not destroy the set when a grading period is destroyed and it is not the last period' do
@grading_period_set.grading_periods.create!(
title: 'A New Grading Period',
start_date: 2.days.from_now,
end_date: 5.days.from_now
)
@period.destroy
expect(@grading_period_set).not_to be_deleted
end
end
end
describe ".for" do
let(:group_helper) { Factories::GradingPeriodGroupHelper.new }
let(:period_helper) { Factories::GradingPeriodHelper.new }
let(:term) do
enrollment_term = @root_account.enrollment_terms.create!
@course.enrollment_term = enrollment_term
@course.save!
enrollment_term
end
context "when context is a course" do
before(:once) do
@root_account = account_model
@sub_account = @root_account.sub_accounts.create!
@course = Course.create!(account: @sub_account)
end
it "finds all grading periods on a course" do
group_1 = group_helper.legacy_create_for_course(@course)
group_2 = group_helper.legacy_create_for_course(@course)
period_1 = period_helper.create_with_weeks_for_group(group_1, 5, 3)
period_2 = period_helper.create_with_weeks_for_group(group_2, 3, 1)
expect(GradingPeriod.for(@course)).to match_array([period_1, period_2])
end
it "ignores grading periods associated with unrelated courses" do
other_course = Course.create!(account: @sub_account)
group_1 = group_helper.legacy_create_for_course(@course)
group_2 = group_helper.legacy_create_for_course(other_course)
period_1 = period_helper.create_with_weeks_for_group(group_1, 5, 3)
period_helper.create_with_weeks_for_group(group_2, 3, 1)
expect(GradingPeriod.for(@course)).to match_array([period_1])
end
it "returns grading periods for the course enrollment term when the course has no grading period groups" do
group = group_helper.create_for_account(@root_account)
term.update_attribute(:grading_period_group_id, group)
period = period_helper.create_with_weeks_for_group(group, 5, 3)
expect(GradingPeriod.for(@course)).to match_array([period])
end
it "returns grading periods for the course enrollment term when the course has no grading periods" do
group_helper.legacy_create_for_course(@course)
group_2 = group_helper.create_for_account(@root_account)
term.update_attribute(:grading_period_group_id, group_2)
period = period_helper.create_with_weeks_for_group(group_2, 5, 3)
expect(GradingPeriod.for(@course)).to match_array([period])
end
it "returns an empty array when the course has no grading periods groups" do
expect(GradingPeriod.for(@course)).to match_array([])
end
it "returns an empty array when the course has no grading periods" do
group_helper.legacy_create_for_course(@course)
expect(GradingPeriod.for(@course)).to match_array([])
end
it "includes only 'active' grading periods from the course grading period group" do
group_1 = group_helper.legacy_create_for_course(@course)
group_2 = group_helper.legacy_create_for_course(@course)
period_1 = period_helper.create_with_weeks_for_group(group_1, 5, 3)
period_2 = period_helper.create_with_weeks_for_group(group_2, 3, 1)
period_2.workflow_state = :deleted
period_2.save
expect(GradingPeriod.for(@course)).to match_array([period_1])
end
it "includes only 'active' grading periods from the course enrollment term group" do
group = group_helper.create_for_account(@root_account)
term.update_attribute(:grading_period_group_id, group)
period_1 = period_helper.create_with_weeks_for_group(group, 5, 3)
period_2 = period_helper.create_with_weeks_for_group(group, 3, 1)
period_2.workflow_state = :deleted
period_2.save
expect(GradingPeriod.for(@course)).to match_array([period_1])
end
it "does not include grading periods from the course enrollment term group if inherit is false" do
group = group_helper.create_for_account(@root_account)
term.update_attribute(:grading_period_group_id, group)
period_helper.create_with_weeks_for_group(group, 5, 3)
period_2 = period_helper.create_with_weeks_for_group(group, 3, 1)
period_2.workflow_state = :deleted
period_2.save
expect(GradingPeriod.for(@course, inherit: false)).to match_array([])
end
end
context "when context is an account" do
before(:once) do
@root_account = account_model
@sub_account = @root_account.sub_accounts.create!
@course = Course.create!(account: @sub_account)
end
it "finds all grading periods on an account" do
group_1 = group_helper.create_for_account(@root_account)
group_2 = group_helper.create_for_account(@root_account)
period_1 = period_helper.create_with_weeks_for_group(group_1, 5, 3)
period_2 = period_helper.create_with_weeks_for_group(group_2, 3, 1)
expect(GradingPeriod.for(@root_account)).to match_array([period_1, period_2])
end
it "returns an empty array when the account has no grading period groups" do
expect(GradingPeriod.for(@root_account)).to match_array([])
end
it "returns an empty array when the account has no grading periods" do
group_helper.create_for_account(@root_account)
expect(GradingPeriod.for(@root_account)).to match_array([])
end
it "does not return grading periods on the course directly" do
group = group_helper.legacy_create_for_course(@course)
period_helper.create_with_weeks_for_group(group, 5, 3)
period_helper.create_with_weeks_for_group(group, 3, 1)
expect(GradingPeriod.for(@root_account)).to match_array([])
end
it "includes only 'active' grading periods from the account grading period group" do
group_1 = group_helper.create_for_account(@root_account)
group_2 = group_helper.create_for_account(@root_account)
period_1 = period_helper.create_with_weeks_for_group(group_1, 5, 3)
period_2 = period_helper.create_with_weeks_for_group(group_2, 3, 1)
period_2.workflow_state = :deleted
period_2.save
expect(GradingPeriod.for(@root_account)).to match_array([period_1])
end
end
end
describe ".current_period_for" do
let(:account) { Account.new }
let(:not_current_grading_period) { double }
let(:current_grading_period) { double }
it "returns the current grading period given a context" do
expect(GradingPeriod).to receive(:for).with(account).and_return([not_current_grading_period, current_grading_period])
expect(not_current_grading_period).to receive(:current?).and_return(false)
expect(current_grading_period).to receive(:current?).and_return(true)
expect(GradingPeriod.current_period_for(account)).to eq(current_grading_period)
end
it "returns nil if grading periods exist for the given context, but none are current" do
expect(GradingPeriod).to receive(:for).with(account).and_return([not_current_grading_period])
expect(not_current_grading_period).to receive(:current?).and_return(false)
expect(GradingPeriod.current_period_for(account)).to be_nil
end
it "returns nil if no grading periods exist for the given context" do
expect(GradingPeriod).to receive(:for).with(account).and_return([])
expect(GradingPeriod.current_period_for(account)).to be_nil
end
end
# TODO: move all of this to filter_with_overrides_by_due_at_for_class.rb
describe "#assignments" do
let!(:first_assignment) { course.assignments.create!(due_at: first_grading_period.start_date + 1.minute) }
let!(:second_assignment) { course.assignments.create!(due_at: second_grading_period.start_date + 1.minute) }
let!(:third_assignment) { course.assignments.create!(due_at: nil) }
let(:first_grading_period) do
grading_period_group.grading_periods.create!(
title: '1st period',
start_date: 2.months.from_now(now),
end_date: 3.months.from_now(now)
)
end
let(:second_grading_period) do
grading_period_group.grading_periods.create!(
title: '2nd period',
start_date: 3.months.from_now(now),
end_date: 4.months.from_now(now)
)
end
let(:grading_period_group) { group_helper.legacy_create_for_course(course) }
it "filters the first grading period" do
assignments = first_grading_period.assignments(course.assignments)
expect(assignments).to eq [first_assignment]
end
it "filters assignments without a due_at into the last grading period" do
assignments = second_grading_period.assignments(course.assignments)
expect(assignments).to eq [second_assignment, third_assignment]
end
describe "when due at is near the edge of a period" do
let!(:fourth_assignment) do
course.assignments.create!(
due_at: third_grading_period.end_date - 0.995.seconds
)
end
let!(:fifth_assignment) do
course.assignments.create!(
due_at: fourth_grading_period.start_date - 0.005.seconds
)
end
let(:third_grading_period) do
grading_period_group.grading_periods.create!(
title: '3rd period',
start_date: 5.months.from_now(now),
end_date: 6.months.from_now(now)
)
end
let(:fourth_grading_period) do
grading_period_group.grading_periods.create!(
title: '4th period',
start_date: 7.months.from_now(now),
end_date: 8.months.from_now(now)
)
end
it "includes assignments if they are on the future edge of end date" do
assignments = third_grading_period.assignments(course.assignments)
expect(assignments).to include fourth_assignment
end
it "does NOT include assignments if they are on the past edge of start date" do
assignments = fourth_grading_period.assignments(course.assignments)
expect(assignments).not_to include fifth_assignment
end
end
end
describe "#current?" do
subject(:grading_period) { GradingPeriod.new }
it "returns false for a grading period in the past" do
grading_period.assign_attributes(
start_date: 2.months.ago(now),
end_date: 1.month.ago(now)
)
expect(grading_period).to_not be_current
end
it "returns true if the current time falls between the start date and end date (inclusive)",
test_id: 2528634, priority: "2" do
grading_period.assign_attributes(
start_date: 1.month.ago(now),
end_date: 1.month.from_now(now)
)
expect(grading_period).to be_current
end
it "returns false for a grading period in the future" do
grading_period.assign_attributes(
start_date: 1.month.from_now(now),
end_date: 2.months.from_now(now)
)
expect(grading_period).to_not be_current
end
end
context 'given an existing grading_period' do
let(:course) { Course.create! }
let(:grading_period_group) { group_helper.legacy_create_for_course(course) }
describe '#overlapping?' do
before(:once) do
@existing_grading_period = grading_period_group.grading_periods.create!(
title: 'a title',
start_date: now.change(sec: 0),
end_date: 2.days.from_now(now).change(sec: 59)
)
@grading_period = grading_period_group.grading_periods.build
end
it 'is overlapping if the start date and end date match an existing period' do
@grading_period.start_date = @existing_grading_period.start_date
@grading_period.end_date = @existing_grading_period.end_date
expect(@grading_period).to be_overlapping
end
it 'is not overlapping if the start date is the end date of an existing period' do
@grading_period.start_date = @existing_grading_period.end_date
@grading_period.end_date = 1.month.from_now(@existing_grading_period.end_date)
expect(@grading_period).not_to be_overlapping
end
it 'is not overlapping if the start date is before the end date of an existing period ' \
'but they are in the same minute' do
@grading_period.start_date = @existing_grading_period.end_date.change(sec: 0)
@grading_period.end_date = 1.month.from_now(@existing_grading_period.end_date)
expect(@grading_period).not_to be_overlapping
end
it 'is overlapping if the start date is before the end date of an existing period and ' \
'they are not in the same minute' do
@grading_period.start_date = 1.minute.ago(@existing_grading_period.end_date)
@grading_period.end_date = 1.month.from_now(@existing_grading_period.end_date)
expect(@grading_period).to be_overlapping
end
it 'is not overlapping if the end date is the start date of an existing period' do
@grading_period.start_date = 1.month.from_now(@existing_grading_period.start_date)
@grading_period.end_date = @existing_grading_period.start_date
expect(@grading_period).not_to be_overlapping
end
it 'is not overlapping if the end date is past the start date of an existing period, ' \
'but there are in the same minute' do
@grading_period.start_date = 1.month.ago(@existing_grading_period.start_date)
@grading_period.end_date = @existing_grading_period.start_date.change(sec: 59)
expect(@grading_period).not_to be_overlapping
end
it 'is overlapping if the end date is past the start date of an existing period and ' \
'they are not in the same minute' do
@grading_period.start_date = 1.month.ago(@existing_grading_period.start_date)
@grading_period.end_date = 1.minute.from_now(@existing_grading_period.start_date)
expect(@grading_period).to be_overlapping
end
it "after a grading period is persisted it continues to not overlap" do
expect(@existing_grading_period).not_to be_overlapping
end
end
end
describe "Soft deletion" do
subject { grading_period_group.grading_periods }
let(:creation_arguments) { [period_one, period_two] }
let(:period_one) { { title: 'an title', start_date: 1.week.ago(now), end_date: 2.weeks.from_now(now) } }
let(:period_two) { { title: 'an title', start_date: 2.weeks.from_now(now), end_date: 5.weeks.from_now(now) } }
include_examples "soft deletion"
end
describe ".in_date_range?" do
subject(:period) do
grading_period_group.grading_periods.build(
title: 'a period',
start_date: 1.week.ago(now).change(sec: 0),
end_date: 2.weeks.from_now(now).change(sec: 0)
)
end
it "is in date range for a date that equals end_date" do
is_expected.to be_in_date_range(period.end_date)
end
it "is in date range for a date that is past the end date but the minute has not yet passed" do
is_expected.to be_in_date_range(period.end_date.change(sec: 59))
end
it "is not in date range for a date that is past the end date and the minute has passed" do
is_expected.not_to be_in_date_range(1.minute.from_now(period.end_date))
end
it "is not in date range for a date before the period" do
is_expected.not_to be_in_date_range(2.weeks.ago(now))
end
it "is not in date range for or a date after the period" do
is_expected.not_to be_in_date_range(3.weeks.from_now(now))
end
it "is not in date range for a date that equals start_date" do
is_expected.not_to be_in_date_range(period.start_date)
end
it "is not in date range for a date that is past the start_date but the minute has not yet passed" do
is_expected.not_to be_in_date_range(period.start_date.change(sec: 59))
end
it "is in date range for a date that is past the start_date and the minute has passed" do
is_expected.to be_in_date_range(1.minute.from_now(period.start_date))
end
end
describe ".json_for" do
context "when given a course" do
it "returns a list sorted by date with is_last" do
group = group_helper.legacy_create_for_course(course)
group.grading_periods.create!(
start_date: 1.week.ago(now),
end_date: 2.weeks.from_now(now),
title: 'C'
)
group.grading_periods.create!(
start_date: 4.weeks.ago(now),
end_date: 3.weeks.ago(now),
title: 'A'
)
group.grading_periods.create!(
start_date: 3.weeks.ago(now),
end_date: 2.weeks.ago(now),
title: 'B'
)
json = GradingPeriod.json_for(course, nil)
expect(json.map { |el| el['title'] }).to eq %w(A B C)
expect(json.map { |el| el['is_last'] }).to eq [false, false, true]
end
end
end
describe '#account_group?' do
context "given an account grading period group" do
it { is_expected.to be_account_group }
end
context "given a course grading period group" do
subject(:course_period) { grading_period_group.grading_periods.create!(params) }
let(:grading_period_group) { group_helper.legacy_create_for_course(course) }
it { is_expected.not_to be_account_group }
end
end
describe '#course_group?' do
context "given a course grading period group" do
subject(:course_period) { grading_period_group.grading_periods.create!(params) }
let(:grading_period_group) { group_helper.legacy_create_for_course(course) }
it { is_expected.to be_course_group }
end
context "given an account grading period group" do
it { is_expected.not_to be_course_group }
end
end
describe '#weight' do
it "can persist double precision values" do
subject.update!(weight: 1.5)
expect(subject.reload.weight).to eql 1.5
end
end
describe 'grading period scores' do
before do
student_in_course(course: course, active_all: true)
teacher_in_course(course: course, active_all: true)
@assignment = course.assignments.create!(due_at: 10.days.from_now(now), points_possible: 10)
@assignment.grade_student(@student, grade: 8, grader: @teacher)
end
it 'creates scores for the grading period upon its creation' do
expect{ grading_period.save! }.to change{ Score.count }.by(1)
end
it 'updates grading period scores when the grading period end date is changed' do
grading_period.save!
expect do
day_after_assignment_is_due = 1.day.from_now(@assignment.due_at)
grading_period.update!(
end_date: day_after_assignment_is_due,
close_date: day_after_assignment_is_due
)
end.to change{
Score.where(grading_period_id: grading_period).first.current_score
}.from(nil).to(80.0)
end
it 'updates grading period scores when the grading period start date is changed' do
day_before_grading_period_starts = 1.day.ago(grading_period.start_date)
@assignment.update!(due_at: day_before_grading_period_starts)
grading_period.save!
expect{ grading_period.update!(start_date: 1.day.ago(@assignment.due_at)) }.to change{
Score.where(grading_period_id: grading_period).first.current_score
}.from(nil).to(80.0)
end
it 'updates grading period ids on submissions without due dates if the "last" period changes' do
student = User.create!
course.enroll_student(student, enrollment_state: :active)
grading_period.save!
other_period = grading_period_group.grading_periods.create!(
title: "I will be the last period when the other one changes dates",
start_date: 2.days.ago(grading_period.start_date),
end_date: 1.day.ago(grading_period.start_date)
)
assignment = course.assignments.create!
submission = assignment.submissions.find_by(user_id: student)
expect do
grading_period.update!(
start_date: 2.days.ago(other_period.start_date),
end_date: 1.day.ago(other_period.start_date)
)
end.to change {
submission.reload.grading_period_id
}.from(grading_period.id).to(other_period.id)
end
it 'updates course score when the grading period weight is changed' do
grading_period.save!
grading_period_group.update!(weighted: true)
expect{ grading_period.update!(weight: 50) }.to change{
Score.where(grading_period_id: nil).first.updated_at
}
end
it 'does not update grading period score when the grading period weight is changed' do
grading_period.save!
grading_period_group.update!(weighted: true)
expect{ grading_period.update!(weight: 20) }.not_to change{
Score.where(grading_period_id: grading_period).first.updated_at
}
end
it 'does not update course score when weight is changed but weighted grading periods are disabled' do
grading_period.save!
grading_period_group.update!(weighted: false)
expect{ grading_period.update!(weight: 50) }.not_to change{
Score.where(grading_period_id: nil).first.updated_at
}
expect{ grading_period.update!(weight: 20) }.not_to change{
Score.where(grading_period_id: grading_period).first.updated_at
}
end
it 'does not update grading period score when weight is changed but weighted grading periods are disabled' do
grading_period.save!
grading_period_group.update!(weighted: false)
expect{ grading_period.update!(weight: 20) }.not_to change{
Score.where(grading_period_id: grading_period).first.updated_at
}
end
end
end