Merge pull request #6750 from cmrd-senya/account_migration_message
Account migration model/message
This commit is contained in:
commit
cbc3900d59
28 changed files with 1073 additions and 76 deletions
|
|
@ -56,6 +56,7 @@ If so, please delete it since it will prevent the federation from working proper
|
||||||
* Support cmd+enter to submit posts, comments and conversations [#7524](https://github.com/diaspora/diaspora/pull/7524)
|
* Support cmd+enter to submit posts, comments and conversations [#7524](https://github.com/diaspora/diaspora/pull/7524)
|
||||||
* Add markdown editor for posts, comments and conversations on mobile [#7235](https://github.com/diaspora/diaspora/pull/7235)
|
* Add markdown editor for posts, comments and conversations on mobile [#7235](https://github.com/diaspora/diaspora/pull/7235)
|
||||||
* Mark as "Mobile Web App Capable" on Android [#7534](https://github.com/diaspora/diaspora/pull/7534)
|
* Mark as "Mobile Web App Capable" on Android [#7534](https://github.com/diaspora/diaspora/pull/7534)
|
||||||
|
* Add support for receiving account migrations [#6750](https://github.com/diaspora/diaspora/pull/6750)
|
||||||
|
|
||||||
# 0.6.8.0
|
# 0.6.8.0
|
||||||
|
|
||||||
|
|
|
||||||
165
app/models/account_migration.rb
Normal file
165
app/models/account_migration.rb
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
class AccountMigration < ApplicationRecord
|
||||||
|
include Diaspora::Federated::Base
|
||||||
|
|
||||||
|
belongs_to :old_person, class_name: "Person"
|
||||||
|
belongs_to :new_person, class_name: "Person"
|
||||||
|
|
||||||
|
validates :old_person, uniqueness: true
|
||||||
|
validates :new_person, uniqueness: true
|
||||||
|
|
||||||
|
after_create :lock_old_user!
|
||||||
|
|
||||||
|
attr_accessor :old_private_key
|
||||||
|
|
||||||
|
def receive(*)
|
||||||
|
perform!
|
||||||
|
end
|
||||||
|
|
||||||
|
def public?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def sender
|
||||||
|
@sender ||= old_user || ephemeral_sender
|
||||||
|
end
|
||||||
|
|
||||||
|
# executes a migration plan according to this AccountMigration object
|
||||||
|
def perform!
|
||||||
|
raise "already performed" if performed?
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
account_deleter.tombstone_person_and_profile
|
||||||
|
account_deleter.close_user if user_left_our_pod?
|
||||||
|
account_deleter.tombstone_user if user_changed_id_locally?
|
||||||
|
|
||||||
|
update_all_references
|
||||||
|
end
|
||||||
|
|
||||||
|
dispatch if locally_initiated?
|
||||||
|
dispatch_contacts if remotely_initiated?
|
||||||
|
end
|
||||||
|
|
||||||
|
def performed?
|
||||||
|
old_person.closed_account?
|
||||||
|
end
|
||||||
|
|
||||||
|
# We assume that migration message subscribers are people that are subscribed to a new user profile updates.
|
||||||
|
# Since during the migration we update contact references, this includes all the contacts of the old person.
|
||||||
|
# In case when a user migrated to our pod from a remote one, we include remote person to subscribers so that
|
||||||
|
# the new pod is informed about the migration as well.
|
||||||
|
def subscribers
|
||||||
|
new_user.profile.subscribers.remote.to_a.tap do |subscribers|
|
||||||
|
subscribers.push(old_person) if old_person.remote?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Normally pod initiates migration locally when the new user is local. Then the pod creates AccountMigration object
|
||||||
|
# itself. If new user is remote, then AccountMigration object is normally received via the federation and this is
|
||||||
|
# remote initiation then.
|
||||||
|
def remotely_initiated?
|
||||||
|
new_person.remote?
|
||||||
|
end
|
||||||
|
|
||||||
|
def locally_initiated?
|
||||||
|
!remotely_initiated?
|
||||||
|
end
|
||||||
|
|
||||||
|
def old_user
|
||||||
|
old_person.owner
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_user
|
||||||
|
new_person.owner
|
||||||
|
end
|
||||||
|
|
||||||
|
def lock_old_user!
|
||||||
|
old_user&.lock_access!
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_left_our_pod?
|
||||||
|
old_user && !new_user
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_changed_id_locally?
|
||||||
|
old_user && new_user
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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|
|
||||||
|
Diaspora::Federation::Dispatcher.defer_dispatch(contact.user, contact)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def dispatch
|
||||||
|
Diaspora::Federation::Dispatcher.build(sender, self).dispatch
|
||||||
|
end
|
||||||
|
|
||||||
|
EphemeralUser = Struct.new(:diaspora_handle, :serialized_private_key) do
|
||||||
|
def id
|
||||||
|
diaspora_handle
|
||||||
|
end
|
||||||
|
|
||||||
|
def encryption_key
|
||||||
|
OpenSSL::PKey::RSA.new(serialized_private_key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ephemeral_sender
|
||||||
|
raise "can't build sender without old private key defined" if old_private_key.nil?
|
||||||
|
EphemeralUser.new(old_person.diaspora_handle, old_private_key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_all_references
|
||||||
|
update_person_references
|
||||||
|
update_user_references if user_changed_id_locally?
|
||||||
|
end
|
||||||
|
|
||||||
|
def person_references
|
||||||
|
references = Person.reflections.reject {|key, _|
|
||||||
|
%w[profile owner notifications pod].include?(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
references.map {|key, value|
|
||||||
|
{value.foreign_key => key}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_references
|
||||||
|
references = User.reflections.reject {|key, _|
|
||||||
|
%w[
|
||||||
|
person profile auto_follow_back_aspect invited_by aspect_memberships contact_people followed_tags
|
||||||
|
ignored_people conversation_visibilities pairwise_pseudonymous_identifiers conversations o_auth_applications
|
||||||
|
].include?(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
references.map {|key, value|
|
||||||
|
{value.foreign_key => key}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_person_references
|
||||||
|
logger.debug "Updating references from person id=#{old_person.id} to person id=#{new_person.id}"
|
||||||
|
update_references(person_references, old_person, new_person.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_user_references
|
||||||
|
logger.debug "Updating references from user id=#{old_user.id} to user id=#{new_user.id}"
|
||||||
|
update_references(user_references, old_user, new_user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_references(references, object, new_id)
|
||||||
|
references.each do |pair|
|
||||||
|
key_id = pair.flatten[0]
|
||||||
|
association = pair.flatten[1]
|
||||||
|
object.send(association).update_all(key_id => new_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_deleter
|
||||||
|
@account_deleter ||= AccountDeleter.new(old_person)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -40,7 +40,10 @@ class Person < ApplicationRecord
|
||||||
has_many :likes, foreign_key: :author_id, dependent: :destroy # This person's own likes
|
has_many :likes, foreign_key: :author_id, dependent: :destroy # This person's own likes
|
||||||
has_many :participations, :foreign_key => :author_id, :dependent => :destroy
|
has_many :participations, :foreign_key => :author_id, :dependent => :destroy
|
||||||
has_many :poll_participations, foreign_key: :author_id, dependent: :destroy
|
has_many :poll_participations, foreign_key: :author_id, dependent: :destroy
|
||||||
has_many :conversation_visibilities
|
has_many :conversation_visibilities, dependent: :destroy
|
||||||
|
has_many :messages, foreign_key: :author_id, dependent: :destroy
|
||||||
|
has_many :conversations, foreign_key: :author_id, dependent: :destroy
|
||||||
|
has_many :blocks, dependent: :destroy
|
||||||
|
|
||||||
has_many :roles
|
has_many :roles
|
||||||
|
|
||||||
|
|
@ -307,11 +310,6 @@ class Person < ApplicationRecord
|
||||||
serialized_public_key
|
serialized_public_key
|
||||||
end
|
end
|
||||||
|
|
||||||
def exported_key= new_key
|
|
||||||
raise "Don't change a key" if serialized_public_key
|
|
||||||
serialized_public_key = new_key
|
|
||||||
end
|
|
||||||
|
|
||||||
# discovery (webfinger)
|
# discovery (webfinger)
|
||||||
def self.find_or_fetch_by_identifier(diaspora_id)
|
def self.find_or_fetch_by_identifier(diaspora_id)
|
||||||
# exiting person?
|
# exiting person?
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ class Profile < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def tombstone!
|
def tombstone!
|
||||||
|
@tag_string = nil
|
||||||
self.taggings.delete_all
|
self.taggings.delete_all
|
||||||
clearable_fields.each do |field|
|
clearable_fields.each do |field|
|
||||||
self[field] = nil
|
self[field] = nil
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ class User < ApplicationRecord
|
||||||
belongs_to :auto_follow_back_aspect, class_name: "Aspect", optional: true
|
belongs_to :auto_follow_back_aspect, class_name: "Aspect", optional: true
|
||||||
belongs_to :invited_by, class_name: "User", optional: true
|
belongs_to :invited_by, class_name: "User", optional: true
|
||||||
|
|
||||||
|
has_many :invited_users, class_name: "User", inverse_of: :invited_by, foreign_key: :invited_by_id
|
||||||
|
|
||||||
has_many :aspect_memberships, :through => :aspects
|
has_many :aspect_memberships, :through => :aspects
|
||||||
|
|
||||||
has_many :contacts
|
has_many :contacts
|
||||||
|
|
|
||||||
14
db/migrate/20170730154117_create_account_migrations.rb
Normal file
14
db/migrate/20170730154117_create_account_migrations.rb
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
class CreateAccountMigrations < ActiveRecord::Migration[5.1]
|
||||||
|
def change
|
||||||
|
create_table :account_migrations do |t|
|
||||||
|
t.integer :old_person_id, null: false
|
||||||
|
t.integer :new_person_id, null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_foreign_key :account_migrations, :people, column: :old_person_id
|
||||||
|
add_foreign_key :account_migrations, :people, column: :new_person_id
|
||||||
|
|
||||||
|
add_index :account_migrations, %i[old_person_id new_person_id], unique: true
|
||||||
|
add_index :account_migrations, :old_person_id, unique: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -30,18 +30,20 @@ class AccountDeleter
|
||||||
delete_contacts_of_me
|
delete_contacts_of_me
|
||||||
tombstone_person_and_profile
|
tombstone_person_and_profile
|
||||||
|
|
||||||
if self.user
|
close_user if user
|
||||||
#user deletion methods
|
|
||||||
remove_share_visibilities_on_contacts_posts
|
|
||||||
delete_standard_user_associations
|
|
||||||
disconnect_contacts
|
|
||||||
tombstone_user
|
|
||||||
end
|
|
||||||
|
|
||||||
mark_account_deletion_complete
|
mark_account_deletion_complete
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# user deletion methods
|
||||||
|
def close_user
|
||||||
|
remove_share_visibilities_on_contacts_posts
|
||||||
|
disconnect_contacts
|
||||||
|
delete_standard_user_associations
|
||||||
|
tombstone_user
|
||||||
|
end
|
||||||
|
|
||||||
#user deletions
|
#user deletions
|
||||||
def normal_ar_user_associates_to_delete
|
def normal_ar_user_associates_to_delete
|
||||||
%i[tag_followings services aspects user_preferences
|
%i[tag_followings services aspects user_preferences
|
||||||
|
|
@ -53,7 +55,7 @@ class AccountDeleter
|
||||||
end
|
end
|
||||||
|
|
||||||
def ignored_ar_user_associations
|
def ignored_ar_user_associations
|
||||||
%i[followed_tags invited_by contact_people aspect_memberships
|
%i[followed_tags invited_by invited_users contact_people aspect_memberships
|
||||||
ignored_people share_visibilities conversation_visibilities conversations reports]
|
ignored_people share_visibilities conversation_visibilities conversations reports]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -70,7 +72,7 @@ class AccountDeleter
|
||||||
end
|
end
|
||||||
|
|
||||||
def disconnect_contacts
|
def disconnect_contacts
|
||||||
user.contacts.reload.destroy_all
|
user.contacts.destroy_all
|
||||||
end
|
end
|
||||||
|
|
||||||
# Currently this would get deleted due to the db foreign key constrainsts,
|
# Currently this would get deleted due to the db foreign key constrainsts,
|
||||||
|
|
@ -97,12 +99,12 @@ class AccountDeleter
|
||||||
end
|
end
|
||||||
|
|
||||||
def normal_ar_person_associates_to_delete
|
def normal_ar_person_associates_to_delete
|
||||||
%i[posts photos mentions participations roles]
|
%i[posts photos mentions participations roles blocks]
|
||||||
end
|
end
|
||||||
|
|
||||||
def ignored_or_special_ar_person_associations
|
def ignored_or_special_ar_person_associations
|
||||||
%i[comments likes poll_participations contacts notification_actors notifications owner profile
|
%i[comments likes poll_participations contacts notification_actors notifications owner profile
|
||||||
conversation_visibilities pod]
|
conversation_visibilities pod conversations messages]
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_account_deletion_complete
|
def mark_account_deletion_complete
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,13 @@ module Diaspora
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.account_migration(account_migration)
|
||||||
|
DiasporaFederation::Entities::AccountMigration.new(
|
||||||
|
author: account_migration.sender.diaspora_handle,
|
||||||
|
profile: profile(account_migration.new_person.profile)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def self.comment(comment)
|
def self.comment(comment)
|
||||||
DiasporaFederation::Entities::Comment.new(
|
DiasporaFederation::Entities::Comment.new(
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ module Diaspora
|
||||||
# used in Diaspora::Federation::Receive
|
# used in Diaspora::Federation::Receive
|
||||||
def self.receiver_for(federation_entity)
|
def self.receiver_for(federation_entity)
|
||||||
case federation_entity
|
case federation_entity
|
||||||
|
when DiasporaFederation::Entities::AccountMigration then :account_migration
|
||||||
when DiasporaFederation::Entities::Comment then :comment
|
when DiasporaFederation::Entities::Comment then :comment
|
||||||
when DiasporaFederation::Entities::Contact then :contact
|
when DiasporaFederation::Entities::Contact then :contact
|
||||||
when DiasporaFederation::Entities::Conversation then :conversation
|
when DiasporaFederation::Entities::Conversation then :conversation
|
||||||
|
|
@ -24,6 +25,7 @@ module Diaspora
|
||||||
# used in Diaspora::Federation::Entities
|
# used in Diaspora::Federation::Entities
|
||||||
def self.builder_for(diaspora_entity)
|
def self.builder_for(diaspora_entity)
|
||||||
case diaspora_entity
|
case diaspora_entity
|
||||||
|
when AccountMigration then :account_migration
|
||||||
when AccountDeletion then :account_deletion
|
when AccountDeletion then :account_deletion
|
||||||
when Comment then :comment
|
when Comment then :comment
|
||||||
when Contact then :contact
|
when Contact then :contact
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,14 @@ module Diaspora
|
||||||
AccountDeletion.create!(person: author_of(entity))
|
AccountDeletion.create!(person: author_of(entity))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.account_migration(entity)
|
||||||
|
profile = profile(entity.profile)
|
||||||
|
AccountMigration.create!(
|
||||||
|
old_person: Person.by_account_identifier(entity.author),
|
||||||
|
new_person: profile.person
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def self.comment(entity)
|
def self.comment(entity)
|
||||||
receive_relayable(Comment, entity) do
|
receive_relayable(Comment, entity) do
|
||||||
Comment.new(
|
Comment.new(
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
describe StreamsController, :type => :controller do
|
describe StreamsController, :type => :controller do
|
||||||
describe '#multi' do
|
describe '#multi' do
|
||||||
before do
|
before do
|
||||||
|
allow(Workers::SendPublic).to receive(:perform_async)
|
||||||
sign_in alice, scope: :user
|
sign_in alice, scope: :user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,11 @@ FactoryGirl.define do
|
||||||
association :person
|
association :person
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory :account_migration do
|
||||||
|
association :old_person, factory: :person
|
||||||
|
association :new_person, factory: :person
|
||||||
|
end
|
||||||
|
|
||||||
factory :like do
|
factory :like do
|
||||||
association :author, :factory => :person
|
association :author, :factory => :person
|
||||||
association :target, :factory => :status_message
|
association :target, :factory => :status_message
|
||||||
|
|
@ -145,6 +150,11 @@ FactoryGirl.define do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory(:share_visibility) do
|
||||||
|
user
|
||||||
|
association :shareable, factory: :status_message
|
||||||
|
end
|
||||||
|
|
||||||
factory(:location) do
|
factory(:location) do
|
||||||
sequence(:address) {|n| "Fernsehturm Berlin, #{n}, Berlin, Germany" }
|
sequence(:address) {|n| "Fernsehturm Berlin, #{n}, Berlin, Germany" }
|
||||||
sequence(:lat) {|n| 52.520645 + 0.0000001 * n }
|
sequence(:lat) {|n| 52.520645 + 0.0000001 * n }
|
||||||
|
|
@ -222,13 +232,8 @@ FactoryGirl.define do
|
||||||
sequence(:uid) { |token| "00000#{token}" }
|
sequence(:uid) { |token| "00000#{token}" }
|
||||||
sequence(:access_token) { |token| "12345#{token}" }
|
sequence(:access_token) { |token| "12345#{token}" }
|
||||||
sequence(:access_secret) { |token| "98765#{token}" }
|
sequence(:access_secret) { |token| "98765#{token}" }
|
||||||
end
|
|
||||||
|
|
||||||
factory :service_user do
|
user
|
||||||
sequence(:uid) { |id| "a#{id}"}
|
|
||||||
sequence(:name) { |num| "Rob Fergus the #{num.ordinalize}" }
|
|
||||||
association :service
|
|
||||||
photo_url "/assets/user/adams.jpg"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :pod do
|
factory :pod do
|
||||||
|
|
@ -354,7 +359,18 @@ FactoryGirl.define do
|
||||||
text SecureRandom.hex(1000)
|
text SecureRandom.hex(1000)
|
||||||
end
|
end
|
||||||
|
|
||||||
factory(:status, :parent => :status_message)
|
factory(:status, parent: :status_message)
|
||||||
|
|
||||||
|
factory :block do
|
||||||
|
user
|
||||||
|
person
|
||||||
|
end
|
||||||
|
|
||||||
|
factory :report do
|
||||||
|
user
|
||||||
|
association :item, factory: :status_message
|
||||||
|
text "offensive content"
|
||||||
|
end
|
||||||
|
|
||||||
factory :o_auth_application, class: Api::OpenidConnect::OAuthApplication do
|
factory :o_auth_application, class: Api::OpenidConnect::OAuthApplication do
|
||||||
client_name { "Diaspora Test Client #{r_str}" }
|
client_name { "Diaspora Test Client #{r_str}" }
|
||||||
|
|
|
||||||
|
|
@ -12,25 +12,11 @@ describe "deleteing account", type: :request do
|
||||||
DataGenerator.create(subject, :generic_user_data)
|
DataGenerator.create(subject, :generic_user_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "deletes all of the user data" do
|
it_behaves_like "deletes all of the user data"
|
||||||
expect {
|
|
||||||
account_removal_method
|
|
||||||
}.to change(nil, "user preferences empty?") { UserPreference.where(user_id: user.id).empty? }.to(be_truthy)
|
|
||||||
.and(change(nil, "notifications empty?") { Notification.where(recipient_id: user.id).empty? }.to(be_truthy))
|
|
||||||
.and(change(nil, "blocks empty?") { Block.where(user_id: user.id).empty? }.to(be_truthy))
|
|
||||||
.and(change(nil, "services empty?") { Service.where(user_id: user.id).empty? }.to(be_truthy))
|
|
||||||
.and(change(nil, "share visibilities empty?") { ShareVisibility.where(user_id: user.id).empty? }.to(be_truthy))
|
|
||||||
.and(change(nil, "aspects empty?") { user.aspects.empty? }.to(be_truthy))
|
|
||||||
.and(change(nil, "contacts empty?") { user.contacts.empty? }.to(be_truthy))
|
|
||||||
.and(change(nil, "tag followings empty?") { user.tag_followings.empty? }.to(be_truthy))
|
|
||||||
.and(change(nil, "clearable fields blank?") {
|
|
||||||
user.send(:clearable_fields).map {|field|
|
|
||||||
user.reload[field].blank?
|
|
||||||
}
|
|
||||||
}.to(eq([true] * user.send(:clearable_fields).count)))
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like "it removes the person associations"
|
it_behaves_like "it removes the person associations"
|
||||||
|
|
||||||
|
it_behaves_like "it keeps the person conversations"
|
||||||
end
|
end
|
||||||
|
|
||||||
context "of remote person" do
|
context "of remote person" do
|
||||||
|
|
@ -41,5 +27,13 @@ describe "deleteing account", type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like "it removes the person associations"
|
it_behaves_like "it removes the person associations"
|
||||||
|
|
||||||
|
it_behaves_like "it keeps the person conversations"
|
||||||
|
|
||||||
|
it_behaves_like "it makes account closed and clears profile" do
|
||||||
|
before do
|
||||||
|
account_removal_method
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
204
spec/integration/account_migration_spec.rb
Normal file
204
spec/integration/account_migration_spec.rb
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
require "integration/federation/federation_helper"
|
||||||
|
|
||||||
|
def create_remote_contact(user, pod_host)
|
||||||
|
FactoryGirl.create(
|
||||||
|
:contact,
|
||||||
|
user: user,
|
||||||
|
person: FactoryGirl.create(
|
||||||
|
:person,
|
||||||
|
pod: Pod.find_or_create_by(url: "http://#{pod_host}"),
|
||||||
|
diaspora_handle: "#{r_str}@#{pod_host}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for "old person account is closed and profile is cleared" do
|
||||||
|
subject { old_user.person }
|
||||||
|
|
||||||
|
before do
|
||||||
|
run_migration
|
||||||
|
subject.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples "it makes account closed and clears profile"
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for "old person doesn't have any reference left" do
|
||||||
|
let(:person) { old_user.person }
|
||||||
|
|
||||||
|
before do
|
||||||
|
DataGenerator.create(person, :generic_person_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_removal_method
|
||||||
|
run_migration
|
||||||
|
person.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples "it removes the person associations"
|
||||||
|
|
||||||
|
include_examples "it removes the person conversations"
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for "every migration scenario" do
|
||||||
|
it_behaves_like "it updates person references"
|
||||||
|
|
||||||
|
it_behaves_like "old person account is closed and profile is cleared"
|
||||||
|
|
||||||
|
it_behaves_like "old person doesn't have any reference left"
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for "migration scenarios with local old user" do
|
||||||
|
it "locks the old user account" do
|
||||||
|
run_migration
|
||||||
|
expect(old_user.reload).to be_a_locked_account
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for "migration scenarios initiated remotely" do
|
||||||
|
it "resends known contacts to the new user" do
|
||||||
|
contacts = Array.new(2) { FactoryGirl.create(:contact, person: old_user.person, sharing: true) }
|
||||||
|
expect(DiasporaFederation::Federation::Sender).to receive(:private)
|
||||||
|
.twice do |sender_id, obj_str, _urls, _xml|
|
||||||
|
expect(sender_id).to eq(contacts.first.user_id)
|
||||||
|
expect(obj_str).to eq("Contact:#{contacts.first.user.diaspora_handle}:#{new_user.diaspora_handle}")
|
||||||
|
contacts.shift
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
inlined_jobs { run_migration }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for "migration scenarios initiated locally" do
|
||||||
|
it "dispatches account migration message to the federation" do
|
||||||
|
expect(DiasporaFederation::Federation::Sender).to receive(:public) do |sender_id, obj_str, urls, xml|
|
||||||
|
if old_user.person.remote?
|
||||||
|
expect(sender_id).to eq(old_user.diaspora_handle)
|
||||||
|
else
|
||||||
|
expect(sender_id).to eq(old_user.id)
|
||||||
|
end
|
||||||
|
expect(obj_str).to eq("AccountMigration:#{old_user.diaspora_handle}:#{new_user.diaspora_handle}")
|
||||||
|
subscribers = [remote_contact.person]
|
||||||
|
subscribers.push(old_user) if old_user.person.remote?
|
||||||
|
expect(urls).to match_array(subscribers.map(&:url).map {|url| "#{url}receive/public" })
|
||||||
|
|
||||||
|
entity = nil
|
||||||
|
expect {
|
||||||
|
magic_env = Nokogiri::XML(xml).root
|
||||||
|
entity = DiasporaFederation::Salmon::MagicEnvelope
|
||||||
|
.unenvelop(magic_env, old_user.diaspora_handle).payload
|
||||||
|
}.not_to raise_error
|
||||||
|
|
||||||
|
expect(entity).to be_a(DiasporaFederation::Entities::AccountMigration)
|
||||||
|
expect(entity.author).to eq(old_user.diaspora_handle)
|
||||||
|
expect(entity.profile.author).to eq(new_user.diaspora_handle)
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
inlined_jobs do
|
||||||
|
run_migration
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "account migration" do
|
||||||
|
# this is the case when we receive account migration message from the federation
|
||||||
|
context "remotely initiated" do
|
||||||
|
let(:entity) { create_account_migration_entity(old_user.diaspora_handle, new_user) }
|
||||||
|
|
||||||
|
def run_migration
|
||||||
|
allow_callbacks(%i[queue_public_receive fetch_public_key receive_entity])
|
||||||
|
post_message(generate_payload(entity, old_user))
|
||||||
|
end
|
||||||
|
|
||||||
|
context "both new and old profiles are remote" do
|
||||||
|
include_context "with remote old user"
|
||||||
|
include_context "with remote new user"
|
||||||
|
|
||||||
|
it "creates AccountMigration db object" do
|
||||||
|
run_migration
|
||||||
|
expect(AccountMigration.where(old_person: old_user.person, new_person: new_user.person)).to exist
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples "every migration scenario"
|
||||||
|
|
||||||
|
include_examples "migration scenarios initiated remotely"
|
||||||
|
end
|
||||||
|
|
||||||
|
# this is the case when we're a pod, which was left by a person in favor of remote one
|
||||||
|
context "old user is local, new user is remote" do
|
||||||
|
include_context "with local old user"
|
||||||
|
include_context "with remote new user"
|
||||||
|
|
||||||
|
include_examples "every migration scenario"
|
||||||
|
|
||||||
|
include_examples "migration scenarios initiated remotely"
|
||||||
|
|
||||||
|
it_behaves_like "migration scenarios with local old user"
|
||||||
|
|
||||||
|
it_behaves_like "deletes all of the user data" do
|
||||||
|
let(:user) { old_user }
|
||||||
|
|
||||||
|
before do
|
||||||
|
DataGenerator.create(user, :generic_user_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_removal_method
|
||||||
|
run_migration
|
||||||
|
user.reload
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "locally initiated" do
|
||||||
|
before do
|
||||||
|
allow(DiasporaFederation.callbacks).to receive(:trigger).and_call_original
|
||||||
|
end
|
||||||
|
|
||||||
|
# this is the case when user migrates to our pod from a remote one
|
||||||
|
context "old user is remote and new user is local" do
|
||||||
|
include_context "with remote old user"
|
||||||
|
include_context "with local new user"
|
||||||
|
|
||||||
|
def run_migration
|
||||||
|
AccountMigration.create!(
|
||||||
|
old_person: old_user.person,
|
||||||
|
new_person: new_user.person,
|
||||||
|
old_private_key: old_user.serialized_private_key
|
||||||
|
).perform!
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples "every migration scenario"
|
||||||
|
|
||||||
|
it_behaves_like "migration scenarios initiated locally" do
|
||||||
|
let!(:remote_contact) { create_remote_contact(new_user, "remote-friend.org") }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# this is the case when a user changes diaspora id but stays on the same pod
|
||||||
|
context "old user is local and new user is local" do
|
||||||
|
include_context "with local old user"
|
||||||
|
include_context "with local new user"
|
||||||
|
|
||||||
|
def run_migration
|
||||||
|
AccountMigration.create!(old_person: old_user.person, new_person: new_user.person).perform!
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples "every migration scenario"
|
||||||
|
|
||||||
|
it_behaves_like "migration scenarios initiated locally" do
|
||||||
|
let!(:remote_contact) { create_remote_contact(old_user, "remote-friend.org") }
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
it_behaves_like "it updates user references"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -6,21 +6,43 @@ def remote_user_on_pod_c
|
||||||
@remote_on_c ||= create_remote_user("remote-c.net")
|
@remote_on_c ||= create_remote_user("remote-c.net")
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_remote_user(pod)
|
def allow_private_key_fetch(user)
|
||||||
|
allow(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||||
|
:fetch_private_key, user.diaspora_handle
|
||||||
|
) { user.encryption_key }
|
||||||
|
end
|
||||||
|
|
||||||
|
def allow_public_key_fetch(user)
|
||||||
|
allow(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||||
|
:fetch_public_key, user.diaspora_handle
|
||||||
|
) { OpenSSL::PKey::RSA.new(user.person.serialized_public_key) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_undiscovered_user(pod)
|
||||||
FactoryGirl.build(:user).tap do |user|
|
FactoryGirl.build(:user).tap do |user|
|
||||||
allow(user).to receive(:person).and_return(
|
allow(user).to receive(:person).and_return(
|
||||||
FactoryGirl.create(:person,
|
FactoryGirl.build(:person,
|
||||||
profile: FactoryGirl.build(:profile),
|
profile: FactoryGirl.build(:profile),
|
||||||
serialized_public_key: user.encryption_key.public_key.export,
|
serialized_public_key: user.encryption_key.public_key.export,
|
||||||
pod: Pod.find_or_create_by(url: "http://#{pod}"),
|
pod: Pod.find_or_create_by(url: "http://#{pod}"),
|
||||||
diaspora_handle: "#{user.username}@#{pod}")
|
diaspora_handle: "#{user.username}@#{pod}")
|
||||||
)
|
)
|
||||||
allow(DiasporaFederation.callbacks).to receive(:trigger).with(
|
end
|
||||||
:fetch_private_key, user.diaspora_handle
|
end
|
||||||
) { user.encryption_key }
|
|
||||||
allow(DiasporaFederation.callbacks).to receive(:trigger).with(
|
def expect_person_discovery(undiscovered_user)
|
||||||
:fetch_public_key, user.diaspora_handle
|
allow(Person).to receive(:find_or_fetch_by_identifier).with(any_args).and_call_original
|
||||||
) { OpenSSL::PKey::RSA.new(user.person.serialized_public_key) }
|
expect(Person).to receive(:find_or_fetch_by_identifier).with(undiscovered_user.diaspora_handle) {
|
||||||
|
undiscovered_user.person.save!
|
||||||
|
undiscovered_user.person
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_remote_user(pod)
|
||||||
|
create_undiscovered_user(pod).tap do |user|
|
||||||
|
user.person.save!
|
||||||
|
allow_private_key_fetch(user)
|
||||||
|
allow_public_key_fetch(user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -44,6 +66,14 @@ def create_relayable_entity(entity_name, parent, diaspora_id)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_account_migration_entity(diaspora_id, new_user)
|
||||||
|
Fabricate(
|
||||||
|
:account_migration_entity,
|
||||||
|
author: diaspora_id,
|
||||||
|
profile: Diaspora::Federation::Entities.build(new_user.profile)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def generate_payload(entity, remote_user, recipient=nil)
|
def generate_payload(entity, remote_user, recipient=nil)
|
||||||
magic_env = DiasporaFederation::Salmon::MagicEnvelope.new(
|
magic_env = DiasporaFederation::Salmon::MagicEnvelope.new(
|
||||||
entity,
|
entity,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ describe "Receive federation messages feature" do
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:sender) { remote_user_on_pod_b }
|
let(:sender) { remote_user_on_pod_b }
|
||||||
let(:sender_id) { remote_user_on_pod_b.diaspora_handle }
|
let(:sender_id) { sender.diaspora_handle }
|
||||||
|
|
||||||
context "with public receive" do
|
context "with public receive" do
|
||||||
let(:recipient) { nil }
|
let(:recipient) { nil }
|
||||||
|
|
@ -29,6 +29,80 @@ describe "Receive federation messages feature" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "account migration" do
|
||||||
|
# In case when sender is unknown we should just ignore the migration
|
||||||
|
# but this depends on https://github.com/diaspora/diaspora_federation/issues/72
|
||||||
|
# which is low-priority, so we just discover the sender profile in this case.
|
||||||
|
# But there won't be a spec for that.
|
||||||
|
|
||||||
|
let(:entity) { create_account_migration_entity(sender_id, new_user) }
|
||||||
|
|
||||||
|
def run_migration
|
||||||
|
post_message(generate_payload(entity, sender))
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with undiscovered new user profile" do
|
||||||
|
before do
|
||||||
|
allow_callbacks(%i[fetch_public_key])
|
||||||
|
allow_private_key_fetch(new_user)
|
||||||
|
expect_person_discovery(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:new_user) { create_undiscovered_user("example.org") }
|
||||||
|
|
||||||
|
it "receives account migration correctly" do
|
||||||
|
run_migration
|
||||||
|
expect(AccountMigration.where(old_person: sender.person, new_person: new_user.person)).to exist
|
||||||
|
expect(AccountMigration.find_by(old_person: sender.person, new_person: new_user.person)).to be_performed
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't accept the same migration for the second time" do
|
||||||
|
run_migration
|
||||||
|
expect {
|
||||||
|
run_migration
|
||||||
|
}.to raise_error(ActiveRecord::RecordInvalid)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't accept second migration for the same sender" do
|
||||||
|
run_migration
|
||||||
|
expect {
|
||||||
|
entity = create_account_migration_entity(sender_id, create_remote_user("example.org"))
|
||||||
|
post_message(generate_payload(entity, sender))
|
||||||
|
}.to raise_error(ActiveRecord::RecordInvalid)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't accept second migration for the same new user profile" do
|
||||||
|
run_migration
|
||||||
|
expect {
|
||||||
|
sender = create_remote_user("example.org")
|
||||||
|
entity = create_account_migration_entity(sender.diaspora_handle, new_user)
|
||||||
|
post_message(generate_payload(entity, sender))
|
||||||
|
}.to raise_error(ActiveRecord::RecordInvalid)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when our pod was left" do
|
||||||
|
let(:sender) { FactoryGirl.create(:user) }
|
||||||
|
|
||||||
|
it "locks the old user account access" do
|
||||||
|
run_migration
|
||||||
|
expect(sender.reload.access_locked?).to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with discovered profile" do
|
||||||
|
let(:new_user) { create_remote_user("example.org") }
|
||||||
|
|
||||||
|
it "updates person profile with data from entity" do
|
||||||
|
new_user.profile.bio = "my updated biography"
|
||||||
|
expect(entity.profile.bio).to eq("my updated biography")
|
||||||
|
expect(new_user.profile.reload.bio).not_to eq("my updated biography")
|
||||||
|
run_migration
|
||||||
|
expect(new_user.profile.reload.bio).to eq("my updated biography")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "reshare" do
|
context "reshare" do
|
||||||
it "reshare of public post passes" do
|
it "reshare of public post passes" do
|
||||||
post = FactoryGirl.create(:status_message, author: alice.person, public: true)
|
post = FactoryGirl.create(:status_message, author: alice.person, public: true)
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,6 @@ describe AccountDeleter do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#perform' do
|
describe '#perform' do
|
||||||
user_removal_methods = %i[
|
|
||||||
delete_standard_user_associations
|
|
||||||
remove_share_visibilities_on_contacts_posts
|
|
||||||
disconnect_contacts tombstone_user
|
|
||||||
]
|
|
||||||
|
|
||||||
person_removal_methods = %i[
|
person_removal_methods = %i[
|
||||||
delete_contacts_of_me
|
delete_contacts_of_me
|
||||||
delete_standard_person_associations
|
delete_standard_person_associations
|
||||||
|
|
@ -32,7 +26,7 @@ describe AccountDeleter do
|
||||||
@account_deletion.perform!
|
@account_deletion.perform!
|
||||||
end
|
end
|
||||||
|
|
||||||
(user_removal_methods + person_removal_methods).each do |method|
|
[*person_removal_methods, :close_user].each do |method|
|
||||||
|
|
||||||
it "calls ##{method.to_s}" do
|
it "calls ##{method.to_s}" do
|
||||||
expect(@account_deletion).to receive(method)
|
expect(@account_deletion).to receive(method)
|
||||||
|
|
@ -64,11 +58,8 @@ describe AccountDeleter do
|
||||||
@person_deletion.perform!
|
@person_deletion.perform!
|
||||||
end
|
end
|
||||||
|
|
||||||
(user_removal_methods).each do |method|
|
it "does not call #close_user" do
|
||||||
|
expect(@person_deletion).not_to receive(:close_user)
|
||||||
it "does not call ##{method.to_s}" do
|
|
||||||
expect(@person_deletion).not_to receive(method)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
(person_removal_methods).each do |method|
|
(person_removal_methods).each do |method|
|
||||||
|
|
@ -81,6 +72,24 @@ describe AccountDeleter do
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#close_user" do
|
||||||
|
user_removal_methods = %i[
|
||||||
|
delete_standard_user_associations
|
||||||
|
remove_share_visibilities_on_contacts_posts
|
||||||
|
disconnect_contacts tombstone_user
|
||||||
|
]
|
||||||
|
|
||||||
|
after do
|
||||||
|
@account_deletion.perform!
|
||||||
|
end
|
||||||
|
|
||||||
|
user_removal_methods.each do |method|
|
||||||
|
it "calls ##{method}" do
|
||||||
|
expect(@account_deletion).to receive(method)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "#delete_standard_user_associations" do
|
describe "#delete_standard_user_associations" do
|
||||||
it 'removes all standard user associaltions' do
|
it 'removes all standard user associaltions' do
|
||||||
@account_deletion.normal_ar_user_associates_to_delete.each do |asso|
|
@account_deletion.normal_ar_user_associates_to_delete.each do |asso|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,16 @@ describe Diaspora::Federation::Entities do
|
||||||
expect(federation_entity.author).to eq(diaspora_entity.person.diaspora_handle)
|
expect(federation_entity.author).to eq(diaspora_entity.person.diaspora_handle)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "builds an account migration" do
|
||||||
|
diaspora_entity = FactoryGirl.build(:account_migration)
|
||||||
|
diaspora_entity.old_private_key = OpenSSL::PKey::RSA.generate(1024).export
|
||||||
|
federation_entity = described_class.build(diaspora_entity)
|
||||||
|
|
||||||
|
expect(federation_entity).to be_instance_of(DiasporaFederation::Entities::AccountMigration)
|
||||||
|
expect(federation_entity.author).to eq(diaspora_entity.old_person.diaspora_handle)
|
||||||
|
expect(federation_entity.profile.author).to eq(diaspora_entity.new_person.diaspora_handle)
|
||||||
|
end
|
||||||
|
|
||||||
it "builds a comment" do
|
it "builds a comment" do
|
||||||
diaspora_entity = FactoryGirl.build(:comment)
|
diaspora_entity = FactoryGirl.build(:comment)
|
||||||
federation_entity = described_class.build(diaspora_entity)
|
federation_entity = described_class.build(diaspora_entity)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,20 @@ describe Diaspora::Federation::Receive do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe ".account_migration" do
|
||||||
|
let(:new_person) { FactoryGirl.create(:person) }
|
||||||
|
let(:profile_entity) { Fabricate(:profile_entity, author: new_person.diaspora_handle) }
|
||||||
|
let(:account_migration_entity) {
|
||||||
|
Fabricate(:account_migration_entity, author: sender.diaspora_handle, profile: profile_entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
it "saves the account deletion" do
|
||||||
|
Diaspora::Federation::Receive.account_migration(account_migration_entity)
|
||||||
|
|
||||||
|
expect(AccountMigration.exists?(old_person: sender, new_person: new_person)).to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe ".comment" do
|
describe ".comment" do
|
||||||
let(:comment_entity) {
|
let(:comment_entity) {
|
||||||
build_relayable_federation_entity(
|
build_relayable_federation_entity(
|
||||||
|
|
|
||||||
148
spec/models/account_migration_spec.rb
Normal file
148
spec/models/account_migration_spec.rb
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
require "integration/federation/federation_helper"
|
||||||
|
|
||||||
|
describe AccountMigration, type: :model do
|
||||||
|
describe "create!" do
|
||||||
|
include_context "with local old user"
|
||||||
|
|
||||||
|
it "locks old local user after creation" do
|
||||||
|
expect {
|
||||||
|
AccountMigration.create!(old_person: old_person, new_person: FactoryGirl.create(:person))
|
||||||
|
}.to change { old_user.reload.access_locked? }.to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:old_person) { FactoryGirl.create(:person) }
|
||||||
|
let(:new_person) { FactoryGirl.create(:person) }
|
||||||
|
let(:account_migration) {
|
||||||
|
AccountMigration.create!(old_person: old_person, new_person: new_person)
|
||||||
|
}
|
||||||
|
|
||||||
|
describe "receive" do
|
||||||
|
it "calls perform!" do
|
||||||
|
expect(account_migration).to receive(:perform!)
|
||||||
|
account_migration.receive
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "sender" do
|
||||||
|
context "with remote old user" do
|
||||||
|
include_context "with remote old user"
|
||||||
|
|
||||||
|
it "creates ephemeral user when private key is provided" do
|
||||||
|
account_migration.old_private_key = old_user.serialized_private_key
|
||||||
|
sender = account_migration.sender
|
||||||
|
expect(sender.id).to eq(old_user.diaspora_handle)
|
||||||
|
expect(sender.diaspora_handle).to eq(old_user.diaspora_handle)
|
||||||
|
expect(sender.encryption_key.to_s).to eq(old_user.encryption_key.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises when no private key is provided" do
|
||||||
|
expect {
|
||||||
|
account_migration.sender
|
||||||
|
}.to raise_error("can't build sender without old private key defined")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with local old user" do
|
||||||
|
include_context "with local old user"
|
||||||
|
|
||||||
|
it "matches the old user" do
|
||||||
|
expect(account_migration.sender).to eq(old_user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "performed?" do
|
||||||
|
it "is changed after perform!" do
|
||||||
|
expect {
|
||||||
|
account_migration.perform!
|
||||||
|
}.to change(account_migration, :performed?).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls old_person.closed_account?" do
|
||||||
|
expect(account_migration.old_person).to receive(:closed_account?)
|
||||||
|
account_migration.performed?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with local new user" do
|
||||||
|
include_context "with local new user"
|
||||||
|
|
||||||
|
describe "subscribers" do
|
||||||
|
it "picks remote subscribers of new user profile and old person" do
|
||||||
|
_local_friend, remote_contact = DataGenerator.create(new_user, %i[mutual_friend remote_mutual_friend])
|
||||||
|
expect(account_migration.new_person.owner.profile).to receive(:subscribers).and_call_original
|
||||||
|
expect(account_migration.subscribers).to match_array([remote_contact.person, old_person])
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with local old user" do
|
||||||
|
include_context "with local old user"
|
||||||
|
|
||||||
|
it "doesn't include old person" do
|
||||||
|
expect(account_migration.subscribers).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "perform!" do
|
||||||
|
# TODO: add references update tests
|
||||||
|
# This spec is missing references update tests. We didn't come with a good idea of how to test it
|
||||||
|
# and it is currently covered by integration tests. But it's beter to add these tests at some point
|
||||||
|
# in future when we have more time to think about it.
|
||||||
|
|
||||||
|
let(:embedded_account_deleter) { account_migration.send(:account_deleter) }
|
||||||
|
|
||||||
|
it "raises if already performed" do
|
||||||
|
expect(account_migration).to receive(:performed?).and_return(true)
|
||||||
|
expect {
|
||||||
|
account_migration.perform!
|
||||||
|
}.to raise_error("already performed")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls AccountDeleter#tombstone_person_and_profile" do
|
||||||
|
expect(embedded_account_deleter).to receive(:tombstone_person_and_profile)
|
||||||
|
account_migration.perform!
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with local old and remote new users" do
|
||||||
|
include_context "with local old user"
|
||||||
|
|
||||||
|
it "calls AccountDeleter#close_user" do
|
||||||
|
expect(embedded_account_deleter).to receive(:close_user)
|
||||||
|
account_migration.perform!
|
||||||
|
end
|
||||||
|
|
||||||
|
it "resends contacts to the remote pod" do
|
||||||
|
contact = FactoryGirl.create(:contact, person: old_person, sharing: true)
|
||||||
|
expect(Diaspora::Federation::Dispatcher).to receive(:defer_dispatch).with(contact.user, contact)
|
||||||
|
account_migration.perform!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with local new and remote old users" do
|
||||||
|
include_context "with remote old user"
|
||||||
|
include_context "with local new user"
|
||||||
|
|
||||||
|
it "dispatches account migration message" do
|
||||||
|
expect(account_migration).to receive(:sender).and_return(old_user)
|
||||||
|
dispatcher = double
|
||||||
|
expect(dispatcher).to receive(:dispatch)
|
||||||
|
expect(Diaspora::Federation::Dispatcher).to receive(:build)
|
||||||
|
.with(old_user, account_migration)
|
||||||
|
.and_return(dispatcher)
|
||||||
|
account_migration.perform!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with local old and new users" do
|
||||||
|
include_context "with local old user"
|
||||||
|
include_context "with local new user"
|
||||||
|
|
||||||
|
it "calls AccountDeleter#tombstone_user" do
|
||||||
|
expect(embedded_account_deleter).to receive(:tombstone_user)
|
||||||
|
account_migration.perform!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -287,6 +287,12 @@ describe Profile, :type => :model do
|
||||||
expect(@profile.taggings).to receive(:delete_all)
|
expect(@profile.taggings).to receive(:delete_all)
|
||||||
@profile.tombstone!
|
@profile.tombstone!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "doesn't recreate taggings if tag string was requested" do
|
||||||
|
@profile.tag_string
|
||||||
|
@profile.tombstone!
|
||||||
|
expect(@profile.taggings).to be_empty
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#clearable_fields" do
|
describe "#clearable_fields" do
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,27 @@
|
||||||
# licensed under the Affero General Public License version 3 or later. See
|
# licensed under the Affero General Public License version 3 or later. See
|
||||||
# the COPYRIGHT file.
|
# the COPYRIGHT file.
|
||||||
|
|
||||||
shared_examples_for "it removes the person associations" do
|
shared_examples_for "deletes all of the user data" do
|
||||||
RSpec::Matchers.define_negated_matcher :remain, :change
|
it "deletes all of the user data" do
|
||||||
|
expect(user).not_to be_a_clear_account
|
||||||
|
|
||||||
|
expect {
|
||||||
|
account_removal_method
|
||||||
|
}.to change(nil, "user preferences empty?") { UserPreference.where(user_id: user.id).empty? }
|
||||||
|
.to(be_truthy)
|
||||||
|
.and(change(nil, "notifications empty?") { Notification.where(recipient_id: user.id).empty? }.to(be_truthy))
|
||||||
|
.and(change(nil, "blocks empty?") { Block.where(user_id: user.id).empty? }.to(be_truthy))
|
||||||
|
.and(change(nil, "services empty?") { Service.where(user_id: user.id).empty? }.to(be_truthy))
|
||||||
|
.and(change(nil, "share visibilities empty?") { ShareVisibility.where(user_id: user.id).empty? }.to(be_truthy))
|
||||||
|
.and(change(nil, "aspects empty?") { user.aspects.empty? }.to(be_truthy))
|
||||||
|
.and(change(nil, "contacts empty?") { user.contacts.empty? }.to(be_truthy))
|
||||||
|
.and(change(nil, "tag followings empty?") { user.tag_followings.empty? }.to(be_truthy))
|
||||||
|
|
||||||
|
expect(user.reload).to be_a_clear_account
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for "it removes the person associations" do
|
||||||
it "removes all of the person associations" do
|
it "removes all of the person associations" do
|
||||||
expect {
|
expect {
|
||||||
account_removal_method
|
account_removal_method
|
||||||
|
|
@ -20,9 +38,39 @@ shared_examples_for "it removes the person associations" do
|
||||||
.and(change(nil, "conversation visibilities empty?") {
|
.and(change(nil, "conversation visibilities empty?") {
|
||||||
ConversationVisibility.where(person_id: person.id).empty?
|
ConversationVisibility.where(person_id: person.id).empty?
|
||||||
}.to(be_truthy))
|
}.to(be_truthy))
|
||||||
.and(remain(nil, "conversations empty?") { Conversation.where(author: person).empty? }.from(be_falsey))
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for "it keeps the person conversations" do
|
||||||
|
RSpec::Matchers.define_negated_matcher :remain, :change
|
||||||
|
|
||||||
|
it "remains the person conversations" do
|
||||||
|
expect {
|
||||||
|
account_removal_method
|
||||||
|
}.to remain(nil, "conversations empty?") { Conversation.where(author: person).empty? }
|
||||||
|
.from(be_falsey)
|
||||||
.and(remain(nil, "conversation visibilities of other participants empty?") {
|
.and(remain(nil, "conversation visibilities of other participants empty?") {
|
||||||
ConversationVisibility.where(conversation: Conversation.where(author: person)).empty?
|
ConversationVisibility.where(conversation: Conversation.where(author: person)).empty?
|
||||||
}.from(be_falsey))
|
}.from(be_falsey))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
shared_examples_for "it removes the person conversations" do
|
||||||
|
it "removes the person conversations" do
|
||||||
|
expect {
|
||||||
|
account_removal_method
|
||||||
|
}.to change(nil, "conversations empty?") { Conversation.where(author: person).empty? }
|
||||||
|
.to(be_truthy)
|
||||||
|
.and(change(nil, "conversation visibilities of other participants empty?") {
|
||||||
|
ConversationVisibility.where(conversation: Conversation.where(author: person)).empty?
|
||||||
|
}.to(be_truthy))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# In fact this example group if for testing effect of AccountDeleter.tombstone_person_and_profile
|
||||||
|
shared_examples_for "it makes account closed and clears profile" do
|
||||||
|
it "" do
|
||||||
|
expect(subject).to be_a_closed_account
|
||||||
|
expect(subject.profile).to be_a_clear_profile
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
||||||
187
spec/shared_behaviors/account_migration.rb
Normal file
187
spec/shared_behaviors/account_migration.rb
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
shared_context "with local old user" do
|
||||||
|
let(:old_user) { FactoryGirl.create(:user) }
|
||||||
|
let(:old_person) { old_user.person }
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_context "with local new user" do
|
||||||
|
let(:new_user) { FactoryGirl.create(:user) }
|
||||||
|
let(:new_person) { new_user.person }
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_context "with remote old user" do
|
||||||
|
let(:old_user) { remote_user_on_pod_c }
|
||||||
|
let(:old_person) { old_user.person }
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_context "with remote new user" do
|
||||||
|
let(:new_user) { remote_user_on_pod_b }
|
||||||
|
let(:new_person) { new_user.person }
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for "it updates person references" do
|
||||||
|
it "updates contact reference" do
|
||||||
|
contact = FactoryGirl.create(:contact, person: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(contact.reload.person).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates status message reference" do
|
||||||
|
post = FactoryGirl.create(:status_message, author: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(post.reload.author).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates reshare reference" do
|
||||||
|
reshare = FactoryGirl.create(:reshare, author: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(reshare.reload.author).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates photo reference" do
|
||||||
|
photo = FactoryGirl.create(:photo, author: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(photo.reload.author).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates comment reference" do
|
||||||
|
comment = FactoryGirl.create(:comment, author: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(comment.reload.author).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates like reference" do
|
||||||
|
like = FactoryGirl.create(:like, author: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(like.reload.author).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates participations reference" do
|
||||||
|
participation = FactoryGirl.create(:participation, author: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(participation.reload.author).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates poll participations reference" do
|
||||||
|
poll_participation = FactoryGirl.create(:poll_participation, author: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(poll_participation.reload.author).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates conversation visibilities reference" do
|
||||||
|
conversation = FactoryGirl.build(:conversation)
|
||||||
|
FactoryGirl.create(:contact, user: old_user, person: conversation.author) if old_person.local?
|
||||||
|
conversation.participants << old_person
|
||||||
|
conversation.save!
|
||||||
|
visibility = ConversationVisibility.find_by(person_id: old_person.id)
|
||||||
|
run_migration
|
||||||
|
expect(visibility.reload.person).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates message reference" do
|
||||||
|
message = FactoryGirl.create(:message, author: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(message.reload.author).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates conversation reference" do
|
||||||
|
conversation = FactoryGirl.create(:conversation, author: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(conversation.reload.author).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates block references" do
|
||||||
|
user = FactoryGirl.create(:user)
|
||||||
|
block = user.blocks.create(person: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(block.reload.person).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates role reference" do
|
||||||
|
role = FactoryGirl.create(:role, person: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(role.reload.person).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates notification actors" do
|
||||||
|
notification = FactoryGirl.build(:notification)
|
||||||
|
notification.actors << old_person
|
||||||
|
notification.save!
|
||||||
|
actor = notification.notification_actors.find_by(person_id: old_person.id)
|
||||||
|
run_migration
|
||||||
|
expect(actor.reload.person).to eq(new_person)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates mention reference" do
|
||||||
|
mention = FactoryGirl.create(:mention, person: old_person)
|
||||||
|
run_migration
|
||||||
|
expect(mention.reload.person).to eq(new_person)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for "it updates user references" do
|
||||||
|
it "updates invited users reference" do
|
||||||
|
invited_user = FactoryGirl.create(:user, invited_by: old_user)
|
||||||
|
run_migration
|
||||||
|
expect(invited_user.reload.invited_by).to eq(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates aspect reference" do
|
||||||
|
aspect = FactoryGirl.create(:aspect, user: old_user, name: r_str)
|
||||||
|
run_migration
|
||||||
|
expect(aspect.reload.user).to eq(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates contact reference" do
|
||||||
|
contact = FactoryGirl.create(:contact, user: old_user)
|
||||||
|
run_migration
|
||||||
|
expect(contact.reload.user).to eq(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates services reference" do
|
||||||
|
service = FactoryGirl.create(:service, user: old_user)
|
||||||
|
run_migration
|
||||||
|
expect(service.reload.user).to eq(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates user preference references" do
|
||||||
|
pref = UserPreference.create!(user: old_user, email_type: "also_commented")
|
||||||
|
run_migration
|
||||||
|
expect(pref.reload.user).to eq(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates tag following references" do
|
||||||
|
tag_following = FactoryGirl.create(:tag_following, user: old_user)
|
||||||
|
run_migration
|
||||||
|
expect(tag_following.reload.user).to eq(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates blocks refrences" do
|
||||||
|
block = FactoryGirl.create(:block, user: old_user)
|
||||||
|
run_migration
|
||||||
|
expect(block.reload.user).to eq(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates notification refrences" do
|
||||||
|
notification = FactoryGirl.create(:notification, recipient: old_user)
|
||||||
|
run_migration
|
||||||
|
expect(notification.reload.recipient).to eq(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates report refrences" do
|
||||||
|
report = FactoryGirl.create(:report, user: old_user)
|
||||||
|
run_migration
|
||||||
|
expect(report.reload.user).to eq(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates authorization refrences" do
|
||||||
|
authorization = FactoryGirl.create(:auth_with_read, user: old_user)
|
||||||
|
run_migration
|
||||||
|
expect(authorization.reload.user).to eq(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates share visibility refrences" do
|
||||||
|
share_visibility = FactoryGirl.create(:share_visibility, user: old_user)
|
||||||
|
run_migration
|
||||||
|
expect(share_visibility.reload.user).to eq(new_user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -14,6 +14,7 @@ describe DataGenerator do
|
||||||
generator.generic_user_data
|
generator.generic_user_data
|
||||||
expect(user.aspects).not_to be_empty
|
expect(user.aspects).not_to be_empty
|
||||||
expect(Post.subscribed_by(user)).not_to be_empty
|
expect(Post.subscribed_by(user)).not_to be_empty
|
||||||
|
expect(Contact.where(user: user).mutual).not_to be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,8 +106,6 @@ RSpec.configure do |config|
|
||||||
I18n.locale = :en
|
I18n.locale = :en
|
||||||
stub_request(:post, "https://pubsubhubbub.appspot.com/")
|
stub_request(:post, "https://pubsubhubbub.appspot.com/")
|
||||||
$process_queue = false
|
$process_queue = false
|
||||||
allow(Workers::SendPublic).to receive(:perform_async)
|
|
||||||
allow(Workers::SendPrivate).to receive(:perform_async)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
config.expect_with :rspec do |expect_config|
|
config.expect_with :rspec do |expect_config|
|
||||||
|
|
|
||||||
43
spec/support/account_matchers.rb
Normal file
43
spec/support/account_matchers.rb
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
RSpec::Matchers.define :be_a_discovered_person do
|
||||||
|
match do |person|
|
||||||
|
!Person.by_account_identifier(person.diaspora_handle).nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec::Matchers.define :be_a_closed_account do
|
||||||
|
match(&:closed_account?)
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec::Matchers.define :be_a_locked_account do
|
||||||
|
match(&:access_locked?)
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec::Matchers.define :be_a_clear_profile do
|
||||||
|
match do |profile|
|
||||||
|
attributes = %i[
|
||||||
|
diaspora_handle first_name last_name image_url image_url_small image_url_medium birthday gender bio
|
||||||
|
location nsfw public_details
|
||||||
|
].map {|attribute| profile[attribute] }
|
||||||
|
|
||||||
|
profile.taggings.empty? && !profile.searchable && attributes.reject(&:nil?).empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec::Matchers.define :be_a_clear_account do
|
||||||
|
match do |user|
|
||||||
|
attributes = %i[
|
||||||
|
language reset_password_token remember_created_at sign_in_count current_sign_in_at last_sign_in_at
|
||||||
|
current_sign_in_ip last_sign_in_ip invited_by_id authentication_token unconfirmed_email confirm_email_token
|
||||||
|
auto_follow_back auto_follow_back_aspect_id reset_password_sent_at last_seen color_theme
|
||||||
|
].map {|attribute| user[attribute] }
|
||||||
|
|
||||||
|
user.disable_mail &&
|
||||||
|
user.strip_exif &&
|
||||||
|
!user.getting_started &&
|
||||||
|
!user.show_community_spotlight_in_stream &&
|
||||||
|
!user.post_default_public &&
|
||||||
|
user.email == "deletedaccount_#{user.id}@example.org" &&
|
||||||
|
user.hidden_shareables.empty? &&
|
||||||
|
attributes.reject(&:nil?).empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -38,6 +38,7 @@ class DataGenerator
|
||||||
private_post_as_receipient
|
private_post_as_receipient
|
||||||
tag_following
|
tag_following
|
||||||
generic_person_data
|
generic_person_data
|
||||||
|
remote_mutual_friend
|
||||||
end
|
end
|
||||||
|
|
||||||
def generic_person_data
|
def generic_person_data
|
||||||
|
|
@ -98,6 +99,10 @@ class DataGenerator
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def remote_mutual_friend
|
||||||
|
FactoryGirl.create(:contact, user: user, sharing: true, receiving: true)
|
||||||
|
end
|
||||||
|
|
||||||
def first_aspect
|
def first_aspect
|
||||||
user.aspects.first || FactoryGirl.create(:aspect, user: user)
|
user.aspects.first || FactoryGirl.create(:aspect, user: user)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ class User
|
||||||
alias_method :share_with_original, :share_with
|
alias_method :share_with_original, :share_with
|
||||||
|
|
||||||
def share_with(*args)
|
def share_with(*args)
|
||||||
|
disable_send_workers
|
||||||
|
|
||||||
inlined_jobs do
|
inlined_jobs do
|
||||||
share_with_original(*args)
|
share_with_original(*args)
|
||||||
end
|
end
|
||||||
|
|
@ -13,6 +15,8 @@ class User
|
||||||
end
|
end
|
||||||
|
|
||||||
def post(class_name, opts = {})
|
def post(class_name, opts = {})
|
||||||
|
disable_send_workers
|
||||||
|
|
||||||
inlined_jobs do
|
inlined_jobs do
|
||||||
aspects = self.aspects_from_ids(opts[:to])
|
aspects = self.aspects_from_ids(opts[:to])
|
||||||
|
|
||||||
|
|
@ -22,11 +26,9 @@ class User
|
||||||
self.aspects.reload
|
self.aspects.reload
|
||||||
|
|
||||||
dispatch_opts = {
|
dispatch_opts = {
|
||||||
url: Rails.application.routes.url_helpers.post_url(
|
url: Rails.application.routes.url_helpers.post_url(p, host: AppConfig.pod_uri.to_s),
|
||||||
p,
|
to: opts[:to]
|
||||||
host: AppConfig.pod_uri.to_s
|
}
|
||||||
),
|
|
||||||
to: opts[:to]}
|
|
||||||
dispatch_post(p, dispatch_opts)
|
dispatch_post(p, dispatch_opts)
|
||||||
end
|
end
|
||||||
unless opts[:created_at]
|
unless opts[:created_at]
|
||||||
|
|
@ -40,4 +42,11 @@ class User
|
||||||
def build_comment(options={})
|
def build_comment(options={})
|
||||||
Comment::Generator.new(self, options.delete(:post), options.delete(:text)).build(options)
|
Comment::Generator.new(self, options.delete(:post), options.delete(:text)).build(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def disable_send_workers
|
||||||
|
RSpec.current_example&.example_group_instance&.instance_eval do
|
||||||
|
allow(Workers::SendPrivate).to receive(:perform_async)
|
||||||
|
allow(Workers::SendPublic).to receive(:perform_async)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue