diff --git a/app/models/account_migration.rb b/app/models/account_migration.rb index c5805173c..e8590bc2b 100644 --- a/app/models/account_migration.rb +++ b/app/models/account_migration.rb @@ -81,6 +81,10 @@ class AccountMigration < ApplicationRecord new_person.owner end + def newest_user + newest_person.owner + end + def lock_old_user! old_user&.lock_access! end @@ -106,7 +110,7 @@ class AccountMigration < ApplicationRecord # We need to resend contacts of users of our pod for the remote new person so that the remote pod received this # contact information from the authoritative source. def dispatch_contacts - new_person.contacts.sharing.each do |contact| + newest_person.contacts.sharing.each do |contact| Diaspora::Federation::Dispatcher.defer_dispatch(contact.user, contact) end end @@ -179,7 +183,7 @@ class AccountMigration < ApplicationRecord def duplicate_person_contacts Contact .joins("INNER JOIN contacts as c2 ON (contacts.user_id = c2.user_id AND contacts.person_id=#{old_person.id} AND"\ - " c2.person_id=#{new_person.id})") + " c2.person_id=#{newest_person.id})") end def duplicate_person_likes @@ -187,7 +191,7 @@ class AccountMigration < ApplicationRecord .joins("INNER JOIN likes as l2 ON (likes.target_id = l2.target_id "\ "AND likes.target_type = l2.target_type "\ "AND likes.author_id=#{old_person.id} AND"\ - " l2.author_id=#{new_person.id})") + " l2.author_id=#{newest_person.id})") end def duplicate_person_participations @@ -195,41 +199,41 @@ class AccountMigration < ApplicationRecord .joins("INNER JOIN participations as p2 ON (participations.target_id = p2.target_id "\ "AND participations.target_type = p2.target_type "\ "AND participations.author_id=#{old_person.id} AND"\ - " p2.author_id=#{new_person.id})") + " p2.author_id=#{newest_person.id})") end def duplicate_person_poll_participations PollParticipation .joins("INNER JOIN poll_participations as p2 ON (poll_participations.poll_id = p2.poll_id "\ "AND poll_participations.author_id=#{old_person.id} AND"\ - " p2.author_id=#{new_person.id})") + " p2.author_id=#{newest_person.id})") end def eliminate_user_duplicates Aspect .joins("INNER JOIN aspects as a2 ON (aspects.name = a2.name AND aspects.user_id=#{old_user.id} - AND a2.user_id=#{new_user.id})") + AND a2.user_id=#{newest_user.id})") .destroy_all Contact .joins("INNER JOIN contacts as c2 ON (contacts.person_id = c2.person_id AND contacts.user_id=#{old_user.id} AND"\ - " c2.user_id=#{new_user.id})") + " c2.user_id=#{newest_user.id})") .destroy_all TagFollowing .joins("INNER JOIN tag_followings as t2 ON (tag_followings.tag_id = t2.tag_id AND"\ - " tag_followings.user_id=#{old_user.id} AND t2.user_id=#{new_user.id})") + " tag_followings.user_id=#{old_user.id} AND t2.user_id=#{newest_user.id})") .destroy_all end def update_person_references - logger.debug "Updating references from person id=#{old_person.id} to person id=#{new_person.id}" + logger.debug "Updating references from person id=#{old_person.id} to person id=#{newest_person.id}" eliminate_person_duplicates - update_references(person_references, old_person, new_person.id) + update_references(person_references, old_person, newest_person.id) end def update_user_references - logger.debug "Updating references from user id=#{old_user.id} to user id=#{new_user.id}" + logger.debug "Updating references from user id=#{old_user.id} to user id=#{newest_user.id}" eliminate_user_duplicates - update_references(user_references, old_user, new_user.id) + update_references(user_references, old_user, newest_user.id) end def update_references(references, object, new_id) diff --git a/spec/integration/account_migration_spec.rb b/spec/integration/account_migration_spec.rb index 177a38fbf..aad22ffa8 100644 --- a/spec/integration/account_migration_spec.rb +++ b/spec/integration/account_migration_spec.rb @@ -149,6 +149,36 @@ shared_examples_for "migration scenarios initiated locally" do end end +shared_examples_for "migration scenarios with local user rename" do + it "updates user references" do + invited_user = FactoryGirl.create(:user, invited_by: old_user) + aspect = FactoryGirl.create(:aspect, user: old_user, name: r_str) + contact = FactoryGirl.create(:contact, user: old_user) + service = FactoryGirl.create(:service, user: old_user) + pref = UserPreference.create!(user: old_user, email_type: "also_commented") + tag_following = FactoryGirl.create(:tag_following, user: old_user) + block = FactoryGirl.create(:block, user: old_user) + notification = FactoryGirl.create(:notification, recipient: old_user) + report = FactoryGirl.create(:report, user: old_user) + authorization = FactoryGirl.create(:auth_with_read, user: old_user) + share_visibility = FactoryGirl.create(:share_visibility, user: old_user) + + run_migration + + expect(invited_user.reload.invited_by).to eq(new_user) + expect(aspect.reload.user).to eq(new_user) + expect(contact.reload.user).to eq(new_user) + expect(service.reload.user).to eq(new_user) + expect(pref.reload.user).to eq(new_user) + expect(tag_following.reload.user).to eq(new_user) + expect(block.reload.user).to eq(new_user) + expect(notification.reload.recipient).to eq(new_user) + expect(report.reload.user).to eq(new_user) + expect(authorization.reload.user).to eq(new_user) + expect(share_visibility.reload.user).to eq(new_user) + end +end + describe "account migration" do # this is the case when we receive account migration message from the federation context "remotely initiated" do @@ -173,6 +203,22 @@ describe "account migration" do include_examples "every migration scenario" include_examples "migration scenarios initiated remotely" + + context "when new person has been migrated before" do + let(:intermidiate_person) { create_remote_user("remote-d.net").person } + + before do + AccountMigration.create!(old_person: intermidiate_person, new_person: new_person).perform! + end + + def run_migration + AccountMigration.create!(old_person: old_person, new_person: intermidiate_person).perform! + end + + include_examples "every migration scenario" + + include_examples "migration scenarios initiated remotely" + end end # this is the case when we're a pod, which was left by a person in favor of remote one @@ -200,6 +246,24 @@ describe "account migration" do user.reload end end + + context "when new person has been migrated before" do + let(:intermidiate_person) { create_remote_user("remote-d.net").person } + + before do + AccountMigration.create!(old_person: intermidiate_person, new_person: new_person).perform! + end + + def run_migration + AccountMigration.create!(old_person: old_user.person, new_person: intermidiate_person).perform! + end + + include_examples "every migration scenario" + + include_examples "migration scenarios initiated remotely" + + it_behaves_like "migration scenarios with local old user" + end end end @@ -228,6 +292,24 @@ describe "account migration" do it_behaves_like "migration scenarios initiated locally" do let!(:remote_contact) { create_remote_contact(new_user, "remote-friend.org") } end + + context "when new person has been migrated before" do + let(:intermidiate_person) { FactoryGirl.create(:user).person } + + before do + AccountMigration.create!(old_person: intermidiate_person, new_person: new_person).perform! + end + + def run_migration + AccountMigration.create!( + old_person: old_person, + new_person: intermidiate_person, + old_private_key: old_user.serialized_private_key + ).perform! + end + + include_examples "every migration scenario" + end end # this is the case when a user changes diaspora id but stays on the same pod @@ -254,32 +336,32 @@ describe "account migration" do expect(old_user.reload).to be_a_clear_account end - it "updates user references" do - invited_user = FactoryGirl.create(:user, invited_by: old_user) - aspect = FactoryGirl.create(:aspect, user: old_user, name: r_str) - contact = FactoryGirl.create(:contact, user: old_user) - service = FactoryGirl.create(:service, user: old_user) - pref = UserPreference.create!(user: old_user, email_type: "also_commented") - tag_following = FactoryGirl.create(:tag_following, user: old_user) - block = FactoryGirl.create(:block, user: old_user) - notification = FactoryGirl.create(:notification, recipient: old_user) - report = FactoryGirl.create(:report, user: old_user) - authorization = FactoryGirl.create(:auth_with_read, user: old_user) - share_visibility = FactoryGirl.create(:share_visibility, user: old_user) + include_examples "migration scenarios with local user rename" - run_migration + context "when new user has been migrated before" do + let(:intermidiate_person) { FactoryGirl.create(:user).person } - expect(invited_user.reload.invited_by).to eq(new_user) - expect(aspect.reload.user).to eq(new_user) - expect(contact.reload.user).to eq(new_user) - expect(service.reload.user).to eq(new_user) - expect(pref.reload.user).to eq(new_user) - expect(tag_following.reload.user).to eq(new_user) - expect(block.reload.user).to eq(new_user) - expect(notification.reload.recipient).to eq(new_user) - expect(report.reload.user).to eq(new_user) - expect(authorization.reload.user).to eq(new_user) - expect(share_visibility.reload.user).to eq(new_user) + before do + AccountMigration.create!(old_person: intermidiate_person, new_person: new_person).perform! + end + + def run_migration + AccountMigration.create!( + old_person: old_person, + new_person: intermidiate_person + ).perform! + end + + include_examples "every migration scenario" + + it_behaves_like "migration scenarios with local old user" + + it "clears the old user account" do + run_migration + expect(old_user.reload).to be_a_clear_account + end + + include_examples "migration scenarios with local user rename" end end end