VDD: assignment override data structures

refs #10831

test-plan:
  - run specs. this is all infrastructure, no real separate test plan.

Change-Id: Ic67f574b7e4cbffd114f6ed34d306a393a6bd93c
Reviewed-on: https://gerrit.instructure.com/14117
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
This commit is contained in:
Jacob Fugal 2012-10-02 22:21:19 -06:00
parent dbc1d7f59b
commit 6fc0e64581
8 changed files with 895 additions and 22 deletions

View File

@ -50,6 +50,7 @@ class Assignment < ActiveRecord::Base
belongs_to :grading_standard
belongs_to :group_category
has_many :assignment_reminders, :dependent => :destroy
has_many :assignment_overrides, :dependent => :destroy
has_one :external_tool_tag, :class_name => 'ContentTag', :as => :context, :dependent => :destroy
validates_associated :external_tool_tag, :if => :external_tool?
@ -128,7 +129,16 @@ class Assignment < ActiveRecord::Base
:clear_unannounced_grading_changes_if_just_unpublished,
:schedule_do_auto_peer_review_job_if_automatic_peer_review,
:delete_empty_abandoned_children,
:remove_assignment_updated_flag
:remove_assignment_updated_flag,
:validate_assignment_overrides
def validate_assignment_overrides
if group_category_id_changed?
# needs to be .each(&:destroy) instead of .update_all(:workflow_state =>
# 'deleted') so that the override gets versioned properly
assignment_overrides.active.scoped(:conditions => {:set_type => 'Group'}).each(&:destroy)
end
end
def schedule_do_auto_peer_review_job_if_automatic_peer_review
if peer_reviews && automatic_peer_reviews && !peer_reviews_assigned
@ -213,26 +223,41 @@ class Assignment < ActiveRecord::Base
write_attribute :turnitin_settings, settings
end
def self.all_day_interpretation(opts={})
if opts[:due_at]
if opts[:due_at] == opts[:due_at_was]
# (comparison is modulo time zone) no real change, leave as was
return opts[:all_day_was], opts[:all_day_date_was]
else
# 'normal' case. compare due_at to fancy midnight and extract its
# date-part
return (opts[:due_at].strftime("%H:%M") == '23:59'), opts[:due_at].to_date
end
else
# no due at = all_day and all_day_date are irrelevant
return nil, nil
end
end
def default_values
raise "Assignments can only be assigned to Course records" if self.context_type && self.context_type != "Course"
self.context_code = "#{self.context_type.underscore}_#{self.context_id}"
self.title ||= (self.assignment_group.default_assignment_name rescue nil) || "Assignment"
self.grading_type = "pass_fail" if self.submission_types == "attendance"
zoned_due_at = self.due_at && ActiveSupport::TimeWithZone.new(self.due_at.utc, (ActiveSupport::TimeZone.new(self.time_zone_edited) rescue nil) || Time.zone)
if self.due_at_changed?
if zoned_due_at && zoned_due_at.strftime("%H:%M") == '23:59'
self.all_day = true
elsif self.due_at && self.due_at_was && self.all_day && self.due_at.strftime("%H:%M") == self.due_at_was.strftime("%H:%M")
self.all_day = true
else
self.all_day = false
end
end
# make the comparison to "fancy midnight" and the date-part extraction in
# the time zone that was active during editing
time_zone = (ActiveSupport::TimeZone.new(self.time_zone_edited) rescue nil) || Time.zone
self.all_day, self.all_day_date = Assignment.all_day_interpretation(
:due_at => self.due_at ? self.due_at.in_time_zone(time_zone) : nil,
:due_at_was => self.due_at_was,
:all_day_was => self.all_day_was,
:all_day_date_was => self.all_day_date_was)
if !self.assignment_group || (self.assignment_group.deleted? && !self.deleted?)
self.assignment_group = self.context.assignment_groups.active.first || self.context.assignment_groups.create!
end
self.mastery_score = [self.mastery_score, self.points_possible].min if self.mastery_score && self.points_possible
self.all_day_date = (zoned_due_at.to_date rescue nil) if !self.all_day_date || self.due_at_changed? || self.all_day_date_changed?
self.submission_types ||= "none"
self.peer_reviews_assign_at = [self.due_at, self.peer_reviews_assign_at].compact.max
@workflow_state_was = self.workflow_state_was

View File

@ -0,0 +1,138 @@
#
# 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/>.
#
class AssignmentOverride < ActiveRecord::Base
include Workflow
include TextHelper
simply_versioned :keep => 5
attr_accessible
belongs_to :assignment
belongs_to :set, :polymorphic => true
has_many :assignment_override_students, :dependent => :destroy
validates_presence_of :assignment, :assignment_version, :title
validates_inclusion_of :set_type, :in => %w(CourseSection Group ADHOC)
validates_length_of :title, :maximum => maximum_string_length, :allow_nil => true
concrete_set = lambda{ |override| ['CourseSection', 'Group'].include?(override.set_type) }
validates_presence_of :set, :set_id, :if => concrete_set
validates_uniqueness_of :set_id, :scope => [:assignment_id, :set_type, :workflow_state], :if => lambda{ |override| override.active? && concrete_set.call(override) }
validate :if => concrete_set do |record|
if record.set && record.assignment
case record.set
when CourseSection
record.errors.add :set, "not from assignment's course" unless record.set.course_id == record.assignment.context_id
when Group
record.errors.add :set, "not from assignment's group category" unless record.deleted? || record.set.group_category_id == record.assignment.group_category_id
end
end
end
validate :set_id, :unless => concrete_set do |record|
if record.set_type == 'ADHOC' && !record.set_id.nil?
record.errors.add :set_id, "must be nil with set_type ADHOC"
end
end
workflow do
state :active
state :deleted
end
alias_method :destroy!, :destroy
def destroy
self.workflow_state = 'deleted'
self.save!
end
named_scope :active, lambda {
{:conditions => {:workflow_state => 'active'} }
}
before_validation :default_values
def default_values
self.set_type ||= 'ADHOC'
self.assignment_version = assignment.version_number if assignment
self.title = set.name if set
end
protected :default_values
# override set read accessor and set_id read/write accessors so that reading
# set/set_id or setting set_id while set_type=ADHOC doesn't try and find the
# ADHOC model
def set_id
read_attribute(:set_id)
end
def set_with_adhoc
if self.set_type == 'ADHOC'
nil
else
set_without_adhoc
end
end
alias_method_chain :set, :adhoc
def set_id=(id)
if self.set_type == 'ADHOC'
write_attribute(:set_id, id)
else
super
end
end
def self.override(field)
define_method "override_#{field}" do |value|
send("#{field}_overridden=", true)
send("#{field}=", value)
end
define_method "clear_#{field}_override" do
send("#{field}_overridden=", false)
send("#{field}=", nil)
end
validates_inclusion_of "#{field}_overridden", :in => [false, true]
before_validation do |override|
if override.send("#{field}_overridden").nil?
override.send("#{field}_overridden=", false)
end
true
end
end
override :due_at
override :unlock_at
override :lock_at
def due_at=(new_due_at)
new_all_day, new_all_day_date = Assignment.all_day_interpretation(
:due_at => new_due_at,
:due_at_was => read_attribute(:due_at),
:all_day_was => read_attribute(:all_day),
:all_day_date_was => read_attribute(:all_day_date))
write_attribute(:due_at, new_due_at)
write_attribute(:all_day, new_all_day)
write_attribute(:all_day_date, new_all_day_date)
end
end

View File

@ -0,0 +1,52 @@
#
# 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/>.
#
class AssignmentOverrideStudent < ActiveRecord::Base
belongs_to :assignment
belongs_to :assignment_override
belongs_to :user
attr_accessible
validates_presence_of :assignment, :assignment_override, :user
validates_uniqueness_of :user_id, :scope => :assignment_id
validate :assignment_override do |record|
if record.assignment_override && record.assignment_override.set_type != 'ADHOC'
record.errors.add :assignment_override, "is not adhoc"
end
end
validate :assignment do |record|
if record.assignment_override && record.assignment_id != record.assignment_override.assignment_id
record.errors.add :assignment, "doesn't match assignment_override"
end
end
validate :user do |record|
if record.user && record.assignment && record.user.student_enrollments.scoped(:conditions => {:course_id => record.assignment.context_id}).first.nil?
record.errors.add :user, "is not in the assignment's course"
end
end
before_validation :default_values
def default_values
self.assignment_id = self.assignment_override.assignment_id if self.assignment_override
end
protected :default_values
end

View File

@ -0,0 +1,65 @@
class AssignmentOverrideMigration < ActiveRecord::Migration
tag :predeploy
def self.up
create_table :assignment_overrides do |t|
t.timestamps
# generic info
t.integer :assignment_id, :null => false, :limit => 8
t.integer :assignment_version, :null => false
t.string :set_type, :null => :false
t.integer :set_id, :limit => 8
t.string :title
t.string :workflow_state, :null => false
# due at override
t.boolean :due_at_overridden, :default => false, :null => false
t.datetime :due_at
t.boolean :all_day
t.date :all_day_date
# unlock at override
t.boolean :unlock_at_overridden, :default => false, :null => false
t.datetime :unlock_at
# lock at override
t.boolean :lock_at_overridden, :default => false, :null => false
t.datetime :lock_at
end
if connection.adapter_name =~ /\Apostgresql/i
add_index :assignment_overrides, [:assignment_id, :set_type, :set_id],
:name => 'index_assignment_overrides_on_assignment_and_set',
:unique => true,
:conditions => "workflow_state='active' and set_id is not null"
else
# can't enforce unique without conditions, since when set_type is 'adhoc'
# and set_id null, there may be multiple overrides
add_index :assignment_overrides, [:assignment_id, :set_type, :set_id],
:name => 'index_assignment_overrides_on_assignment_and_set'
end
add_foreign_key :assignment_overrides, :assignments
create_table :assignment_override_students do |t|
t.timestamps
t.integer :assignment_id, :null => false, :limit => 8
t.integer :assignment_override_id, :null => false, :limit => 8
t.integer :user_id, :null => false, :limit => 8
end
add_index :assignment_override_students, [:assignment_id, :user_id], :unique => true
add_index :assignment_override_students, :assignment_override_id
add_foreign_key :assignment_override_students, :assignments
add_foreign_key :assignment_override_students, :assignment_overrides
add_foreign_key :assignment_override_students, :users
end
def self.down
drop_table :assignment_override_students
drop_table :assignment_overrides
end
end

View File

@ -0,0 +1,27 @@
#
# Copyright (C) 2011 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/>.
#
def assignment_override_model(opts={})
assignment = opts.delete(:assignment) || assignment_model(opts)
@override = factory_with_protected_attributes(assignment.assignment_overrides, assignment_override_valid_attributes.merge(opts))
@override
end
def assignment_override_valid_attributes
{ :title => "Some Title" }
end

View File

@ -0,0 +1,341 @@
#
# Copyright (C) 2011 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 AssignmentOverride do
it "should soft-delete" do
@override = assignment_override_model
@override.destroy
@override = AssignmentOverride.find_by_id(@override.id)
@override.should_not be_nil
@override.workflow_state.should == 'deleted'
end
it "should default set_type to adhoc" do
@override = assignment_override_model
@override.valid? # trigger bookkeeping
@override.set_type.should == 'ADHOC'
end
it "should allow reading set and set_id when set_type is adhoc" do
@override = assignment_override_model
@override.set_type = 'ADHOC'
@override.set.should be_nil
@override.set_id.should be_nil
end
it "should be versioned" do
@override = assignment_override_model
@override.should respond_to :version_number
old_version = @override.version_number
@override.override_due_at(5.days.from_now)
@override.save!
@override.version_number.should_not == old_version
end
it "should keep its assignment version up to date" do
@override = assignment_override_model
@override.valid? # trigger bookkeeping
@override.assignment_version.should == @override.assignment.version_number
old_version = @override.assignment.version_number
@override.assignment.due_at = 5.days.from_now
@override.assignment.save!
@override.assignment.version_number.should_not == old_version
@override.valid? # trigger bookkeeping
@override.assignment_version.should == @override.assignment.version_number
end
describe "active scope" do
it "should include active overrides" do
5.times.map{ assignment_override_model }
AssignmentOverride.active.count.should == 5
end
it "should exclude deleted overrides" do
5.times.map{ assignment_override_model.destroy }
AssignmentOverride.active.count.should == 0
end
end
describe "validations" do
before :each do
@override = assignment_override_model
@override.should be_valid
end
def invalid_id_for_model(model)
(model.scoped(:select => 'max(id) as id').first.id || 0) + 1
end
it "should reject non-nil set_id with an adhoc set" do
@override.set_id = 1
@override.should_not be_valid
end
it "should reject an empty title with an adhoc set" do
@override.title = nil
@override.should_not be_valid
end
it "should reject an empty assignment" do
@override.assignment = nil
@override.should_not be_valid
end
it "should reject an invalid assignment" do
@override.assignment = nil
@override.assignment_id = invalid_id_for_model(Assignment)
@override.should_not be_valid
end
it "should accept section sets" do
@override.set = @course.course_sections.create!
@override.should be_valid
end
it "should accept group sets" do
@category = @course.group_categories.create!
@override.assignment.group_category = @category
@override.set = @category.groups.create!
@override.should be_valid
end
it "should reject an empty set_id with a non-adhoc set_type" do
@override.set = nil
@override.set_type = 'CourseSection'
@override.set_id = nil
@override.should_not be_valid
end
it "should reject an invalid set_id with a non-adhoc set_type" do
@override.set = nil
@override.set_type = 'CourseSection'
@override.set_id = invalid_id_for_model(CourseSection)
@override.should_not be_valid
end
it "should reject sections in different course than assignment" do
@other_course = course_model
@override.set = @other_course.default_section
@override.should_not be_valid
end
it "should reject groups in different category than assignment" do
@assignment.group_category = @course.group_categories.create!
@category = @course.group_categories.create!
@override.set = @category.groups.create
@override.should_not be_valid
end
# necessary to allow deleting but otherwise keeping assignments that were
# for an assignment's previous group category when the assignment's group
# category changes
it "should not reject groups in different category than assignment when deleted" do
@assignment.group_category = @course.group_categories.create!
@category = @course.group_categories.create!
@override.set = @category.groups.create
@override.workflow_state = 'deleted'
@override.should be_valid
end
it "should reject unrecognized sets" do
@override.set = @override.assignment.context
@override.should_not be_valid
end
it "should reject duplicate sets" do
@override.set = @course.default_section
@override.save!
@override = AssignmentOverride.new
@override.assignment = @assignment
@override.set = @course.default_section
@override.should_not be_valid
end
it "should allow duplicates of sets where only one is active" do
@override.set = @course.default_section
@override.save!
@override.destroy
@override = AssignmentOverride.new
@override.assignment = @assignment
@override.set = @course.default_section
@override.should be_valid
@override.destroy
@override = AssignmentOverride.new
@override.assignment = @assignment
@override.set = @course.default_section
@override.should be_valid
end
end
describe "title" do
before :each do
@override = assignment_override_model
end
it "should force title to the name of the section" do
@section = @course.default_section
@section.name = 'Section Test Value'
@override.set = @section
@override.title = 'Other Value'
@override.valid? # trigger bookkeeping
@override.title.should == @section.name
end
it "should default title to the name of the group" do
@assignment.group_category = @course.group_categories.create!
@group = @assignment.group_category.groups.create!
@group.name = 'Group Test Value'
@override.set = @group
@override.title = 'Other Value'
@override.valid? # trigger bookkeeping
@override.title.should == @group.name
end
it "should not be changed for adhoc sets" do
@override.title = 'Other Value'
@override.valid? # trigger bookkeeping
@override.title.should == 'Other Value'
end
end
def self.describe_override(field, value1, value2)
describe "#{field} overrides" do
before :each do
@assignment = assignment_model(field.to_sym => value1)
@override = assignment_override_model(:assignment => @assignment)
end
it "should set the override when a override_#{field} is called" do
@override.send("override_#{field}", value2)
@override.send("#{field}_overridden").should == true
@override.send(field).should == value2
end
it "should clear the override when clear_#{field}_override is called" do
@override.send("override_#{field}", value2)
@override.send("clear_#{field}_override")
@override.send("#{field}_overridden").should == false
@override.send(field).should be_nil
end
end
end
describe_override("due_at", 5.minutes.from_now, 7.minutes.from_now)
describe_override("unlock_at", 5.minutes.from_now, 7.minutes.from_now)
describe_override("lock_at", 5.minutes.from_now, 7.minutes.from_now)
describe "#due_at=" do
def fancy_midnight(opts={})
zone = opts[:zone] || Time.zone
Time.use_zone(zone) do
time = opts[:time] || Time.zone.now
time.in_time_zone.midnight + 1.day - 1.minute
end
end
before :each do
@override = assignment_override_model
end
it "should interpret 11:59pm as all day with no prior value" do
@override.due_at = fancy_midnight(:zone => 'Alaska')
@override.all_day.should == true
end
it "should interpret 11:59pm as all day with same-tz all-day prior value" do
@override.due_at = fancy_midnight(:zone => 'Alaska') + 1.day
@override.due_at = fancy_midnight(:zone => 'Alaska')
@override.all_day.should == true
end
it "should interpret 11:59pm as all day with other-tz all-day prior value" do
@override.due_at = fancy_midnight(:zone => 'Baghdad')
@override.due_at = fancy_midnight(:zone => 'Alaska')
@override.all_day.should == true
end
it "should interpret 11:59pm as all day with non-all-day prior value" do
@override.due_at = fancy_midnight(:zone => 'Alaska') + 1.hour
@override.due_at = fancy_midnight(:zone => 'Alaska')
@override.all_day.should == true
end
it "should not interpret non-11:59pm as all day no prior value" do
@override.due_at = fancy_midnight(:zone => 'Alaska').in_time_zone('Baghdad')
@override.all_day.should == false
end
it "should not interpret non-11:59pm as all day with same-tz all-day prior value" do
@override.due_at = fancy_midnight(:zone => 'Alaska')
@override.due_at = fancy_midnight(:zone => 'Alaska') + 1.hour
@override.all_day.should == false
end
it "should not interpret non-11:59pm as all day with other-tz all-day prior value" do
@override.due_at = fancy_midnight(:zone => 'Baghdad')
@override.due_at = fancy_midnight(:zone => 'Alaska') + 1.hour
@override.all_day.should == false
end
it "should not interpret non-11:59pm as all day with non-all-day prior value" do
@override.due_at = fancy_midnight(:zone => 'Alaska') + 1.hour
@override.due_at = fancy_midnight(:zone => 'Alaska') + 2.hour
@override.all_day.should == false
end
it "should preserve all-day when only changing time zone" do
@override.due_at = fancy_midnight(:zone => 'Alaska')
@override.due_at = fancy_midnight(:zone => 'Alaska').in_time_zone('Baghdad')
@override.all_day.should == true
end
it "should preserve non-all-day when only changing time zone" do
@override.due_at = fancy_midnight(:zone => 'Alaska').in_time_zone('Baghdad')
@override.due_at = fancy_midnight(:zone => 'Alaska')
@override.all_day.should == false
end
it "should determine date from due_at's timezone" do
@override.due_at = Date.today.in_time_zone('Baghdad') + 1.hour # 01:00:00 AST +03:00 today
@override.all_day_date.should == Date.today
@override.due_at = @override.due_at.in_time_zone('Alaska') - 2.hours # 12:00:00 AKDT -08:00 previous day
@override.all_day_date.should == Date.today - 1.day
end
it "should preserve all-day date when only changing time zone" do
@override.due_at = Date.today.in_time_zone('Baghdad') # 00:00:00 AST +03:00 today
@override.due_at = @override.due_at.in_time_zone('Alaska') # 13:00:00 AKDT -08:00 previous day
@override.all_day_date.should == Date.today
end
it "should preserve non-all-day date when only changing time zone" do
@override.due_at = Date.today.in_time_zone('Alaska') - 11.hours # 13:00:00 AKDT -08:00 previous day
@override.due_at = @override.due_at.in_time_zone('Baghdad') # 00:00:00 AST +03:00 today
@override.all_day_date.should == Date.today - 1.day
end
end
end

View File

@ -0,0 +1,81 @@
#
# Copyright (C) 2011 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 AssignmentOverrideStudent do
describe "validations" do
before :each do
student_in_course
@override = assignment_override_model(:course => @course)
@override_student = @override.assignment_override_students.build
@override_student.user = @student
end
it "should be valid in nominal setup" do
@override_student.should be_valid
end
it "should reject an assignment other than that of the override" do
@override_student.assignment = assignment_model
@override_student.should_not be_valid
end
it "should reject an empty assignment_override" do
@override_student.assignment_override = nil
@override_student.should_not be_valid
end
it "should reject a non-adhoc assignment_override" do
@override_student.assignment_override.set = @course.default_section
@override_student.should_not be_valid
end
it "should reject an empty user" do
@override_student.user = nil
@override_student.should_not be_valid
end
it "should reject a student not in the course" do
@override_student.user = user_model
@override_student.should_not be_valid
end
it "should reject duplicate tuples" do
@override_student.save!
@override_student2 = @override.assignment_override_students.build
@override_student2.user = @student
@override_student2.should_not be_valid
end
end
it "should maintain assignment from assignment_override" do
student_in_course
@override1 = assignment_override_model(:course => @course)
@override2 = assignment_override_model(:course => @course)
@override1.assignment_id.should_not == @override2.assignment_id
@override_student = @override1.assignment_override_students.build
@override_student.user = @student
@override_student.valid? # trigger maintenance
@override_student.assignment_id.should == @override1.assignment_id
@override_student.assignment_override = @override2
@override_student.valid? # trigger maintenance
@override_student.assignment_id.should == @override2.assignment_id
end
end

View File

@ -417,18 +417,162 @@ describe Assignment do
end
end
it "should treat 11:59pm as an all_day" do
assignment_model(:due_at => "Sep 4 2008 11:59pm")
@assignment.all_day.should eql(true)
@assignment.due_at.strftime("%H:%M").should eql("23:59")
@assignment.all_day_date.should eql(Date.parse("Sep 4 2008"))
describe "all_day and all_day_date from due_at" do
def fancy_midnight(opts={})
zone = opts[:zone] || Time.zone
Time.use_zone(zone) do
time = opts[:time] || Time.zone.now
time.in_time_zone.midnight + 1.day - 1.minute
end
end
before :each do
@assignment = assignment_model
end
it "should interpret 11:59pm as all day with no prior value" do
@assignment.due_at = fancy_midnight(:zone => 'Alaska')
@assignment.time_zone_edited = 'Alaska'
@assignment.save!
@assignment.all_day.should == true
end
it "should interpret 11:59pm as all day with same-tz all-day prior value" do
@assignment.due_at = fancy_midnight(:zone => 'Alaska') + 1.day
@assignment.save!
@assignment.due_at = fancy_midnight(:zone => 'Alaska')
@assignment.time_zone_edited = 'Alaska'
@assignment.save!
@assignment.all_day.should == true
end
it "should interpret 11:59pm as all day with other-tz all-day prior value" do
@assignment.due_at = fancy_midnight(:zone => 'Baghdad')
@assignment.save!
@assignment.due_at = fancy_midnight(:zone => 'Alaska')
@assignment.time_zone_edited = 'Alaska'
@assignment.save!
@assignment.all_day.should == true
end
it "should interpret 11:59pm as all day with non-all-day prior value" do
@assignment.due_at = fancy_midnight(:zone => 'Alaska') + 1.hour
@assignment.save!
@assignment.due_at = fancy_midnight(:zone => 'Alaska')
@assignment.time_zone_edited = 'Alaska'
@assignment.save!
@assignment.all_day.should == true
end
it "should not interpret non-11:59pm as all day no prior value" do
@assignment.due_at = fancy_midnight(:zone => 'Alaska').in_time_zone('Baghdad')
@assignment.time_zone_edited = 'Baghdad'
@assignment.save!
@assignment.all_day.should == false
end
it "should not interpret non-11:59pm as all day with same-tz all-day prior value" do
@assignment.due_at = fancy_midnight(:zone => 'Alaska')
@assignment.save!
@assignment.due_at = fancy_midnight(:zone => 'Alaska') + 1.hour
@assignment.time_zone_edited = 'Alaska'
@assignment.save!
@assignment.all_day.should == false
end
it "should not interpret non-11:59pm as all day with other-tz all-day prior value" do
@assignment.due_at = fancy_midnight(:zone => 'Baghdad')
@assignment.save!
@assignment.due_at = fancy_midnight(:zone => 'Alaska') + 1.hour
@assignment.time_zone_edited = 'Alaska'
@assignment.save!
@assignment.all_day.should == false
end
it "should not interpret non-11:59pm as all day with non-all-day prior value" do
@assignment.due_at = fancy_midnight(:zone => 'Alaska') + 1.hour
@assignment.save!
@assignment.due_at = fancy_midnight(:zone => 'Alaska') + 2.hour
@assignment.time_zone_edited = 'Alaska'
@assignment.save!
@assignment.all_day.should == false
end
it "should preserve all-day when only changing time zone" do
@assignment.due_at = fancy_midnight(:zone => 'Alaska')
@assignment.time_zone_edited = 'Alaska'
@assignment.save!
@assignment.due_at = fancy_midnight(:zone => 'Alaska').in_time_zone('Baghdad')
@assignment.time_zone_edited = 'Baghdad'
@assignment.save!
@assignment.all_day.should == true
end
it "should preserve non-all-day when only changing time zone" do
@assignment.due_at = fancy_midnight(:zone => 'Alaska').in_time_zone('Baghdad')
@assignment.save!
@assignment.due_at = fancy_midnight(:zone => 'Alaska')
@assignment.time_zone_edited = 'Alaska'
@assignment.save!
@assignment.all_day.should == false
end
it "should determine date from due_at's timezone" do
@assignment.due_at = Date.today.in_time_zone('Baghdad') + 1.hour # 01:00:00 AST +03:00 today
@assignment.time_zone_edited = 'Baghdad'
@assignment.save!
@assignment.all_day_date.should == Date.today
@assignment.due_at = @assignment.due_at.in_time_zone('Alaska') - 2.hours # 12:00:00 AKDT -08:00 previous day
@assignment.time_zone_edited = 'Alaska'
@assignment.save!
@assignment.all_day_date.should == Date.today - 1.day
end
it "should preserve all-day date when only changing time zone" do
@assignment.due_at = Date.today.in_time_zone('Baghdad') # 00:00:00 AST +03:00 today
@assignment.time_zone_edited = 'Baghdad'
@assignment.save!
@assignment.due_at = @assignment.due_at.in_time_zone('Alaska') # 13:00:00 AKDT -08:00 previous day
@assignment.time_zone_edited = 'Alaska'
@assignment.save!
@assignment.all_day_date.should == Date.today
end
it "should preserve non-all-day date when only changing time zone" do
@assignment.due_at = Date.today.in_time_zone('Alaska') - 11.hours # 13:00:00 AKDT -08:00 previous day
@assignment.save!
@assignment.due_at = @assignment.due_at.in_time_zone('Baghdad') # 00:00:00 AST +03:00 today
@assignment.time_zone_edited = 'Baghdad'
@assignment.save!
@assignment.all_day_date.should == Date.today - 1.day
end
end
it "should not be set to all_day if a time is specified" do
assignment_model(:due_at => "Sep 4 2008 11:58pm")
@assignment.all_day.should eql(false)
@assignment.due_at.strftime("%H:%M").should eql("23:58")
@assignment.all_day_date.should eql(Date.parse("Sep 4 2008"))
it "should destroy group overrides when the group category changes" do
@assignment = assignment_model
@assignment.group_category = @assignment.context.group_categories.create!
@assignment.save!
overrides = 5.times.map do
override = @assignment.assignment_overrides.build
override.set = @assignment.group_category.groups.create!
override.save!
override.workflow_state.should == 'active'
override
end
@assignment.group_category = @assignment.context.group_categories.create!
@assignment.save!
overrides.each do |override|
override.reload
override.workflow_state.should == 'deleted'
override.versions.size.should == 2
override.assignment_version.should == @assignment.version_number
end
end
context "concurrent inserts" do