From 9ee9dbe9695a0fb74b17511b620e7f382650a9da Mon Sep 17 00:00:00 2001 From: cmrd Senya Date: Sat, 19 Aug 2017 16:21:16 +0300 Subject: [PATCH 1/5] Fix local migration run without old private key It was possible to run migration locally without providing old private key. This way migration was performed but not dispatched, which obviously leads to desynchronization of the federation state so let's validate sender before performing any actual actions. closes #7558 --- Changelog.md | 1 + app/models/account_migration.rb | 5 +++++ spec/models/account_migration_spec.rb | 10 +++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 3e034ed6d..157bee633 100644 --- a/Changelog.md +++ b/Changelog.md @@ -21,6 +21,7 @@ * Fix recipient prefill on contacts and profile page [#7599](https://github.com/diaspora/diaspora/pull/7599) * Display likes and reshares without login [#7583](https://github.com/diaspora/diaspora/pull/7583) * Fix invalid data in the database for user data export [#7614](https://github.com/diaspora/diaspora/pull/7614) +* Fix local migration run without old private key [#7558](https://github.com/diaspora/diaspora/pull/7558) ## Features * Ask for confirmation when leaving a submittable comment field [#7530](https://github.com/diaspora/diaspora/pull/7530) diff --git a/app/models/account_migration.rb b/app/models/account_migration.rb index 266ab521e..ced82ace7 100644 --- a/app/models/account_migration.rb +++ b/app/models/account_migration.rb @@ -28,6 +28,7 @@ class AccountMigration < ApplicationRecord # executes a migration plan according to this AccountMigration object def perform! raise "already performed" if performed? + validate_sender if locally_initiated? ActiveRecord::Base.transaction do account_deleter.tombstone_person_and_profile @@ -115,6 +116,10 @@ class AccountMigration < ApplicationRecord EphemeralUser.new(old_person.diaspora_handle, old_private_key) end + def validate_sender + sender # sender method raises exception when sender can't be instantiated + end + def update_all_references update_person_references update_user_references if user_changed_id_locally? diff --git a/spec/models/account_migration_spec.rb b/spec/models/account_migration_spec.rb index 02b79dde5..2121a5a7a 100644 --- a/spec/models/account_migration_spec.rb +++ b/spec/models/account_migration_spec.rb @@ -127,7 +127,7 @@ describe AccountMigration, type: :model do include_context "with local new user" it "dispatches account migration message" do - expect(account_migration).to receive(:sender).and_return(old_user) + expect(account_migration).to receive(:sender).twice.and_return(old_user) dispatcher = double expect(dispatcher).to receive(:dispatch) expect(Diaspora::Federation::Dispatcher).to receive(:build) @@ -135,6 +135,14 @@ describe AccountMigration, type: :model do .and_return(dispatcher) account_migration.perform! end + + it "doesn't run migration if old key is not provided" do + expect(embedded_account_deleter).not_to receive(:tombstone_person_and_profile) + + expect { + account_migration.perform! + }.to raise_error "can't build sender without old private key defined" + end end context "with local old and new users" do From 265a7ee2530999f17cdec5c0f09934b776dad5f3 Mon Sep 17 00:00:00 2001 From: cmrd Senya Date: Sun, 24 Sep 2017 13:00:40 +0300 Subject: [PATCH 2/5] Don't force fixtures rebuild on each rspec run Main purpose of fixtures for us is to speed up test runs. Rebuilding fixtures each run makes them pointless. They will be rebuilt automatically if some of "files_to_check" are changed, so we're protected from the case when fixtures are outdated and don't load properly. --- spec/spec_helper.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3be837537..8cb7fa13b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -82,9 +82,6 @@ def client_assertion_with_nonexistent_client_id_path "client_assertion_with_nonexistent_client_id.txt") end -# Force fixture rebuild -FileUtils.rm_f(Rails.root.join("tmp", "fixture_builder.yml")) - # Requires supporting files with custom matchers and macros, etc, # in ./support/ and its subdirectories. fixture_builder_file = "#{File.dirname(__FILE__)}/support/fixture_builder.rb" @@ -93,6 +90,9 @@ support_files.each {|f| require f } require fixture_builder_file RSpec.configure do |config| + config.fixture_path = Rails.root.join("spec", "fixtures") + config.global_fixtures = :all + config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::IntegrationHelpers, type: :request config.mock_with :rspec From 9f0b74ebbb82dff16f19334c179eb9eb1abc1f3b Mon Sep 17 00:00:00 2001 From: cmrd Senya Date: Sun, 24 Sep 2017 17:06:31 +0300 Subject: [PATCH 3/5] Memory usage test for archive export It is not included into the main test suite, because it has :performance tag. One can run this test using command: $ bin/rspec --tag performance spec/integration/export/memory_usage_spec.rb This test creates additional fixtures set to speed up repeated runs. --- spec/factories.rb | 9 +++ spec/integration/export/memory_usage_spec.rb | 51 +++++++++++++ spec/support/fixture_builder.rb | 80 ++++++++++---------- 3 files changed, 101 insertions(+), 39 deletions(-) create mode 100644 spec/integration/export/memory_usage_spec.rb diff --git a/spec/factories.rb b/spec/factories.rb index cd5a4bc30..0c246e7c1 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -249,6 +249,15 @@ FactoryGirl.define do association(:post, factory: :status_message) end + factory :signed_comment, parent: :comment do + association(:parent, factory: :status_message) + + after(:build) do |comment| + order = SignatureOrder.first || FactoryGirl.create(:signature_order) + comment.signature = FactoryGirl.build(:comment_signature, comment: comment, signature_order: order) + end + end + factory(:notification, class: Notifications::AlsoCommented) do association :recipient, :factory => :user association :target, :factory => :comment diff --git a/spec/integration/export/memory_usage_spec.rb b/spec/integration/export/memory_usage_spec.rb new file mode 100644 index 000000000..9036796ee --- /dev/null +++ b/spec/integration/export/memory_usage_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +# Memory usage methods are from: https://gist.github.com/pvdb/6240788 + +# returns detailed information about the process memory usage (as recorded in the "/proc/$$/statm" proc fs file) +def Process.statm + Hash[%i[size resident shared trs lrs drs dt].zip(open("/proc/#{Process.pid}/statm").read.split)] +end + +# the real memory (resident set) size of the process (in 1_024 byte units, assuming a 4kB memory page size) +def Process.rss + Process.statm[:resident].to_i * 4 +end + +describe Diaspora::Exporter do + context "on big profiles", :performance do + before :all do + if Post.count < 1000 + # Force fixture rebuild + FileUtils.rm_f(Rails.root.join("tmp", "fixture_builder.yml")) + end + + FixtureBuilder.configure do |fbuilder| + # rebuild fixtures automatically when these files change: + fbuilder.files_to_check += Dir[ + "app/models/*.rb", "lib/**/*.rb", "spec/factories/*.rb", "spec/support/fixture_builder.rb" + ] - ["lib/diaspora/exporter.rb"] + + # now declare objects + fbuilder.factory do + create_basic_users + + 1000.times { + FactoryGirl.create(:signed_comment, post: bob.person.posts.first) + FactoryGirl.create(:status_message, author: bob.person) + FactoryGirl.create(:comment, author: bob.person) + FactoryGirl.create(:contact, user: bob) + FactoryGirl.create(:participation, author: bob.person) + } + end + end + end + + it "doesn't exceed sensible memory usage limit" do + json = Diaspora::Exporter.new(bob).execute + expect(json).not_to be_empty + expect(Process.rss).to be < 500 * 1024 + puts "Process resident set size: #{Process.rss}" + end + end +end diff --git a/spec/support/fixture_builder.rb b/spec/support/fixture_builder.rb index b88c24953..242ebb03b 100644 --- a/spec/support/fixture_builder.rb +++ b/spec/support/fixture_builder.rb @@ -1,47 +1,49 @@ # frozen_string_literal: true -FixtureBuilder.configure do |fbuilder| +def create_basic_users + # Users + alice = FactoryGirl.create(:user_with_aspect, username: "alice", strip_exif: false) + alices_aspect = alice.aspects.where(name: "generic").first + eve = FactoryGirl.create(:user_with_aspect, username: "eve") + eves_aspect = eve.aspects.where(name: "generic").first + + bob = FactoryGirl.create(:user_with_aspect, username: "bob") + bobs_aspect = bob.aspects.where(name: "generic").first + FactoryGirl.create(:aspect, name: "empty", user: bob) + + connect_users(bob, bobs_aspect, alice, alices_aspect) + connect_users(bob, bobs_aspect, eve, eves_aspect) + + # Set up friends - 2 local, 1 remote + local_luke = FactoryGirl.create(:user_with_aspect, username: "luke") + lukes_aspect = local_luke.aspects.where(name: "generic").first + + local_leia = FactoryGirl.create(:user_with_aspect, username: "leia") + leias_aspect = local_leia.aspects.where(name: "generic").first + + remote_raphael = FactoryGirl.create(:person, diaspora_handle: "raphael@remote.net") + + connect_users_with_aspects(local_luke, local_leia) + + local_leia.contacts.create(person: remote_raphael, aspects: [leias_aspect]) + local_luke.contacts.create(person: remote_raphael, aspects: [lukes_aspect]) + + # Set up a follower + peter = FactoryGirl.create(:user_with_aspect, username: "peter") + peters_aspect = peter.aspects.where(name: "generic").first + + peter.contacts.create!(person: alice.person, aspects: [peters_aspect], sharing: false, receiving: true) +end + +FixtureBuilder.configure do |fbuilder| # rebuild fixtures automatically when these files change: - fbuilder.files_to_check += Dir["app/models/*.rb", "lib/**/*.rb", "spec/factories/*.rb", "spec/support/fixture_builder.rb"] + fbuilder.files_to_check += Dir[ + "app/models/*.rb", "lib/**/*.rb", "spec/factories/*.rb", "spec/support/fixture_builder.rb" + ] - ["lib/diaspora/exporter.rb"] # now declare objects fbuilder.factory do - # Users - alice = FactoryGirl.create(:user_with_aspect, :username => "alice", :strip_exif => false) - alices_aspect = alice.aspects.where(:name => "generic").first - - eve = FactoryGirl.create(:user_with_aspect, :username => "eve") - eves_aspect = eve.aspects.where(:name => "generic").first - - bob = FactoryGirl.create(:user_with_aspect, :username => "bob") - bobs_aspect = bob.aspects.where(:name => "generic").first - FactoryGirl.create(:aspect, :name => "empty", :user => bob) - - connect_users(bob, bobs_aspect, alice, alices_aspect) - connect_users(bob, bobs_aspect, eve, eves_aspect) - - # Set up friends - 2 local, 1 remote - local_luke = FactoryGirl.create(:user_with_aspect, :username => "luke") - lukes_aspect = local_luke.aspects.where(:name => "generic").first - - local_leia = FactoryGirl.create(:user_with_aspect, :username => "leia") - leias_aspect = local_leia.aspects.where(:name => "generic").first - - remote_raphael = FactoryGirl.create(:person, :diaspora_handle => "raphael@remote.net") - - connect_users_with_aspects(local_luke, local_leia) - - local_leia.contacts.create(:person => remote_raphael, :aspects => [leias_aspect]) - local_luke.contacts.create(:person => remote_raphael, :aspects => [lukes_aspect]) - - # Set up a follower - peter = FactoryGirl.create(:user_with_aspect, :username => "peter") - peters_aspect = peter.aspects.where(:name => "generic").first - - peter.contacts.create!(:person => alice.person, - :aspects => [peters_aspect], - :sharing => false, - :receiving => true) - end + create_basic_users + end end From ea57fc5d25c60c3e11efd704a928b1b80f89e1c5 Mon Sep 17 00:00:00 2001 From: cmrd Senya Date: Mon, 25 Sep 2017 00:49:11 +0300 Subject: [PATCH 4/5] Fix mistyped remote person in dispatcher spec --- spec/lib/diaspora/federation/dispatcher/private_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/lib/diaspora/federation/dispatcher/private_spec.rb b/spec/lib/diaspora/federation/dispatcher/private_spec.rb index 4a1e8a90d..a5453023b 100644 --- a/spec/lib/diaspora/federation/dispatcher/private_spec.rb +++ b/spec/lib/diaspora/federation/dispatcher/private_spec.rb @@ -83,7 +83,7 @@ describe Diaspora::Federation::Dispatcher::Private do expect(magic_env).to receive(:envelop).with(encryption_key).and_return(magic_env_xml) expect(DiasporaFederation::Salmon::EncryptedMagicEnvelope).to receive(:encrypt) do |magic_env, public_key| expect(magic_env).to eq(magic_env_xml) - expect(public_key.to_s).to eq(remote_raphael.public_key.to_s) + expect(public_key.to_s).to eq(remote_person.public_key.to_s) json end @@ -109,7 +109,7 @@ describe Diaspora::Federation::Dispatcher::Private do expect(magic_env).to receive(:envelop).with(encryption_key).and_return(magic_env_xml) expect(DiasporaFederation::Salmon::EncryptedMagicEnvelope).to receive(:encrypt) do |magic_env, public_key| expect(magic_env).to eq(magic_env_xml) - expect(public_key.to_s).to eq(remote_raphael.public_key.to_s) + expect(public_key.to_s).to eq(remote_person.public_key.to_s) json end From c6ed850a8532049bb6a65233795334be1c9952f4 Mon Sep 17 00:00:00 2001 From: cmrd Senya Date: Mon, 25 Sep 2017 19:03:25 +0300 Subject: [PATCH 5/5] Memory usage optimization for archive export - Removed posts and non contacts from other's data - Collections are exported in batches to lower memory footprint - In base exporters create User object instead of keeping instance because it caches all associations closes #7627 --- Changelog.md | 1 + .../export/others_data_serializer.rb | 29 +++-- app/serializers/export/own_post_serializer.rb | 2 +- .../export/person_metadata_serializer.rb | 19 ---- app/serializers/export/user_serializer.rb | 28 ++++- .../federation_entity_serializer.rb | 2 +- app/serializers/flat_map_array_serializer.rb | 11 ++ lib/diaspora/exporter.rb | 4 +- lib/diaspora/exporter/non_contact_authors.rb | 38 ------- lib/diaspora/exporter/posts_with_activity.rb | 61 ----------- spec/integration/exporter_spec.rb | 102 ++---------------- .../exporter/non_contact_authors_spec.rb | 27 ----- .../exporter/posts_with_activity_spec.rb | 26 ----- spec/lib/diaspora/exporter_spec.rb | 2 +- .../export/others_data_serializer_spec.rb | 26 +---- .../export/person_metadata_serializer_spec.rb | 14 --- .../export/user_serializer_spec.rb | 2 +- spec/support/serializer_matchers.rb | 8 +- 18 files changed, 74 insertions(+), 328 deletions(-) delete mode 100644 app/serializers/export/person_metadata_serializer.rb create mode 100644 app/serializers/flat_map_array_serializer.rb delete mode 100644 lib/diaspora/exporter/non_contact_authors.rb delete mode 100644 lib/diaspora/exporter/posts_with_activity.rb delete mode 100644 spec/lib/diaspora/exporter/non_contact_authors_spec.rb delete mode 100644 spec/lib/diaspora/exporter/posts_with_activity_spec.rb delete mode 100644 spec/serializers/export/person_metadata_serializer_spec.rb diff --git a/Changelog.md b/Changelog.md index 157bee633..0757be3aa 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ * Use Bootstrap 3 progress-bar for polls [#7600](https://github.com/diaspora/diaspora/pull/7600) * Enable frozen string literals [#7595](https://github.com/diaspora/diaspora/pull/7595) * Remove `rails_admin_histories` table [#7597](https://github.com/diaspora/diaspora/pull/7597) +* Optimize memory usage on profile export [#7627](https://github.com/diaspora/diaspora/pull/7627) ## Bug fixes * Fix displaying polls with long answers [#7579](https://github.com/diaspora/diaspora/pull/7579) diff --git a/app/serializers/export/others_data_serializer.rb b/app/serializers/export/others_data_serializer.rb index fe53579e1..eff06a806 100644 --- a/app/serializers/export/others_data_serializer.rb +++ b/app/serializers/export/others_data_serializer.rb @@ -4,33 +4,32 @@ module Export class OthersDataSerializer < ActiveModel::Serializer # Relayables of other people in the archive: comments, likes, participations, poll participations where author is # the archive owner - has_many :relayables, each_serializer: FederationEntitySerializer + has_many :relayables, serializer: FlatMapArraySerializer, each_serializer: FederationEntitySerializer - # Parent posts of user's own relayables. We have to save metadata to use - # it in case when posts temporary unavailable on the target pod. - has_many :posts, each_serializer: FederationEntitySerializer - - # Authors of posts where we participated and authors are not in contacts - has_many :non_contact_authors, each_serializer: PersonMetadataSerializer + def initialize(user_id) + @user_id = user_id + super(object) + end private + def object + User.find(@user_id) + end + def relayables %i[comments likes poll_participations].map {|relayable| - others_relayables.send(relayable) - }.sum + others_relayables.send(relayable).find_each(batch_size: 20) + } end def others_relayables @others_relayables ||= Diaspora::Exporter::OthersRelayables.new(object.person_id) end - def posts - @posts ||= Diaspora::Exporter::PostsWithActivity.new(object).query - end - - def non_contact_authors - Diaspora::Exporter::NonContactAuthors.new(posts, object).query + # Avoid calling pointless #embedded_in_root_associations method + def serializable_data + {} end end end diff --git a/app/serializers/export/own_post_serializer.rb b/app/serializers/export/own_post_serializer.rb index bea797aaa..2813253e5 100644 --- a/app/serializers/export/own_post_serializer.rb +++ b/app/serializers/export/own_post_serializer.rb @@ -29,7 +29,7 @@ module Export end def excluded_subscription_key - entity.public ? :subscribed_users_ids : :subscribed_pods_uris + object.public? ? :subscribed_users_ids : :subscribed_pods_uris end end end diff --git a/app/serializers/export/person_metadata_serializer.rb b/app/serializers/export/person_metadata_serializer.rb deleted file mode 100644 index b309a7d12..000000000 --- a/app/serializers/export/person_metadata_serializer.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module Export - class PersonMetadataSerializer < ActiveModel::Serializer - attributes :guid, - :account_id, - :public_key - - private - - def account_id - object.diaspora_handle - end - - def public_key - object.serialized_public_key - end - end -end diff --git a/app/serializers/export/user_serializer.rb b/app/serializers/export/user_serializer.rb index c571bdf47..f1c4db5a3 100644 --- a/app/serializers/export/user_serializer.rb +++ b/app/serializers/export/user_serializer.rb @@ -18,12 +18,31 @@ module Export has_many :followed_tags has_many :post_subscriptions - has_many :relayables, each_serializer: Export::OwnRelayablesSerializer + has_many :relayables, serializer: FlatMapArraySerializer, each_serializer: Export::OwnRelayablesSerializer + + def initialize(user_id, options={}) + @user_id = user_id + super(object, options) + end private + def object + User.find(@user_id) + end + + def posts + object.posts.find_each(batch_size: 20) + end + + def contacts + object.contacts.find_each(batch_size: 100) + end + def relayables - [*comments, *likes, *poll_participations] + [comments, likes, poll_participations].map {|relayable| + relayable.find_each(batch_size: 20) + } end %i[comments likes poll_participations].each {|collection| @@ -47,5 +66,10 @@ module Export def post_subscriptions Post.subscribed_by(object).pluck(:guid) end + + # Avoid calling pointless #embedded_in_root_associations method + def serializable_data + {} + end end end diff --git a/app/serializers/federation_entity_serializer.rb b/app/serializers/federation_entity_serializer.rb index 5bc620171..22fa74afc 100644 --- a/app/serializers/federation_entity_serializer.rb +++ b/app/serializers/federation_entity_serializer.rb @@ -13,6 +13,6 @@ class FederationEntitySerializer < ActiveModel::Serializer end def entity - @entity ||= Diaspora::Federation::Entities.build(object) + Diaspora::Federation::Entities.build(object) end end diff --git a/app/serializers/flat_map_array_serializer.rb b/app/serializers/flat_map_array_serializer.rb new file mode 100644 index 000000000..18f45cd8f --- /dev/null +++ b/app/serializers/flat_map_array_serializer.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class FlatMapArraySerializer < ActiveModel::ArraySerializer + def serializable_object(options={}) + @object.flat_map do |subarray| + subarray.map do |item| + serializer_for(item).serializable_object_with_notification(options) + end + end + end +end diff --git a/lib/diaspora/exporter.rb b/lib/diaspora/exporter.rb index cbdecc10d..837261b76 100644 --- a/lib/diaspora/exporter.rb +++ b/lib/diaspora/exporter.rb @@ -20,8 +20,8 @@ module Diaspora def full_archive {version: SERIALIZED_VERSION} - .merge(Export::UserSerializer.new(@user).as_json) - .merge(Export::OthersDataSerializer.new(@user).as_json) + .merge(Export::UserSerializer.new(@user.id).as_json) + .merge(Export::OthersDataSerializer.new(@user.id).as_json) end end end diff --git a/lib/diaspora/exporter/non_contact_authors.rb b/lib/diaspora/exporter/non_contact_authors.rb deleted file mode 100644 index 1c89f8d8d..000000000 --- a/lib/diaspora/exporter/non_contact_authors.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module Diaspora - class Exporter - # This class is capable of quering a list of people from authors of given posts that are non-contacts of a given - # user. - class NonContactAuthors - # @param posts [Post::ActiveRecord_Relation] posts that we fetch authors from to make authors list - # @param user [User] a user we fetch a contact list from - def initialize(posts, user) - @posts = posts - @user = user - end - - # Create a request of non-contact authors of the posts for the user - # @return [Post::ActiveRecord_Relation] - def query - Person.where(id: non_contact_authors_ids) - end - - private - - def non_contact_authors_ids - posts_authors_ids - contacts_ids - end - - def posts_authors_ids - posts.pluck(:author_id).uniq - end - - def contacts_ids - user.contacts.pluck(:person_id) - end - - attr_reader :posts, :user - end - end -end diff --git a/lib/diaspora/exporter/posts_with_activity.rb b/lib/diaspora/exporter/posts_with_activity.rb deleted file mode 100644 index 9ebafa759..000000000 --- a/lib/diaspora/exporter/posts_with_activity.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -module Diaspora - class Exporter - # This class allows to query posts where a person made any activity (submitted comments, - # likes, participations or poll participations). - class PostsWithActivity - # @param user [User] user who the activity belongs to (the one who liked, commented posts, etc) - def initialize(user) - @user = user - end - - # Create a request of posts with activity - # @return [Post::ActiveRecord_Relation] - def query - Post.from("(#{sql_union_all_activities}) AS posts") - end - - private - - attr_reader :user - - def person - user.person - end - - def sql_union_all_activities - all_activities.map(&:to_sql).join(" UNION ") - end - - def all_activities - [comments_activity, likes_activity, subscriptions, polls_activity, reshares_activity] - end - - def likes_activity - other_people_posts.liked_by(person) - end - - def comments_activity - other_people_posts.commented_by(person) - end - - def subscriptions - other_people_posts.subscribed_by(user) - end - - def reshares_activity - other_people_posts.reshared_by(person) - end - - def polls_activity - StatusMessage.where.not(author_id: person.id).joins(:poll_participations) - .where(poll_participations: {author_id: person.id}) - end - - def other_people_posts - Post.where.not(author_id: person.id) - end - end - end -end diff --git a/spec/integration/exporter_spec.rb b/spec/integration/exporter_spec.rb index 930a8a57e..00110dafa 100644 --- a/spec/integration/exporter_spec.rb +++ b/spec/integration/exporter_spec.rb @@ -201,7 +201,7 @@ describe Diaspora::Exporter do expect(json).to include_json(user: {posts: [serialized]}) end - it "contains a reshare and its root" do + it "contains a reshare" do reshare = FactoryGirl.create(:reshare, author: user.person) serialized_reshare = { "subscribed_pods_uris": [reshare.root.author.pod.url_to(""), AppConfig.pod_uri.to_s], @@ -216,21 +216,8 @@ describe Diaspora::Exporter do } } - status_message = reshare.root - serialized_parent = { - "entity_type": "status_message", - "entity_data": { - "author": status_message.diaspora_handle, - "guid": status_message.guid, - "created_at": status_message.created_at.iso8601, - "text": status_message.text, - "public": true - } - } - expect(json).to include_json( - user: {posts: [serialized_reshare]}, - others_data: {posts: [serialized_parent]} + user: {posts: [serialized_reshare]} ) end @@ -244,7 +231,7 @@ describe Diaspora::Exporter do expect(json).to include_json(user: {post_subscriptions: [subscription.target.guid]}) end - it "contains a comment and the commented post" do + it "contains a comment" do comment = FactoryGirl.create(:comment, author: user.person) serialized_comment = { "entity_type": "comment", @@ -258,25 +245,12 @@ describe Diaspora::Exporter do "property_order": %w[author guid parent_guid text created_at] } - status_message = comment.parent - serialized_post = { - "entity_type": "status_message", - "entity_data": { - "author": status_message.diaspora_handle, - "guid": status_message.guid, - "created_at": status_message.created_at.iso8601, - "text": status_message.text, - "public": false - } - } - expect(json).to include_json( - user: {relayables: [serialized_comment]}, - others_data: {posts: [serialized_post]} + user: {relayables: [serialized_comment]} ) end - it "contains a like and the liked post" do + it "contains a like" do like = FactoryGirl.create(:like, author: user.person) serialized_like = { "entity_type": "like", @@ -290,25 +264,12 @@ describe Diaspora::Exporter do "property_order": %w[author guid parent_guid parent_type positive] } - status_message = like.target - serialized_post = { - "entity_type": "status_message", - "entity_data": { - "author": status_message.diaspora_handle, - "guid": status_message.guid, - "created_at": status_message.created_at.iso8601, - "text": status_message.text, - "public": false - } - } - expect(json).to include_json( - user: {relayables: [serialized_like]}, - others_data: {posts: [serialized_post]} + user: {relayables: [serialized_like]} ) end - it "contains a poll participation and post with this poll" do + it "contains a poll participation" do poll_participation = FactoryGirl.create(:poll_participation, author: user.person) serialized_participation = { "entity_type": "poll_participation", @@ -321,38 +282,8 @@ describe Diaspora::Exporter do "property_order": %w[author guid parent_guid poll_answer_guid] } - poll = poll_participation.poll - status_message = poll_participation.status_message - serialized_post = { - "entity_type": "status_message", - "entity_data": { - "author": status_message.diaspora_handle, - "guid": status_message.guid, - "created_at": status_message.created_at.iso8601, - "text": status_message.text, - "poll": { - "entity_type": "poll", - "entity_data": { - "guid": poll.guid, - "question": poll.question, - "poll_answers": poll.poll_answers.map {|answer| - { - "entity_type": "poll_answer", - "entity_data": { - "guid": answer.guid, - "answer": answer.answer - } - } - } - } - }, - "public": false - } - } - expect(json).to include_json( - user: {relayables: [serialized_participation]}, - others_data: {posts: [serialized_post]} + user: {relayables: [serialized_participation]} ) end @@ -409,23 +340,6 @@ describe Diaspora::Exporter do expect(json).to include_json(others_data: {relayables: [serialized]}) end - it "contains metadata of a non-contact author of a post where we commented" do - comment = FactoryGirl.create(:comment, author: user.person) - - author = comment.parent.author - expect(json).to include_json( - others_data: { - non_contact_authors: [ - { - "guid": author.guid, - "account_id": author.diaspora_handle, - "public_key": author.serialized_public_key - } - ] - } - ) - end - def transform_value(value) return value.iso8601 if value.is_a? Date value diff --git a/spec/lib/diaspora/exporter/non_contact_authors_spec.rb b/spec/lib/diaspora/exporter/non_contact_authors_spec.rb deleted file mode 100644 index 766fce62a..000000000 --- a/spec/lib/diaspora/exporter/non_contact_authors_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -describe Diaspora::Exporter::NonContactAuthors do - describe "#query" do - let(:user) { FactoryGirl.create(:user_with_aspect) } - let(:post) { FactoryGirl.create(:status_message) } - let(:instance) { - Diaspora::Exporter::NonContactAuthors.new(Post.where(id: post.id), user) - } - - context "without contact relationship" do - it "includes post author to the result set" do - expect(instance.query).to eq([post.author]) - end - end - - context "with contact relationship" do - before do - user.share_with(post.author, user.aspects.first) - end - - it "doesn't include post author to the result set" do - expect(instance.query).to be_empty - end - end - end -end diff --git a/spec/lib/diaspora/exporter/posts_with_activity_spec.rb b/spec/lib/diaspora/exporter/posts_with_activity_spec.rb deleted file mode 100644 index f3c2e83ce..000000000 --- a/spec/lib/diaspora/exporter/posts_with_activity_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -describe Diaspora::Exporter::PostsWithActivity do - let(:user) { FactoryGirl.create(:user) } - let(:instance) { Diaspora::Exporter::PostsWithActivity.new(user) } - - describe "#query" do - let(:activity) { - [ - user.person.likes.first.target, - user.person.comments.first.parent, - user.person.poll_participations.first.parent.status_message, - user.person.participations.first.target, - user.person.posts.reshares.first.root - ] - } - - before do - DataGenerator.create(user, %i[activity participation]) - end - - it "returns all posts with person's activity" do - expect(instance.query).to match_array(activity) - end - end -end diff --git a/spec/lib/diaspora/exporter_spec.rb b/spec/lib/diaspora/exporter_spec.rb index 63c4c9c37..6a9b1056f 100644 --- a/spec/lib/diaspora/exporter_spec.rb +++ b/spec/lib/diaspora/exporter_spec.rb @@ -6,7 +6,7 @@ describe Diaspora::Exporter do expect_any_instance_of(Export::UserSerializer).to receive(:as_json).and_return(user: "user_data") expect_any_instance_of(Export::OthersDataSerializer).to receive(:as_json).and_return(others_date: "others_data") - json = Diaspora::Exporter.new(nil).execute + json = Diaspora::Exporter.new(FactoryGirl.create(:user)).execute expect(json).to include_json( version: "2.0", user: "user_data", diff --git a/spec/serializers/export/others_data_serializer_spec.rb b/spec/serializers/export/others_data_serializer_spec.rb index 3c06563aa..2c40adfb8 100644 --- a/spec/serializers/export/others_data_serializer_spec.rb +++ b/spec/serializers/export/others_data_serializer_spec.rb @@ -2,15 +2,7 @@ describe Export::OthersDataSerializer do let(:user) { FactoryGirl.create(:user) } - let(:serializer) { Export::OthersDataSerializer.new(user) } - let(:others_posts) { - [ - *user.person.likes.map(&:target), - *user.person.comments.map(&:parent), - *user.person.posts.reshares.map(&:root), - *user.person.poll_participations.map(&:status_message) - ] - } + let(:serializer) { Export::OthersDataSerializer.new(user.id) } it "uses FederationEntitySerializer for array serializing relayables" do sm = DataGenerator.new(user).status_message_with_activity @@ -25,21 +17,5 @@ describe Export::OthersDataSerializer do before do DataGenerator.new(user).activity end - - it "uses FederationEntitySerializer for array serializing posts" do - expect(Export::OthersDataSerializer).to serialize_association(:posts) - .with_each_serializer(FederationEntitySerializer) - .with_objects(others_posts) - serializer.associations - end - - it "uses PersonMetadataSerializer for array serializing non_contact_authors" do - non_contact_authors = others_posts.map(&:author) - - expect(Export::OthersDataSerializer).to serialize_association(:non_contact_authors) - .with_each_serializer(Export::PersonMetadataSerializer) - .with_objects(non_contact_authors) - serializer.associations - end end end diff --git a/spec/serializers/export/person_metadata_serializer_spec.rb b/spec/serializers/export/person_metadata_serializer_spec.rb deleted file mode 100644 index 8a278da78..000000000 --- a/spec/serializers/export/person_metadata_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -describe Export::PersonMetadataSerializer do - let(:person) { FactoryGirl.create(:person) } - let(:serializer) { Export::PersonMetadataSerializer.new(person) } - - it "has person metadata attributes" do - expect(serializer.attributes).to eq( - guid: person.guid, - account_id: person.diaspora_handle, - public_key: person.serialized_public_key - ) - end -end diff --git a/spec/serializers/export/user_serializer_spec.rb b/spec/serializers/export/user_serializer_spec.rb index 0340b696c..a316ef3d9 100644 --- a/spec/serializers/export/user_serializer_spec.rb +++ b/spec/serializers/export/user_serializer_spec.rb @@ -2,7 +2,7 @@ describe Export::UserSerializer do let(:user) { FactoryGirl.create(:user) } - let(:serializer) { Export::UserSerializer.new(user, root: false) } + let(:serializer) { Export::UserSerializer.new(user.id, root: false) } it "has basic user's attributes" do expect(serializer.attributes).to eq( diff --git a/spec/support/serializer_matchers.rb b/spec/support/serializer_matchers.rb index 0a1eff813..7da7b6927 100644 --- a/spec/support/serializer_matchers.rb +++ b/spec/support/serializer_matchers.rb @@ -15,6 +15,7 @@ RSpec::Matchers.define :serialize_association do |association_name| match do |root_serializer_class| association = fetch_association(root_serializer_class, association_name) + @serializer_from_options = association.serializer_from_options execute_receive_matcher_with(association) end @@ -50,7 +51,11 @@ RSpec::Matchers.define :serialize_association do |association_name| def with_object_expectation(object) if association_object.is_a?(Array) - expect(object).to match_array(association_object) + if serializer_class == FlatMapArraySerializer + expect(object.flat_map(&:to_a)).to match_array(association_object) + else + expect(object).to match_array(association_object) + end elsif !association_object.nil? expect(object).to eq(association_object) end @@ -66,6 +71,7 @@ RSpec::Matchers.define :serialize_association do |association_name| def pick_serializer_class return association_serializer_class unless association_serializer_class.nil? + return @serializer_from_options unless @serializer_from_options.nil? return ActiveModel::ArraySerializer unless each_serializer_class.nil? end end