335 lines
12 KiB
Ruby
335 lines
12 KiB
Ruby
#
|
|
# Copyright (C) 2011 - 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 File.expand_path(File.dirname(__FILE__) + '/../sharding_spec_helper.rb')
|
|
|
|
describe DelayedMessage do
|
|
it "should create a new instance given valid attributes" do
|
|
delayed_message_model
|
|
end
|
|
|
|
context "named scopes" do
|
|
before :once do
|
|
DelayedMessage.delete_all
|
|
end
|
|
|
|
it "should have scope for :daily" do
|
|
delayed_message_model(:frequency => 'daily')
|
|
expect(DelayedMessage.for(:daily)).to eq [@delayed_message]
|
|
end
|
|
|
|
it "should scope for :weekly" do
|
|
delayed_message_model(:frequency => 'weekly')
|
|
expect(DelayedMessage.for(:weekly)).to eq [@delayed_message]
|
|
end
|
|
|
|
it "should scope for notification" do
|
|
notification_model
|
|
delayed_message_model
|
|
expect(DelayedMessage.for(@notification)).to eq [@delayed_message]
|
|
end
|
|
|
|
it "should scope for notification_policy" do
|
|
notification_policy_model
|
|
delayed_message_model(:notification_policy_id => @notification_policy.id)
|
|
expect(@notification_policy).to be_is_a(NotificationPolicy)
|
|
expect(DelayedMessage.for(@notification_policy)).to eq [@delayed_message]
|
|
end
|
|
|
|
it "should scope for communication_channel" do
|
|
communication_channel_model
|
|
delayed_message_model(:communication_channel_id => @communication_channel.id)
|
|
expect(@communication_channel).to be_is_a(CommunicationChannel)
|
|
expect(DelayedMessage.for(@communication_channel)).to eq [@delayed_message]
|
|
end
|
|
|
|
it "should scope for context" do
|
|
delayed_message_model
|
|
@delayed_message.context = assignment_model
|
|
@delayed_message.save!
|
|
expect(DelayedMessage.for(@assignment)).to eq [@delayed_message]
|
|
end
|
|
|
|
it "should have a scope to filter by the state" do
|
|
notification = notification_model :name => 'New Stuff'
|
|
delayed_message_model(:workflow_state => 'pending')
|
|
delayed_message_model(:workflow_state => 'cancelled')
|
|
delayed_message_model(:workflow_state => 'sent')
|
|
expect(DelayedMessage.in_state(:pending).all? { |d| d.state == :pending }).to be_truthy
|
|
expect(DelayedMessage.in_state(:pending).size).to eql(1)
|
|
expect(DelayedMessage.in_state(:cancelled).all? { |d| d.state == :cancelled }).to be_truthy
|
|
expect(DelayedMessage.in_state(:cancelled).size).to eql(1)
|
|
expect(DelayedMessage.in_state(:sent).all? { |d| d.state == :sent }).to be_truthy
|
|
expect(DelayedMessage.in_state(:sent).size).to eql(1)
|
|
end
|
|
end
|
|
|
|
context "workflow" do
|
|
before :once do
|
|
delayed_message_model
|
|
end
|
|
|
|
it "should start the workflow with pending" do
|
|
expect(@delayed_message.state).to eql(:pending)
|
|
end
|
|
|
|
it "should should be able to go to cancelled from pending" do
|
|
@delayed_message.cancel
|
|
expect(@delayed_message.state).to eql(:cancelled)
|
|
end
|
|
|
|
it "should be able to be sent from pending" do
|
|
@delayed_message.begin_send
|
|
expect(@delayed_message.state).to eql(:sent)
|
|
end
|
|
end
|
|
|
|
it "should use the user's main account domain for links" do
|
|
Canvas::MessageHelper.create_notification(:name => 'Summaries', :category => 'Summaries')
|
|
account = Account.create!(:name => 'new acct')
|
|
user = user_with_pseudonym(:account => account)
|
|
expect(user.pseudonym.account).to eq account
|
|
expect(SisPseudonym).to receive(:for).with(user, Account.default, type: :implicit, require_sis: false).and_return(user.pseudonym)
|
|
expect(HostUrl).to receive(:context_host).with(account).at_least(1).and_return("dm.dummy.test.host")
|
|
allow(HostUrl).to receive(:default_host).and_return("test.host")
|
|
user.communication_channel.confirm!
|
|
dm = DelayedMessage.create!(:summary => "This is a notification", :context => Account.default, :communication_channel => user.communication_channel, :notification => notification_model)
|
|
DelayedMessage.summarize([dm])
|
|
message = Message.last
|
|
expect(message.body.to_s).not_to match(%r{http://test.host/})
|
|
expect(message.body.to_s).to match(%r{http://dm.dummy.test.host/})
|
|
end
|
|
|
|
it 'should return nil if the delayed messages are using a retired communication channel' do
|
|
Canvas::MessageHelper.create_notification(:name => 'Summaries', :category => 'Summaries')
|
|
account = Account.create!(:name => 'new acct')
|
|
user = user_with_pseudonym(:account => account)
|
|
user.communication_channel.retire!
|
|
dm = DelayedMessage.create!(:summary => "This is a notification", :context => Account.default, :communication_channel => user.communication_channel, :notification => notification_model)
|
|
expect(DelayedMessage.summarize([dm])).to be_nil
|
|
end
|
|
|
|
it "uses the root account's locale if the user locale isn't set" do
|
|
Canvas::MessageHelper.create_notification(:name => 'Summaries', :category => 'Summaries')
|
|
account = Account.create!(default_locale: 'es')
|
|
delayed_message_model(root_account_id: account.id).save!
|
|
expect(I18n).to receive(:with_locale).with('es').once
|
|
DelayedMessage.summarize([@delayed_message])
|
|
end
|
|
|
|
it "uses the user's locale for the summary message" do
|
|
Canvas::MessageHelper.create_notification(:name => 'Summaries', :category => 'Summaries')
|
|
@user = User.create!(locale: 'es')
|
|
delayed_message_model.save!
|
|
expect(I18n).to receive(:with_locale).with('es').once
|
|
DelayedMessage.summarize([@delayed_message])
|
|
end
|
|
|
|
context "sharding" do
|
|
specs_require_sharding
|
|
|
|
it "should create messages on the user's shard" do
|
|
Canvas::MessageHelper.create_notification(:name => 'Summaries', :category => 'Summaries')
|
|
|
|
@shard1.activate do
|
|
account = Account.create!(:name => 'new acct')
|
|
user = user_with_pseudonym(:account => account)
|
|
expect(user.pseudonym.account).to eq account
|
|
expect(HostUrl).to receive(:context_host).with(user.pseudonym.account).at_least(1).and_return("dm.dummy.test.host")
|
|
allow(HostUrl).to receive(:default_host).and_return("test.host")
|
|
@cc = user.communication_channel
|
|
@cc.confirm!
|
|
@dm = DelayedMessage.create!(:summary => "This is a notification", :context => account, :communication_channel => @cc, :notification => notification_model)
|
|
end
|
|
@shard2.activate do
|
|
DelayedMessage.summarize([@dm])
|
|
expect(@cc.messages.last).not_to be_nil
|
|
expect(@cc.messages.last.shard).to eq @shard1
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "set_send_at" do
|
|
before :once do
|
|
# shouldn't be used, but to make sure it's not equal to any of the other
|
|
# time zones in play
|
|
Time.zone = 'UTC'
|
|
@true_now = Time.zone.now
|
|
|
|
# time zones of interest
|
|
@mountain = ActiveSupport::TimeZone.us_zones.find{ |zone| zone.name == 'Mountain Time (US & Canada)' }
|
|
@central = ActiveSupport::TimeZone.us_zones.find{ |zone| zone.name == 'Central Time (US & Canada)' }
|
|
@eastern = ActiveSupport::TimeZone.us_zones.find{ |zone| zone.name == 'Eastern Time (US & Canada)' }
|
|
|
|
# set up user in central time (different than the specific time zones
|
|
# referenced in set_send_at)
|
|
@account = Account.create!(:name => 'new acct')
|
|
@user = user_with_pseudonym(:account => @account)
|
|
@user.time_zone = @central.name
|
|
@user.pseudonym.update_attribute(:account, @account)
|
|
@user.save
|
|
end
|
|
|
|
before :each do
|
|
# build the delayed message
|
|
@dm = DelayedMessage.new(:context => @account, :communication_channel => @user.communication_channel)
|
|
end
|
|
|
|
it "should do nothing if the CC isn't set yet" do
|
|
@dm.communication_channel = nil
|
|
@dm.send(:set_send_at)
|
|
expect(@dm.send_at).to be_nil
|
|
end
|
|
|
|
it "should do nothing if send_at is already set" do
|
|
send_at = @true_now - 5.days
|
|
@dm.send_at = send_at
|
|
@dm.send(:set_send_at)
|
|
expect(@dm.send_at).to eq send_at
|
|
end
|
|
|
|
it "should set to 6pm in the user's time zone for non-weekly messages" do
|
|
Timecop.freeze(@central.now.change(:hour => 12)) do
|
|
@dm.frequency = 'daily'
|
|
@dm.send(:set_send_at)
|
|
expect(@dm.send_at).to eq @central.now.change(:hour => 18)
|
|
end
|
|
end
|
|
|
|
it "should set to 6pm in the Mountain time zone for non-weekly messages when the user hasn't set a time zone" do
|
|
@user.time_zone = nil
|
|
@user.save
|
|
|
|
Timecop.freeze(@mountain.now.change(:hour => 12)) do
|
|
@dm.frequency = 'daily'
|
|
@dm.send(:set_send_at)
|
|
expect(@dm.send_at).to eq @mountain.now.change(:hour => 18)
|
|
end
|
|
end
|
|
|
|
it "should set to 6pm the next day for non-weekly messages created after 6pm" do
|
|
Timecop.freeze(@central.now.change(:hour => 20)) do
|
|
@dm.frequency = 'daily'
|
|
@dm.send(:set_send_at)
|
|
expect(@dm.send_at).to eq @central.now.tomorrow.change(:hour => 18)
|
|
end
|
|
end
|
|
|
|
it "should set to next saturday (Eastern-time) for weekly messages" do
|
|
monday = @eastern.now.monday
|
|
saturday = monday + 5.days
|
|
sunday = saturday + 1.day
|
|
|
|
Timecop.freeze(monday) do
|
|
@dm.frequency = 'weekly'
|
|
@dm.send(:set_send_at)
|
|
expect(@dm.send_at.in_time_zone(@eastern).midnight).to eq saturday
|
|
end
|
|
|
|
Timecop.freeze(sunday) do
|
|
@dm.send_at = nil
|
|
@dm.send(:set_send_at)
|
|
expect(@dm.send_at.in_time_zone(@eastern).midnight).to eq saturday + 1.week
|
|
end
|
|
end
|
|
|
|
it "should set to next saturday (Eastern-time) for weekly messages scheduled later saturday" do
|
|
monday = @eastern.now.monday
|
|
saturday = monday + 5.days
|
|
|
|
Timecop.freeze(monday) do
|
|
@dm.frequency = 'weekly'
|
|
@dm.send(:set_send_at)
|
|
end
|
|
|
|
Timecop.freeze(@dm.send_at + 30.minutes) do
|
|
@dm.send_at = nil
|
|
@dm.send(:set_send_at)
|
|
expect(@dm.send_at.in_time_zone(@eastern).midnight).to eq saturday + 1.week
|
|
end
|
|
end
|
|
|
|
it "should use the same time of day across weeks for weekly messages for the same user" do
|
|
# anchor to January 1st to avoid DST; we're consigned to slightly weird
|
|
# behavior around DST, but don't want it failing tests
|
|
monday = @eastern.now.change(:month => 1, :day => 1).monday
|
|
first = nil
|
|
|
|
Timecop.freeze(monday) do
|
|
@dm.frequency = 'weekly'
|
|
@dm.send(:set_send_at)
|
|
first = @dm.send_at
|
|
end
|
|
|
|
Timecop.freeze(monday + 1.week) do
|
|
@dm.send_at = nil
|
|
@dm.send(:set_send_at)
|
|
expect(@dm.send_at.in_time_zone(@eastern)).to eq first + 1.week
|
|
end
|
|
end
|
|
|
|
it "should spread weekly messages for users in different accounts over the windows" do
|
|
monday = @eastern.now.monday
|
|
saturday = monday + 5.days
|
|
|
|
Timecop.freeze(monday) do
|
|
@dm.frequency = 'weekly'
|
|
|
|
expected_windows = []
|
|
actual_windows = []
|
|
|
|
DelayedMessage::WEEKLY_ACCOUNT_BUCKETS.times.map do |i|
|
|
@dm.communication_channel.user.pseudonym.account_id = i
|
|
@dm.send_at = nil
|
|
@dm.send(:set_send_at)
|
|
actual_windows << (@dm.send_at - saturday).to_i / (60 * DelayedMessage::MINUTES_PER_WEEKLY_ACCOUNT_BUCKET)
|
|
expected_windows << i
|
|
end
|
|
|
|
expect(actual_windows.sort).to eq expected_windows
|
|
end
|
|
end
|
|
|
|
it "should spread weekly messages for different users in the same account over the same window" do
|
|
monday = @eastern.now.monday
|
|
saturday = monday + 5.days
|
|
|
|
Timecop.freeze(monday) do
|
|
@dm.frequency = 'weekly'
|
|
|
|
expected_diffs = []
|
|
actual_diffs = []
|
|
windows = []
|
|
|
|
DelayedMessage::MINUTES_PER_WEEKLY_ACCOUNT_BUCKET.times.map do |i|
|
|
@dm.communication_channel.user.id = i
|
|
@dm.send_at = nil
|
|
@dm.send(:set_send_at)
|
|
window = (@dm.send_at - saturday).to_i / (60 * DelayedMessage::MINUTES_PER_WEEKLY_ACCOUNT_BUCKET)
|
|
windows << window
|
|
actual_diffs << @dm.send_at - saturday - (DelayedMessage::MINUTES_PER_WEEKLY_ACCOUNT_BUCKET * window).minutes.to_i
|
|
expected_diffs << i.minutes
|
|
end
|
|
|
|
expect(actual_diffs.sort).to eq expected_diffs
|
|
expect(windows.uniq.size).to eq 1
|
|
end
|
|
end
|
|
end
|
|
end
|