651 lines
30 KiB
Ruby
651 lines
30 KiB
Ruby
#
|
|
# Copyright (C) 2016 - 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 SplitUsers do
|
|
describe 'user splitting' do
|
|
let!(:restored_user) { user_model } # user will be merged into source_user and then restored on split
|
|
let!(:source_user) { user_model } # always the destination user of merge
|
|
let(:user3) { user_model }
|
|
let(:course1) { course_factory(active_all: true) }
|
|
let(:course2) { course_factory(active_all: true) }
|
|
let(:course3) { course_factory(active_all: true) }
|
|
let(:account1) { Account.default }
|
|
let(:sub_account) { account1.sub_accounts.create! }
|
|
|
|
it 'should restore terms_of use one way' do
|
|
source_user.accept_terms
|
|
source_user.save!
|
|
UserMerge.from(restored_user).into(source_user)
|
|
SplitUsers.split_db_users(restored_user)
|
|
expect(restored_user.reload.preferences[:accepted_terms]).to be_nil
|
|
expect(source_user.reload.preferences[:accepted_terms]).to_not be_nil
|
|
end
|
|
|
|
it 'should restore terms_of use other way' do
|
|
restored_user.accept_terms
|
|
restored_user.save!
|
|
UserMerge.from(restored_user).into(source_user)
|
|
expect(source_user.reload.preferences[:accepted_terms]).to_not be_nil
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(restored_user.reload.preferences[:accepted_terms]).to_not be_nil
|
|
expect(source_user.reload.preferences[:accepted_terms]).to be_nil
|
|
end
|
|
|
|
it 'should restore terms_of use no way' do
|
|
UserMerge.from(restored_user).into(source_user)
|
|
source_user.accept_terms
|
|
source_user.save!
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(source_user.reload.preferences[:accepted_terms]).to be_nil
|
|
expect(restored_user.reload.preferences[:accepted_terms]).to be_nil
|
|
end
|
|
|
|
it 'should restore terms_of use both ways' do
|
|
restored_user.accept_terms
|
|
restored_user.save!
|
|
source_user.accept_terms
|
|
source_user.save!
|
|
UserMerge.from(restored_user).into(source_user)
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(source_user.reload.preferences[:accepted_terms]).to_not be_nil
|
|
expect(restored_user.reload.preferences[:accepted_terms]).to_not be_nil
|
|
end
|
|
|
|
it 'should restore names' do
|
|
restored_user.name = "jimmy one"
|
|
restored_user.save!
|
|
source_user.name = "jenny one"
|
|
source_user.save!
|
|
UserMerge.from(restored_user).into(source_user)
|
|
source_user.name = "other name"
|
|
source_user.save!
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(restored_user.reload.name).to eq "jimmy one"
|
|
expect(source_user.reload.name).to eq "jenny one"
|
|
end
|
|
|
|
it 'should restore pseudonyms to the original user' do
|
|
pseudonym1 = source_user.pseudonyms.create!(unique_id: 'sam1@example.com')
|
|
pseudonym2 = account1.pseudonyms.create!(user: restored_user, unique_id: 'sam2@example.com')
|
|
pseudonym3 = account1.pseudonyms.create!(user: restored_user, unique_id: 'sam3@example.com')
|
|
UserMerge.from(restored_user).into(source_user)
|
|
SplitUsers.split_db_users(source_user)
|
|
|
|
source_user.reload
|
|
restored_user.reload
|
|
expect(pseudonym1.user).to eq source_user
|
|
expect(pseudonym2.user).to eq restored_user
|
|
expect(pseudonym3.user).to eq restored_user
|
|
end
|
|
|
|
it 'should not split if the data is too old' do
|
|
pseudonym1 = source_user.pseudonyms.create!(unique_id: 'sam1@example.com')
|
|
pseudonym2 = account1.pseudonyms.create!(user: restored_user, unique_id: 'sam2@example.com')
|
|
Timecop.travel(183.days.ago) do
|
|
UserMerge.from(restored_user).into(source_user)
|
|
end
|
|
|
|
expect(SplitUsers.split_db_users(source_user)).to eq []
|
|
|
|
expect(restored_user.workflow_state).to eq 'deleted'
|
|
expect(pseudonym1.reload.user).to eq source_user
|
|
expect(pseudonym2.reload.user).to eq source_user
|
|
end
|
|
|
|
it 'should use the setting for split time.' do
|
|
pseudonym1 = source_user.pseudonyms.create!(unique_id: 'sam1@example.com')
|
|
pseudonym2 = account1.pseudonyms.create!(user: restored_user, unique_id: 'sam2@example.com')
|
|
Setting.set('user_merge_to_split_time', '12')
|
|
Timecop.travel(15.days.ago) do
|
|
UserMerge.from(restored_user).into(source_user)
|
|
end
|
|
|
|
expect(SplitUsers.split_db_users(source_user)).to eq []
|
|
|
|
Setting.set('user_merge_to_split_time', '30')
|
|
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(pseudonym1.reload.user).to eq source_user
|
|
expect(pseudonym2.reload.user).to eq restored_user
|
|
end
|
|
|
|
describe 'with merge data' do
|
|
|
|
it "should move lti_id to the new user" do
|
|
course1.enroll_user(source_user)
|
|
course2.enroll_user(restored_user)
|
|
UserMerge.from(restored_user).into(source_user)
|
|
UserMerge.from(source_user).into(user3)
|
|
SplitUsers.split_db_users(user3)
|
|
expect(user3.reload.past_lti_ids.count).to eq 0
|
|
expect(source_user.reload.past_lti_ids.count).to eq 1
|
|
end
|
|
|
|
it 'should split multiple users if no merge_data is specified' do
|
|
enrollment1 = course1.enroll_student(restored_user, enrollment_state: 'active')
|
|
enrollment2 = course1.enroll_student(source_user, enrollment_state: 'active')
|
|
enrollment3 = course2.enroll_student(restored_user, enrollment_state: 'active')
|
|
enrollment4 = course3.enroll_teacher(restored_user)
|
|
enrollment5 = course1.enroll_teacher(user3)
|
|
UserMerge.from(restored_user).into(source_user)
|
|
UserMerge.from(user3).into(source_user)
|
|
SplitUsers.split_db_users(source_user)
|
|
|
|
restored_user.reload
|
|
source_user.reload
|
|
user3.reload
|
|
expect(restored_user).not_to be_deleted
|
|
expect(source_user).not_to be_deleted
|
|
expect(user3).not_to be_deleted
|
|
expect(enrollment1.reload.user).to eq restored_user
|
|
expect(enrollment1.workflow_state).to eq 'active'
|
|
expect(enrollment2.reload.user).to eq source_user
|
|
expect(enrollment3.reload.user).to eq restored_user
|
|
expect(enrollment4.reload.user).to eq restored_user
|
|
expect(enrollment5.reload.user).to eq user3
|
|
end
|
|
|
|
it 'should handle conflicting enrollments' do
|
|
enrollment1 = course1.enroll_student(restored_user, enrollment_state: 'active')
|
|
UserMerge.from(restored_user).into(source_user)
|
|
enrollment2 = course1.enroll_student(restored_user, enrollment_state: 'active')
|
|
SplitUsers.split_db_users(source_user)
|
|
|
|
restored_user.reload
|
|
source_user.reload
|
|
expect(restored_user).not_to be_deleted
|
|
expect(source_user).not_to be_deleted
|
|
expect(enrollment1.reload.user).to eq source_user
|
|
expect(enrollment2.reload.user).to eq restored_user
|
|
end
|
|
|
|
it 'should handle user_observers' do
|
|
observer1 = user_model
|
|
observer2 = user_model
|
|
add_linked_observer(restored_user, observer1)
|
|
add_linked_observer(source_user, observer2)
|
|
UserMerge.from(restored_user).into(source_user)
|
|
|
|
SplitUsers.split_db_users(source_user)
|
|
|
|
expect(restored_user.linked_observers).to eq [observer1]
|
|
expect(source_user.linked_observers).to eq [observer2]
|
|
end
|
|
|
|
it 'should handle access tokens' do
|
|
at = AccessToken.create!(user: restored_user, :developer_key => DeveloperKey.default)
|
|
UserMerge.from(restored_user).into(source_user)
|
|
expect(at.reload.user_id).to eq source_user.id
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(at.reload.user_id).to eq restored_user.id
|
|
end
|
|
|
|
it 'should handle polls' do
|
|
poll = Polling::Poll.create!(user: restored_user, question: 'A Test Poll', description: 'A test description.')
|
|
UserMerge.from(restored_user).into(source_user)
|
|
expect(poll.reload.user_id).to eq source_user.id
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(poll.reload.user_id).to eq restored_user.id
|
|
end
|
|
|
|
it 'should handle favorites' do
|
|
course1.enroll_user(restored_user)
|
|
fav = Favorite.create!(user: restored_user, context: course1)
|
|
UserMerge.from(restored_user).into(source_user)
|
|
expect(source_user.favorites.take.context_id).to eq course1.id
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(fav.reload.user_id).to eq restored_user.id
|
|
end
|
|
|
|
it 'should handle ignores' do
|
|
course1.enroll_user(restored_user)
|
|
assignment2 = assignment_model(course: course1)
|
|
ignore = Ignore.create!(asset: assignment2, user: restored_user, purpose: 'submitting')
|
|
UserMerge.from(restored_user).into(source_user)
|
|
expect(ignore.reload.user_id).to eq source_user.id
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(ignore.reload.user_id).to eq restored_user.id
|
|
end
|
|
|
|
it 'should handle conversations' do
|
|
sender = restored_user
|
|
recipient = user3
|
|
convo = sender.initiate_conversation([recipient])
|
|
UserMerge.from(restored_user).into(source_user)
|
|
expect(convo.reload.user_id).to eq source_user.id
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(convo.reload.user_id).to eq restored_user.id
|
|
end
|
|
|
|
it 'should handle attachments' do
|
|
attachment1 = Attachment.create!(user: restored_user,
|
|
context: restored_user,
|
|
filename: "test.txt",
|
|
uploaded_data: StringIO.new("first"))
|
|
attachment2 = Attachment.create!(user: source_user,
|
|
context: source_user,
|
|
filename: "test2.txt",
|
|
uploaded_data: StringIO.new("second"))
|
|
|
|
UserMerge.from(restored_user).into(source_user)
|
|
run_jobs
|
|
|
|
expect(attachment1.reload.context).to eq source_user
|
|
expect(restored_user.reload.attachments).to eq []
|
|
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(restored_user.reload.attachments).to eq [attachment1]
|
|
expect(source_user.reload.attachments).to eq [attachment2]
|
|
end
|
|
|
|
it 'should handle when observing merged user' do
|
|
link = add_linked_observer(source_user, restored_user)
|
|
UserMerge.from(restored_user).into(source_user)
|
|
|
|
SplitUsers.split_db_users(source_user)
|
|
|
|
expect(restored_user.reload.as_observer_observation_links.to_a).to eq [link]
|
|
expect(source_user.reload.as_student_observation_links.to_a).to eq [link]
|
|
end
|
|
|
|
|
|
it 'should handle as_observer_observation_links' do
|
|
observee1 = user_model
|
|
observee2 = user_model
|
|
add_linked_observer(observee1, restored_user)
|
|
add_linked_observer(observee2, source_user)
|
|
UserMerge.from(restored_user).into(source_user)
|
|
|
|
SplitUsers.split_db_users(source_user)
|
|
|
|
expect(restored_user.as_observer_observation_links).to eq observee1.as_student_observation_links
|
|
expect(source_user.as_observer_observation_links).to eq observee2.as_student_observation_links
|
|
end
|
|
|
|
it 'should handle duplicate user_observers' do
|
|
observer1 = user_model
|
|
observee1 = user_model
|
|
add_linked_observer(observee1, restored_user)
|
|
add_linked_observer(observee1, source_user)
|
|
add_linked_observer(restored_user, observer1)
|
|
add_linked_observer(source_user, observer1)
|
|
UserMerge.from(restored_user).into(source_user)
|
|
SplitUsers.split_db_users(source_user)
|
|
|
|
expect(restored_user.as_observer_observation_links.count).to eq 1
|
|
expect(source_user.as_observer_observation_links.count).to eq 1
|
|
expect(restored_user.linked_observers).to eq [observer1]
|
|
expect(source_user.linked_observers).to eq [observer1]
|
|
|
|
expect(restored_user.as_observer_observation_links.first.workflow_state).to eq 'active'
|
|
expect(source_user.as_observer_observation_links.first.workflow_state).to eq 'active'
|
|
expect(restored_user.as_student_observation_links.first.workflow_state).to eq 'active'
|
|
expect(source_user.as_student_observation_links.first.workflow_state).to eq 'active'
|
|
end
|
|
|
|
it 'should only split users from merge_data when specified' do
|
|
enrollment1 = course1.enroll_user(restored_user)
|
|
enrollment2 = course1.enroll_student(source_user, enrollment_state: 'active')
|
|
enrollment3 = course2.enroll_student(restored_user, enrollment_state: 'active')
|
|
enrollment4 = course3.enroll_teacher(restored_user)
|
|
enrollment5 = course1.enroll_teacher(user3)
|
|
UserMerge.from(restored_user).into(source_user)
|
|
UserMerge.from(user3).into(source_user)
|
|
merge_data = UserMergeData.where(user_id: source_user, from_user: restored_user).first
|
|
SplitUsers.split_db_users(source_user, merge_data)
|
|
|
|
restored_user.reload
|
|
source_user.reload
|
|
user3.reload
|
|
expect(restored_user).not_to be_deleted
|
|
expect(source_user).not_to be_deleted
|
|
expect(user3).to be_deleted
|
|
expect(enrollment1.reload.user).to eq restored_user
|
|
expect(enrollment2.reload.user).to eq source_user
|
|
expect(enrollment3.reload.user).to eq restored_user
|
|
expect(enrollment4.reload.user).to eq restored_user
|
|
expect(enrollment5.reload.user).to eq source_user
|
|
end
|
|
|
|
it "should move ccs to the new user (but only if they don't already exist)" do
|
|
notification = Notification.where(name: "Report Generated").first_or_create
|
|
# unconfirmed: active conflict
|
|
restored_user.communication_channels.create!(path: 'a@instructure.com')
|
|
source_user.communication_channels.create!(path: 'A@instructure.com') { |cc| cc.workflow_state = 'active' }
|
|
# active: unconfirmed conflict
|
|
restored_user.communication_channels.create!(path: 'b@instructure.com') { |cc| cc.workflow_state = 'active' }
|
|
cc1 = source_user.communication_channels.create!(path: 'B@instructure.com')
|
|
# active: active conflict + notification policy copy
|
|
np_cc = restored_user.communication_channels.create!(path: 'c@instructure.com') { |cc| cc.workflow_state = 'active' }
|
|
np_cc.notification_policies.create!(notification_id: notification.id, frequency: 'weekly')
|
|
needs_np = source_user.communication_channels.create!(path: 'C@instructure.com') { |cc| cc.workflow_state = 'active' }
|
|
# unconfirmed: unconfirmed conflict
|
|
restored_user.communication_channels.create!(path: 'd@instructure.com')
|
|
source_user.communication_channels.create!(path: 'D@instructure.com')
|
|
# retired: unconfirmed conflict
|
|
restored_user.communication_channels.create!(path: 'e@instructure.com') { |cc| cc.workflow_state = 'retired' }
|
|
source_user.communication_channels.create!(path: 'E@instructure.com')
|
|
# unconfirmed: retired conflict
|
|
restored_user.communication_channels.create!(path: 'f@instructure.com')
|
|
source_user.communication_channels.create!(path: 'F@instructure.com') { |cc| cc.workflow_state = 'retired' }
|
|
# retired: active conflict
|
|
restored_user.communication_channels.create!(path: 'g@instructure.com') { |cc| cc.workflow_state = 'retired' }
|
|
source_user.communication_channels.create!(path: 'G@instructure.com') { |cc| cc.workflow_state = 'active' }
|
|
# active: retired conflict
|
|
restored_user.communication_channels.create!(path: 'h@instructure.com') { |cc| cc.workflow_state = 'active' }
|
|
source_user.communication_channels.create!(path: 'H@instructure.com') { |cc| cc.workflow_state = 'retired' }
|
|
# retired: retired conflict
|
|
restored_user.communication_channels.create!(path: 'i@instructure.com') { |cc| cc.workflow_state = 'retired' }
|
|
source_user.communication_channels.create!(path: 'I@instructure.com') { |cc| cc.workflow_state = 'retired' }
|
|
# <nothing>: active
|
|
source_user.communication_channels.create!(path: 'J@instructure.com') { |cc| cc.workflow_state = 'active' }
|
|
# active: <nothing>
|
|
restored_user.communication_channels.create!(path: 'k@instructure.com') { |cc| cc.workflow_state = 'active' }
|
|
# <nothing>: unconfirmed
|
|
source_user.communication_channels.create!(path: 'L@instructure.com')
|
|
# unconfirmed: <nothing>
|
|
restored_user.communication_channels.create!(path: 'm@instructure.com')
|
|
# <nothing>: retired
|
|
source_user.communication_channels.create!(path: 'N@instructure.com') { |cc| cc.workflow_state = 'retired' }
|
|
# retired: <nothing>
|
|
restored_user.communication_channels.create!(path: 'o@instructure.com') { |cc| cc.workflow_state = 'retired' }
|
|
|
|
restored_user_ccs = restored_user.communication_channels.where.not(workflow_state: 'retired').
|
|
map { |cc| [cc.path, cc.workflow_state] }.sort
|
|
# cc will not be restored because it conflicted on merge and it was unconfirmed and it is frd deleted
|
|
source_user_ccs = source_user.communication_channels.where.not(id: cc1, workflow_state: 'retired').
|
|
map { |cc| [cc.path, cc.workflow_state] }.sort
|
|
|
|
UserMerge.from(restored_user).into(source_user)
|
|
expect(needs_np.notification_policies.take.frequency).to eq 'weekly'
|
|
SplitUsers.split_db_users(source_user)
|
|
restored_user.reload
|
|
source_user.reload
|
|
|
|
expect(restored_user.communication_channels.where.not(workflow_state: 'retired').
|
|
map { |cc| [cc.path, cc.workflow_state] }.sort).to eq restored_user_ccs
|
|
expect(source_user.communication_channels.where.not(workflow_state: 'retired').
|
|
map { |cc| [cc.path, cc.workflow_state] }.sort).to eq source_user_ccs
|
|
end
|
|
|
|
end
|
|
|
|
it 'should restore submissions' do
|
|
course1.enroll_student(restored_user, enrollment_state: 'active')
|
|
assignment = course1.assignments.new(title: "some assignment")
|
|
assignment.workflow_state = "published"
|
|
assignment.save
|
|
valid_attributes = {
|
|
grade: "1.5",
|
|
grader: @teacher,
|
|
url: "www.instructure.com"
|
|
}
|
|
submission = assignment.submissions.find_by!(user: restored_user)
|
|
submission.update!(valid_attributes)
|
|
|
|
UserMerge.from(restored_user).into(source_user)
|
|
expect(submission.reload.user).to eq source_user
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(submission.reload.user).to eq restored_user
|
|
end
|
|
|
|
it 'should handle conflicting submissions' do
|
|
course1.enroll_student(restored_user, enrollment_state: 'active')
|
|
course1.enroll_student(source_user, enrollment_state: 'active')
|
|
assignment = course1.assignments.new(title: "some assignment")
|
|
assignment.workflow_state = "published"
|
|
assignment.save
|
|
valid_attributes = {
|
|
grade: "1.5",
|
|
grader: @teacher,
|
|
url: "www.instructure.com"
|
|
}
|
|
submission1 = assignment.submissions.find_by!(user: restored_user)
|
|
submission1.update!(valid_attributes)
|
|
submission2 = assignment.submissions.find_by!(user: source_user)
|
|
submission2.update!(valid_attributes)
|
|
|
|
UserMerge.from(restored_user).into(source_user)
|
|
expect(submission1.reload.user).to eq restored_user
|
|
expect(submission2.reload.user).to eq source_user
|
|
Submission.where(id: submission1).update_all(workflow_state: 'deleted')
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(submission1.reload.user).to eq restored_user
|
|
expect(submission2.reload.user).to eq source_user
|
|
end
|
|
|
|
it 'should handle conflicting submissions other way too' do
|
|
course1.enroll_student(restored_user, enrollment_state: 'active')
|
|
course1.enroll_student(source_user, enrollment_state: 'active')
|
|
assignment = course1.assignments.new(title: "some assignment")
|
|
assignment.workflow_state = "published"
|
|
assignment.save
|
|
valid_attributes = {
|
|
grade: "1.5",
|
|
grader: @teacher,
|
|
url: "www.instructure.com"
|
|
}
|
|
submission1 = assignment.submissions.find_by!(user: restored_user)
|
|
submission1.update!(valid_attributes)
|
|
submission2 = assignment.submissions.find_by!(user: source_user)
|
|
|
|
UserMerge.from(restored_user).into(source_user)
|
|
expect(submission1.reload.user).to eq source_user
|
|
expect(submission2.reload.user).to eq restored_user
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(submission1.reload.user).to eq restored_user
|
|
expect(submission2.reload.user).to eq source_user
|
|
end
|
|
|
|
it 'should not blow up on deleted courses' do
|
|
course1.enroll_student(restored_user, enrollment_state: 'active')
|
|
UserMerge.from(restored_user).into(source_user)
|
|
course1.destroy
|
|
expect { SplitUsers.split_db_users(source_user) }.not_to raise_error
|
|
end
|
|
|
|
it 'should restore admins to the original state' do
|
|
admin = account1.account_users.create(user: restored_user)
|
|
admin2 = sub_account.account_users.create(user: restored_user)
|
|
admin3 = sub_account.account_users.create(user: source_user)
|
|
UserMerge.from(restored_user).into(source_user)
|
|
admin.reload.destroy
|
|
SplitUsers.split_db_users(source_user)
|
|
|
|
expect(admin.reload.workflow_state).to eq 'active'
|
|
expect(admin.reload.user).to eq restored_user
|
|
expect(admin2.reload.user).to eq restored_user
|
|
expect(admin3.reload.user).to eq source_user
|
|
end
|
|
|
|
context 'sharding' do
|
|
specs_require_sharding
|
|
let!(:shard1_source_user) { @shard1.activate { user_model } }
|
|
let!(:shard1_account) { @shard1.activate { Account.create! } }
|
|
let!(:shard1_course) { shard1_account.courses.create! }
|
|
|
|
it 'should handle access tokens' do
|
|
at = AccessToken.create!(user: restored_user, :developer_key => DeveloperKey.default)
|
|
UserMerge.from(restored_user).into(shard1_source_user)
|
|
expect(at.reload.user_id).to eq shard1_source_user.id
|
|
SplitUsers.split_db_users(shard1_source_user)
|
|
expect(at.reload.user_id).to eq restored_user.id
|
|
end
|
|
|
|
it 'should move submissions from new courses post merge when appropriate' do
|
|
pseudonym1 = restored_user.pseudonyms.create!(unique_id: 'sam1@example.com')
|
|
UserMerge.from(restored_user).into(shard1_source_user)
|
|
e = course1.enroll_student(shard1_source_user, enrollment_state: 'active')
|
|
Enrollment.where(id: e).update_all(sis_pseudonym_id: pseudonym1.id)
|
|
assignment = course1.assignments.new(title: "some assignment")
|
|
assignment.workflow_state = "published"
|
|
assignment.save
|
|
valid_attributes = {
|
|
grade: "1.5",
|
|
grader: @teacher,
|
|
url: "www.instructure.com"
|
|
}
|
|
submission = assignment.submissions.find_by!(user: shard1_source_user)
|
|
submission.update!(valid_attributes)
|
|
SplitUsers.split_db_users(shard1_source_user)
|
|
expect(submission.reload.user).to eq restored_user
|
|
end
|
|
|
|
it 'should handle user_observers cross shard' do
|
|
observer1 = user_model
|
|
observer2 = user_model
|
|
add_linked_observer(restored_user, observer1)
|
|
add_linked_observer(shard1_source_user, observer2)
|
|
UserMerge.from(restored_user).into(shard1_source_user)
|
|
expect(restored_user.linked_observers).to eq []
|
|
expect(shard1_source_user.linked_observers.pluck(:id).sort).to eq [observer1.id, observer2.id].sort
|
|
SplitUsers.split_db_users(shard1_source_user)
|
|
expect(restored_user.reload.linked_observers).to eq [observer1]
|
|
expect(shard1_source_user.reload.linked_observers).to eq [observer2]
|
|
end
|
|
|
|
it 'should handle conflicting submissions for cross shard users' do
|
|
course1.enroll_student(restored_user, enrollment_state: 'active')
|
|
course1.enroll_student(shard1_source_user, enrollment_state: 'active')
|
|
assignment = course1.assignments.new(title: "some assignment")
|
|
assignment.workflow_state = "published"
|
|
assignment.save
|
|
valid_attributes = {
|
|
grade: "1.5",
|
|
grader: @teacher,
|
|
url: "www.instructure.com"
|
|
}
|
|
submission1 = assignment.submissions.find_by!(user: restored_user)
|
|
submission1.update!(valid_attributes)
|
|
submission2 = assignment.submissions.find_by!(user: shard1_source_user)
|
|
|
|
UserMerge.from(restored_user).into(shard1_source_user)
|
|
expect(submission1.reload.user).to eq shard1_source_user
|
|
expect(submission2.reload.user).to eq restored_user
|
|
SplitUsers.split_db_users(shard1_source_user)
|
|
expect(submission1.reload.user).to eq restored_user
|
|
expect(submission2.reload.user).to eq shard1_source_user
|
|
end
|
|
|
|
it 'should restore admins to the original state' do
|
|
admin = account1.account_users.create(user: restored_user)
|
|
shard1_source_user.associate_with_shard(sub_account.shard)
|
|
admin2 = sub_account.account_users.create(user: shard1_source_user)
|
|
UserMerge.from(restored_user).into(shard1_source_user)
|
|
admin.reload.destroy
|
|
SplitUsers.split_db_users(shard1_source_user)
|
|
|
|
expect(admin.reload.workflow_state).to eq 'active'
|
|
expect(admin.reload.user).to eq restored_user
|
|
expect(admin2.reload.user).to eq shard1_source_user
|
|
end
|
|
|
|
it 'should merge a user across shards' do
|
|
pseudonym1 = restored_user.pseudonyms.create!(unique_id: 'sam1@example.com')
|
|
@shard1.activate do
|
|
account = Account.create!
|
|
@pseudonym2 = shard1_source_user.pseudonyms.create!(account: account, unique_id: 'sam1@example.com')
|
|
UserMerge.from(restored_user).into(shard1_source_user)
|
|
SplitUsers.split_db_users(shard1_source_user)
|
|
end
|
|
|
|
restored_user.reload
|
|
shard1_source_user.reload
|
|
|
|
expect(restored_user).not_to be_deleted
|
|
expect(pseudonym1.reload.user).to eq restored_user
|
|
expect(shard1_source_user.all_pseudonyms).to eq [@pseudonym2]
|
|
end
|
|
|
|
it "should split a user across shards with ccs" do
|
|
restored_user.communication_channels.create!(:path => 'a@example.com') { |cc| cc.workflow_state = 'active' }
|
|
restored_user_ccs = restored_user.communication_channels.map { |cc| [cc.path, cc.workflow_state] }.sort
|
|
source_user_ccs = shard1_source_user.communication_channels.map { |cc| [cc.path, cc.workflow_state] }.sort
|
|
|
|
@shard1.activate do
|
|
UserMerge.from(restored_user).into(shard1_source_user)
|
|
cc = shard1_source_user.reload.communication_channels.where(path: 'a@example.com').take
|
|
n = Notification.create!(name: 'Assignment Createds', subject: 'Tests', category: 'TestNevers')
|
|
NotificationPolicy.create(notification: n, communication_channel: cc, frequency: 'immediately')
|
|
SplitUsers.split_db_users(shard1_source_user)
|
|
end
|
|
|
|
restored_user.reload
|
|
shard1_source_user.reload
|
|
expect(restored_user.communication_channels.map { |cc| [cc.path, cc.workflow_state] }.sort).to eq restored_user_ccs
|
|
expect(shard1_source_user.communication_channels.map { |cc| [cc.path, cc.workflow_state] }.sort).to eq source_user_ccs
|
|
end
|
|
|
|
it 'should handle enrollments across shards' do
|
|
e = course1.enroll_user(restored_user)
|
|
@shard1.activate do
|
|
@e = shard1_course.enroll_user(shard1_source_user)
|
|
UserMerge.from(restored_user).into(shard1_source_user)
|
|
end
|
|
SplitUsers.split_db_users(shard1_source_user)
|
|
|
|
expect(e.reload.user).to eq restored_user
|
|
expect(@e.reload.user).to eq shard1_source_user
|
|
end
|
|
|
|
it "should work with cross-shard submissions" do
|
|
shard1_course.enroll_student(restored_user, enrollment_state: 'active')
|
|
assignment = shard1_course.assignments.create!(title: "some assignment", workflow_state: 'published', submission_types: "online_text_entry")
|
|
submission = assignment.submit_homework(restored_user, submission_type: 'online_text_entry', body: 'fooey')
|
|
|
|
UserMerge.from(restored_user).into(source_user)
|
|
SplitUsers.split_db_users(source_user)
|
|
expect(submission.reload.user).to eq restored_user
|
|
end
|
|
|
|
it "should copy notification policies" do
|
|
og_cc = restored_user.communication_channels.create!(:path => 'a@example.com') { |cc| cc.workflow_state = 'active' }
|
|
|
|
n = Notification.create!(name: 'Assignment', subject: 'Tests', category: 'TestNevers')
|
|
NotificationPolicy.create!(notification: n, communication_channel: og_cc, frequency: 'immediately')
|
|
|
|
@shard1.activate do
|
|
UserMerge.from(restored_user).into(shard1_source_user)
|
|
cc = shard1_source_user.communication_channels.where(path: 'a@example.com').take!
|
|
expect(cc.notification_policies.count).to eq 1
|
|
end
|
|
|
|
SplitUsers.split_db_users(shard1_source_user)
|
|
expect(shard1_source_user.communication_channels.count).to eq 0
|
|
end
|
|
|
|
it "should copy notification policies on conflict" do
|
|
og_cc = restored_user.communication_channels.create!(:path => 'a@example.com') { |cc| cc.workflow_state = 'active' }
|
|
|
|
n = Notification.create!(name: 'Assignment', subject: 'Tests', category: 'TestNevers')
|
|
NotificationPolicy.create!(notification: n, communication_channel: og_cc, frequency: 'immediately')
|
|
# conflict_cc
|
|
cc = shard1_source_user.communication_channels.create!(:path => 'a@example.com') { |cc| cc.workflow_state = 'active' }
|
|
|
|
UserMerge.from(restored_user).into(shard1_source_user)
|
|
expect(cc.notification_policies.count).to eq 1
|
|
|
|
SplitUsers.split_db_users(shard1_source_user)
|
|
expect(shard1_source_user.communication_channels.count).to eq 1
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|