Merge pull request #6726 from cmrd-senya/archive-schema
Update the user data export archive format.
This commit is contained in:
commit
9d2d20e854
56 changed files with 1982 additions and 341 deletions
|
|
@ -25,6 +25,7 @@ If so, please delete it since it will prevent the federation from working proper
|
|||
* Change sender for mails [#7495](https://github.com/diaspora/diaspora/pull/7495)
|
||||
* Move back to top to the right to avoid misclicks [#7516](https://github.com/diaspora/diaspora/pull/7516)
|
||||
* Include count in mobile post action link [#7520](https://github.com/diaspora/diaspora/pull/7520)
|
||||
* Update the user data export archive format [#6726](https://github.com/diaspora/diaspora/pull/6726)
|
||||
|
||||
## Bug fixes
|
||||
|
||||
|
|
|
|||
7
Gemfile
7
Gemfile
|
|
@ -13,7 +13,8 @@ gem "unicorn-worker-killer", "0.4.4"
|
|||
|
||||
# Federation
|
||||
|
||||
gem "diaspora_federation-rails", "0.2.0"
|
||||
gem "diaspora_federation-json_schema", "0.2.1"
|
||||
gem "diaspora_federation-rails", "0.2.1"
|
||||
|
||||
# API and JSON
|
||||
|
||||
|
|
@ -277,6 +278,8 @@ group :test do
|
|||
|
||||
gem "fixture_builder", "0.5.0"
|
||||
gem "fuubar", "2.2.0"
|
||||
gem "json-schema-rspec", "0.0.4"
|
||||
gem "rspec-json_expectations", "~> 2.1"
|
||||
gem "test_after_commit", "1.1.0"
|
||||
|
||||
# Cucumber (integration tests)
|
||||
|
|
@ -294,7 +297,7 @@ group :test do
|
|||
gem "timecop", "0.9.1"
|
||||
gem "webmock", "3.0.1", require: false
|
||||
|
||||
gem "diaspora_federation-test", "0.2.0"
|
||||
gem "diaspora_federation-test", "0.2.1"
|
||||
|
||||
# Coverage
|
||||
gem 'coveralls', require: false
|
||||
|
|
|
|||
26
Gemfile.lock
26
Gemfile.lock
|
|
@ -162,17 +162,18 @@ GEM
|
|||
devise
|
||||
rails (>= 3.0.4)
|
||||
diaspora-prosody-config (0.0.7)
|
||||
diaspora_federation (0.2.0)
|
||||
diaspora_federation (0.2.1)
|
||||
faraday (>= 0.9.0, < 0.13.0)
|
||||
faraday_middleware (>= 0.10.0, < 0.12.0)
|
||||
faraday_middleware (>= 0.10.0, < 0.13.0)
|
||||
nokogiri (~> 1.6, >= 1.6.8)
|
||||
typhoeus (~> 1.0)
|
||||
valid (~> 1.0)
|
||||
diaspora_federation-rails (0.2.0)
|
||||
diaspora_federation-json_schema (0.2.1)
|
||||
diaspora_federation-rails (0.2.1)
|
||||
actionpack (>= 4.2, < 6)
|
||||
diaspora_federation (= 0.2.0)
|
||||
diaspora_federation-test (0.2.0)
|
||||
diaspora_federation (= 0.2.0)
|
||||
diaspora_federation (= 0.2.1)
|
||||
diaspora_federation-test (0.2.1)
|
||||
diaspora_federation (= 0.2.1)
|
||||
fabrication (~> 2.16.0)
|
||||
uuid (~> 2.3.8)
|
||||
diff-lcs (1.3)
|
||||
|
|
@ -199,7 +200,7 @@ GEM
|
|||
sigar (~> 0.7.3)
|
||||
state_machines
|
||||
thor
|
||||
fabrication (2.16.1)
|
||||
fabrication (2.16.2)
|
||||
factory_girl (4.8.0)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_girl_rails (4.8.0)
|
||||
|
|
@ -334,6 +335,9 @@ GEM
|
|||
url_safe_base64
|
||||
json-schema (2.8.0)
|
||||
addressable (>= 2.4)
|
||||
json-schema-rspec (0.0.4)
|
||||
json-schema (~> 2.5)
|
||||
rspec
|
||||
jsonpath (0.8.5)
|
||||
multi_json
|
||||
jwt (1.5.6)
|
||||
|
|
@ -582,6 +586,7 @@ GEM
|
|||
rspec-expectations (3.6.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-json_expectations (2.1.0)
|
||||
rspec-mocks (3.6.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
|
|
@ -772,8 +777,9 @@ DEPENDENCIES
|
|||
devise (= 4.3.0)
|
||||
devise_lastseenable (= 0.0.6)
|
||||
diaspora-prosody-config (= 0.0.7)
|
||||
diaspora_federation-rails (= 0.2.0)
|
||||
diaspora_federation-test (= 0.2.0)
|
||||
diaspora_federation-json_schema (= 0.2.1)
|
||||
diaspora_federation-rails (= 0.2.1)
|
||||
diaspora_federation-test (= 0.2.1)
|
||||
entypo-rails (= 3.0.0)
|
||||
eye (= 0.9.2)
|
||||
factory_girl_rails (= 4.8.0)
|
||||
|
|
@ -800,6 +806,7 @@ DEPENDENCIES
|
|||
js_image_paths (= 0.1.0)
|
||||
json (= 2.1.0)
|
||||
json-schema (= 2.8.0)
|
||||
json-schema-rspec (= 0.0.4)
|
||||
leaflet-rails (= 1.1.0)
|
||||
logging-rails (= 0.6.0)
|
||||
markerb (= 1.1.0)
|
||||
|
|
@ -858,6 +865,7 @@ DEPENDENCIES
|
|||
rb-inotify (= 0.9.10)
|
||||
redcarpet (= 3.4.0)
|
||||
responders (= 2.4.0)
|
||||
rspec-json_expectations (~> 2.1)
|
||||
rspec-rails (= 3.6.0)
|
||||
rubocop (= 0.49.1)
|
||||
ruby-oembed (= 0.12.0)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@ class Person < ActiveRecord::Base
|
|||
has_many :posts, :foreign_key => :author_id, :dependent => :destroy # This person's own posts
|
||||
has_many :photos, :foreign_key => :author_id, :dependent => :destroy # This person's own photos
|
||||
has_many :comments, :foreign_key => :author_id, :dependent => :destroy # This person's own comments
|
||||
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 :poll_participations, foreign_key: :author_id, dependent: :destroy
|
||||
has_many :conversation_visibilities
|
||||
|
||||
has_many :roles
|
||||
|
|
@ -76,8 +78,8 @@ class Person < ActiveRecord::Base
|
|||
|
||||
#not defensive
|
||||
scope :in_aspects, ->(aspect_ids) {
|
||||
joins(:contacts => :aspect_memberships).
|
||||
where(:aspect_memberships => {:aspect_id => aspect_ids})
|
||||
joins(contacts: :aspect_memberships)
|
||||
.where(aspect_memberships: {aspect_id: aspect_ids}).distinct
|
||||
}
|
||||
|
||||
scope :profile_tagged_with, ->(tag_name) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ class PollParticipation < ActiveRecord::Base
|
|||
|
||||
belongs_to :poll
|
||||
belongs_to :poll_answer, counter_cache: :vote_count
|
||||
has_one :status_message, through: :poll
|
||||
|
||||
has_one :signature, class_name: "PollParticipationSignature", dependent: :delete
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,17 @@ class Post < ActiveRecord::Base
|
|||
joins(:likes).where(:likes => {:author_id => person.id})
|
||||
}
|
||||
|
||||
scope :subscribed_by, ->(user) {
|
||||
joins(:participations).where(participations: {author_id: user.person_id})
|
||||
}
|
||||
|
||||
scope :reshares, -> { where(type: "Reshare") }
|
||||
|
||||
scope :reshared_by, ->(person) {
|
||||
# we join on the same table, Rails renames "posts" to "reshares_posts" for the right table
|
||||
joins(:reshares).where(reshares_posts: {author_id: person.id})
|
||||
}
|
||||
|
||||
def post_type
|
||||
self.class.name
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class StatusMessage < Post
|
|||
|
||||
has_one :location
|
||||
has_one :poll, autosave: true
|
||||
has_many :poll_participations, through: :poll
|
||||
|
||||
attr_accessor :oembed_url
|
||||
attr_accessor :open_graph_url
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
module Export
|
||||
class CommentSerializer < ActiveModel::Serializer
|
||||
attributes :guid,
|
||||
:text,
|
||||
:post_guid
|
||||
|
||||
def post_guid
|
||||
object.post.guid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2,11 +2,33 @@ module Export
|
|||
class ContactSerializer < ActiveModel::Serializer
|
||||
attributes :sharing,
|
||||
:receiving,
|
||||
:following,
|
||||
:followed,
|
||||
:person_guid,
|
||||
:person_name,
|
||||
:person_first_name,
|
||||
:person_diaspora_handle
|
||||
:account_id,
|
||||
:public_key
|
||||
|
||||
has_many :aspects, each_serializer: Export::AspectSerializer
|
||||
has_many :contact_groups_membership
|
||||
|
||||
def following
|
||||
object.sharing
|
||||
end
|
||||
|
||||
def followed
|
||||
object.receiving
|
||||
end
|
||||
|
||||
def account_id
|
||||
object.person_diaspora_handle
|
||||
end
|
||||
|
||||
def contact_groups_membership
|
||||
object.aspects.map(&:name)
|
||||
end
|
||||
|
||||
def public_key
|
||||
object.person.serialized_public_key
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
34
app/serializers/export/others_data_serializer.rb
Normal file
34
app/serializers/export/others_data_serializer.rb
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
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
|
||||
|
||||
# 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
|
||||
|
||||
private
|
||||
|
||||
def relayables
|
||||
%i[comments likes poll_participations].map {|relayable|
|
||||
others_relayables.send(relayable)
|
||||
}.sum
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/serializers/export/own_post_serializer.rb
Normal file
33
app/serializers/export/own_post_serializer.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
module Export
|
||||
# This is a serializer for the user's own posts
|
||||
class OwnPostSerializer < FederationEntitySerializer
|
||||
# Only for public posts.
|
||||
# Includes URIs of pods which must be notified on the post updates.
|
||||
# Must always include local pod URI since we will want all the updates on the post if user migrates.
|
||||
has_many :subscribed_pods_uris
|
||||
|
||||
# Only for private posts.
|
||||
# Includes diaspora* IDs of people who must be notified on post updates.
|
||||
has_many :subscribed_users_ids
|
||||
|
||||
# Normally accepts Post as an object.
|
||||
def initialize(*)
|
||||
super
|
||||
self.except = [excluded_subscription_key]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def subscribed_pods_uris
|
||||
object.subscribed_pods_uris.push(AppConfig.pod_uri.to_s)
|
||||
end
|
||||
|
||||
def subscribed_users_ids
|
||||
object.subscribers.map(&:diaspora_handle)
|
||||
end
|
||||
|
||||
def excluded_subscription_key
|
||||
entity.public ? :subscribed_users_ids : :subscribed_pods_uris
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/serializers/export/own_relayables_serializer.rb
Normal file
13
app/serializers/export/own_relayables_serializer.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module Export
|
||||
# This is a serializer for the user's own relayables. We remove signature from the own relayables since it isn't
|
||||
# useful and takes space.
|
||||
class OwnRelayablesSerializer < FederationEntitySerializer
|
||||
private
|
||||
|
||||
def modify_serializable_object(hash)
|
||||
super.tap {|hash|
|
||||
hash[:entity_data].delete(:author_signature)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
17
app/serializers/export/person_metadata_serializer.rb
Normal file
17
app/serializers/export/person_metadata_serializer.rb
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
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
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
module Export
|
||||
class PostSerializer < ActiveModel::Serializer
|
||||
attributes :guid,
|
||||
:text,
|
||||
:public,
|
||||
:diaspora_handle,
|
||||
:type,
|
||||
:likes_count,
|
||||
:comments_count,
|
||||
:reshares_count,
|
||||
:created_at
|
||||
end
|
||||
end
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
module Export
|
||||
class ProfileSerializer < ActiveModel::Serializer
|
||||
attributes :first_name,
|
||||
:last_name,
|
||||
:gender,
|
||||
:bio,
|
||||
:birthday,
|
||||
:location,
|
||||
:image_url,
|
||||
:diaspora_handle,
|
||||
:searchable,
|
||||
:nsfw
|
||||
end
|
||||
end
|
||||
|
|
@ -1,24 +1,49 @@
|
|||
module Export
|
||||
class UserSerializer < ActiveModel::Serializer
|
||||
attributes :name,
|
||||
attributes :username,
|
||||
:email,
|
||||
:language,
|
||||
:username,
|
||||
:serialized_private_key,
|
||||
:private_key,
|
||||
:disable_mail,
|
||||
:show_community_spotlight_in_stream,
|
||||
:auto_follow_back,
|
||||
:auto_follow_back_aspect,
|
||||
:strip_exif
|
||||
has_one :profile, serializer: Export::ProfileSerializer
|
||||
has_many :aspects, each_serializer: Export::AspectSerializer
|
||||
has_one :profile, serializer: FederationEntitySerializer
|
||||
has_many :contact_groups, each_serializer: Export::AspectSerializer
|
||||
has_many :contacts, each_serializer: Export::ContactSerializer
|
||||
has_many :posts, each_serializer: Export::PostSerializer
|
||||
has_many :comments, each_serializer: Export::CommentSerializer
|
||||
has_many :posts, each_serializer: Export::OwnPostSerializer
|
||||
has_many :followed_tags
|
||||
has_many :post_subscriptions
|
||||
|
||||
def comments
|
||||
object.person.comments
|
||||
has_many :relayables, each_serializer: Export::OwnRelayablesSerializer
|
||||
|
||||
private
|
||||
|
||||
def relayables
|
||||
[*comments, *likes, *poll_participations]
|
||||
end
|
||||
|
||||
%i[comments likes poll_participations].each {|collection|
|
||||
delegate collection, to: :person
|
||||
}
|
||||
|
||||
delegate :person, to: :object
|
||||
|
||||
def contact_groups
|
||||
object.aspects
|
||||
end
|
||||
|
||||
def private_key
|
||||
object.serialized_private_key
|
||||
end
|
||||
|
||||
def followed_tags
|
||||
object.followed_tags.map(&:name)
|
||||
end
|
||||
|
||||
def post_subscriptions
|
||||
Post.subscribed_by(object).pluck(:guid)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
16
app/serializers/federation_entity_serializer.rb
Normal file
16
app/serializers/federation_entity_serializer.rb
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# This is an ActiveModel::Serializer based class which uses DiasporaFederation::Entity JSON serialization
|
||||
# features in order to serialize local DB objects. To determine a type of entity class to use the same routines
|
||||
# are used as for federation messages generation.
|
||||
class FederationEntitySerializer < ActiveModel::Serializer
|
||||
include SerializerPostProcessing
|
||||
|
||||
private
|
||||
|
||||
def modify_serializable_object(hash)
|
||||
hash.merge(entity.to_json)
|
||||
end
|
||||
|
||||
def entity
|
||||
@entity ||= Diaspora::Federation::Entities.build(object)
|
||||
end
|
||||
end
|
||||
20
app/serializers/serializer_post_processing.rb
Normal file
20
app/serializers/serializer_post_processing.rb
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# This module encapsulates knowledge about the way AMS works with the serializable object.
|
||||
# The main responsibility of this module is to allow changing resulting object just before the
|
||||
# JSON serialization happens.
|
||||
module SerializerPostProcessing
|
||||
# serializable_object output is used in AMS to produce a hash from input object that is passed to JSON serializer.
|
||||
# serializable_object of ActiveModel::Serializer is not documented as officialy available API
|
||||
# NOTE: if we ever move to AMS 0.10, this method was renamed there to serializable_hash
|
||||
def serializable_object(options={})
|
||||
modify_serializable_object(super)
|
||||
end
|
||||
|
||||
# Users of this module may override this method in order to change serializable_object after
|
||||
# the serializable hash generation and before its serialization.
|
||||
def modify_serializable_object(hash)
|
||||
hash
|
||||
end
|
||||
|
||||
# except is an array of keys that are excluded from serialized_object before JSON serialization
|
||||
attr_accessor :except
|
||||
end
|
||||
|
|
@ -53,3 +53,8 @@ Feature: Change settings
|
|||
And I go to the stream page
|
||||
And I expand the publisher
|
||||
Then I should see "Public" within ".aspect_dropdown"
|
||||
|
||||
Scenario: exporting profile data
|
||||
When I click on the first selector "#account_data a"
|
||||
Then I should see "Download my profile"
|
||||
And I should have 1 email delivery
|
||||
|
|
|
|||
|
|
@ -101,7 +101,8 @@ class AccountDeleter
|
|||
end
|
||||
|
||||
def ignored_or_special_ar_person_associations
|
||||
%i(comments contacts notification_actors notifications owner profile conversation_visibilities pod)
|
||||
%i[comments likes poll_participations contacts notification_actors notifications owner profile
|
||||
conversation_visibilities pod]
|
||||
end
|
||||
|
||||
def mark_account_deletion_complete
|
||||
|
|
|
|||
|
|
@ -6,22 +6,23 @@ module Diaspora
|
|||
|
||||
class Exporter
|
||||
|
||||
SERIALIZED_VERSION = '1.0'
|
||||
SERIALIZED_VERSION = "2.0".freeze
|
||||
|
||||
def initialize(user)
|
||||
@user = user
|
||||
end
|
||||
|
||||
def execute
|
||||
@export ||= JSON.generate serialized_user.merge(version: SERIALIZED_VERSION)
|
||||
JSON.generate full_archive
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def serialized_user
|
||||
@serialized_user ||= Export::UserSerializer.new(@user).as_json
|
||||
def full_archive
|
||||
{version: SERIALIZED_VERSION}
|
||||
.merge(Export::UserSerializer.new(@user).as_json)
|
||||
.merge(Export::OthersDataSerializer.new(@user).as_json)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
36
lib/diaspora/exporter/non_contact_authors.rb
Normal file
36
lib/diaspora/exporter/non_contact_authors.rb
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
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
|
||||
42
lib/diaspora/exporter/others_relayables.rb
Normal file
42
lib/diaspora/exporter/others_relayables.rb
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
module Diaspora
|
||||
class Exporter
|
||||
# This class implements methods that allow to query relayables (comments, likes, participations,
|
||||
# poll_participations) of other people for posts of the given person.
|
||||
class OthersRelayables
|
||||
# @param person_id [Integer] Database id of a person for whom we want to request relayalbes
|
||||
def initialize(person_id)
|
||||
@person_id = person_id
|
||||
end
|
||||
|
||||
# Comments of other people to the person's post
|
||||
# @return [Comment::ActiveRecord_Relation]
|
||||
def comments
|
||||
Comment
|
||||
.where.not(author_id: person_id)
|
||||
.joins("INNER JOIN posts ON (commentable_type = 'Post' AND posts.id = commentable_id)")
|
||||
.where("posts.author_id = ?", person_id)
|
||||
end
|
||||
|
||||
# Likes of other people to the person's post
|
||||
# @return [Like::ActiveRecord_Relation]
|
||||
def likes
|
||||
Like
|
||||
.where.not(author_id: person_id)
|
||||
.joins("INNER JOIN posts ON (target_type = 'Post' AND posts.id = target_id)")
|
||||
.where("posts.author_id = ?", person_id)
|
||||
end
|
||||
|
||||
# Poll participations of other people to the person's polls
|
||||
# @return [PollParticipation::ActiveRecord_Relation]
|
||||
def poll_participations
|
||||
PollParticipation
|
||||
.where.not(author_id: person_id).joins(:status_message)
|
||||
.where("posts.author_id = ?", person_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :person_id
|
||||
end
|
||||
end
|
||||
end
|
||||
59
lib/diaspora/exporter/posts_with_activity.rb
Normal file
59
lib/diaspora/exporter/posts_with_activity.rb
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
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
|
||||
|
|
@ -50,6 +50,14 @@ module Diaspora
|
|||
end
|
||||
end
|
||||
|
||||
# Remote pods which are known to be subscribed to the post. Must include all pods which received the post in the
|
||||
# past.
|
||||
#
|
||||
# @return [Array<String>] The list of pods' URIs
|
||||
def subscribed_pods_uris
|
||||
Pod.find(subscribers.select(&:remote?).map(&:pod_id).uniq).map {|pod| pod.url_to("") }
|
||||
end
|
||||
|
||||
module QueryMethods
|
||||
def owned_or_visible_by_user(user)
|
||||
with_visibility.where(
|
||||
|
|
|
|||
255
lib/schemas/archive-format.json
Normal file
255
lib/schemas/archive-format.json
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "https://diaspora.github.io/diaspora/schemas/archive_format.json",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": { "type": "string" },
|
||||
"language": { "type": "string" },
|
||||
"username": { "type": "string" },
|
||||
"private_key": { "type": "string" },
|
||||
"disable_mail": { "type": "boolean" },
|
||||
"show_community_spotlight_in_stream": { "type": "boolean" },
|
||||
"auto_follow_back": { "type": "boolean" },
|
||||
"auto_follow_back_aspect": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"strip_exif": { "type": "boolean" },
|
||||
|
||||
"profile": {
|
||||
"$ref": "https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json#definitions/profile"
|
||||
},
|
||||
|
||||
"contact_groups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"contacts_visible": { "type": "boolean" },
|
||||
"chat_enabled": { "type": "boolean" }
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"contacts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sharing": { "type": "boolean" },
|
||||
"following": { "type": "boolean" },
|
||||
"receiving": { "type": "boolean" },
|
||||
"followed": { "type": "boolean" },
|
||||
"account_id": { "type": "string" },
|
||||
"contact_groups_membership": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"person_name": { "type": "string" },
|
||||
"person_guid": {
|
||||
"$ref": "https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json#/definitions/guid"
|
||||
},
|
||||
"public_key": { "type": "string" }
|
||||
},
|
||||
"required": [
|
||||
"sharing",
|
||||
"following",
|
||||
"receiving",
|
||||
"followed",
|
||||
"account_id",
|
||||
"contact_groups_membership"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"posts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/posts"
|
||||
},
|
||||
{
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/remote_subscription/public" },
|
||||
{ "$ref": "#/definitions/remote_subscription/private" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
"relayables": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json#/definitions/comment"
|
||||
},
|
||||
{
|
||||
"$ref": "https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json#/definitions/like"
|
||||
},
|
||||
{
|
||||
"$ref": "https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json#/definitions/poll_participation"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"followed_tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
|
||||
"post_subscriptions": {
|
||||
"type": "array",
|
||||
"description": "GUIDs of posts for which changes we want to be subscribed",
|
||||
"items": {
|
||||
"$ref": "https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json#/definitions/guid"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"username",
|
||||
"email",
|
||||
"private_key",
|
||||
"profile"
|
||||
]
|
||||
},
|
||||
"others_data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"relayables": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"allOf": [
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json#/definitions/comment"
|
||||
},
|
||||
{
|
||||
"$ref": "https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json#/definitions/like"
|
||||
},
|
||||
{
|
||||
"$ref": "https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json#/definitions/poll_participation"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"non_contact_authors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"account_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"guid": {
|
||||
"type": "string"
|
||||
},
|
||||
"public_key": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"account_id",
|
||||
"guid",
|
||||
"public_key"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"posts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/posts"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"pattern": "^2\.0$"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"user",
|
||||
"version"
|
||||
],
|
||||
"definitions": {
|
||||
"posts": {
|
||||
"oneOf": [
|
||||
{ "$ref": "https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json#/definitions/status_message" },
|
||||
{ "$ref": "https://diaspora.github.io/diaspora_federation/schemas/federation_entities.json#/definitions/reshare" }
|
||||
]
|
||||
},
|
||||
|
||||
"remote_subscription": {
|
||||
"public": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"subscribed_pods_uris": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"entity_data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"public": {
|
||||
"enum": [ true ]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"public"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"entity_data"
|
||||
]
|
||||
},
|
||||
|
||||
"private": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"subscribed_users_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
|
||||
"entity_data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"public": {
|
||||
"enum": [ false ]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"public"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -149,9 +149,9 @@ FactoryGirl.define do
|
|||
end
|
||||
|
||||
factory(:location) do
|
||||
address "Fernsehturm Berlin, Berlin, Germany"
|
||||
lat 52.520645
|
||||
lng 13.409779
|
||||
sequence(:address) {|n| "Fernsehturm Berlin, #{n}, Berlin, Germany" }
|
||||
sequence(:lat) {|n| 52.520645 + 0.0000001 * n }
|
||||
sequence(:lng) {|n| 13.409779 + 0.0000001 * n }
|
||||
end
|
||||
|
||||
factory :participation do
|
||||
|
|
@ -342,6 +342,11 @@ FactoryGirl.define do
|
|||
additional_data { {"new_property" => "some text"} }
|
||||
end
|
||||
|
||||
factory :role do
|
||||
association :person
|
||||
name "moderator"
|
||||
end
|
||||
|
||||
factory(:poll_participation_signature) do
|
||||
author_signature "some signature"
|
||||
association :signature_order, order: "guid parent_guid author poll_answer_guid new_property"
|
||||
|
|
|
|||
|
|
@ -27,12 +27,12 @@ module HelperMethods
|
|||
File.open(fixture_name)
|
||||
end
|
||||
|
||||
def create_conversation_with_message(sender, recipient_person, subject, text)
|
||||
def create_conversation_with_message(sender_person, recipient_person, subject, text)
|
||||
create_hash = {
|
||||
:author => sender.person,
|
||||
:participant_ids => [sender.person.id, recipient_person.id],
|
||||
:subject => subject,
|
||||
:messages_attributes => [ {:author => sender.person, :text => text} ]
|
||||
author: sender_person,
|
||||
participant_ids: [sender_person.id, recipient_person.id],
|
||||
subject: subject,
|
||||
messages_attributes: [{author: sender_person, text: text}]
|
||||
}
|
||||
|
||||
Conversation.create!(create_hash)
|
||||
|
|
|
|||
|
|
@ -1,128 +1,46 @@
|
|||
describe "deleteing your account", type: :request do
|
||||
context "user" do
|
||||
before do
|
||||
@person = bob.person
|
||||
@alices_post = alice.post(:status_message,
|
||||
text: "@{bob Grimn; #{bob.person.diaspora_handle}} you are silly",
|
||||
to: alice.aspects.find_by_name("generic"))
|
||||
|
||||
# bob's own content
|
||||
bob.post(:status_message, text: "asldkfjs", to: bob.aspects.first)
|
||||
FactoryGirl.create(:photo, author: bob.person)
|
||||
|
||||
@aspect_vis = AspectVisibility.where(aspect_id: bob.aspects.map(&:id))
|
||||
|
||||
# objects on post
|
||||
bob.like!(@alices_post)
|
||||
bob.comment!(@alices_post, "here are some thoughts on your post")
|
||||
|
||||
# conversations
|
||||
create_conversation_with_message(alice, bob.person, "Subject", "Hey bob")
|
||||
|
||||
# join tables
|
||||
@users_sv = ShareVisibility.where(user_id: bob.id).load
|
||||
@persons_sv = ShareVisibility.where(shareable_id: bob.posts.map(&:id), shareable_type: "Post").load
|
||||
|
||||
# user associated objects
|
||||
@prefs = []
|
||||
%w(mentioned liked reshared).each do |pref|
|
||||
@prefs << bob.user_preferences.create!(email_type: pref)
|
||||
end
|
||||
|
||||
# notifications
|
||||
@notifications = []
|
||||
3.times do
|
||||
@notifications << FactoryGirl.create(:notification, recipient: bob)
|
||||
end
|
||||
|
||||
# services
|
||||
@services = []
|
||||
3.times do
|
||||
@services << FactoryGirl.create(:service, user: bob)
|
||||
end
|
||||
|
||||
# block
|
||||
@block = bob.blocks.create!(person: eve.person)
|
||||
|
||||
AccountDeleter.new(bob.person.diaspora_handle).perform!
|
||||
bob.reload
|
||||
end
|
||||
|
||||
it "deletes all of the user's preferences" do
|
||||
expect(UserPreference.where(id: @prefs.map(&:id))).to be_empty
|
||||
end
|
||||
|
||||
it "deletes all of the user's notifications" do
|
||||
expect(Notification.where(id: @notifications.map(&:id))).to be_empty
|
||||
end
|
||||
|
||||
it "deletes all of the users's blocked users" do
|
||||
expect(Block.where(id: @block.id)).to be_empty
|
||||
end
|
||||
|
||||
it "deletes all of the user's services" do
|
||||
expect(Service.where(id: @services.map(&:id))).to be_empty
|
||||
end
|
||||
|
||||
it "deletes all of bobs share visiblites" do
|
||||
expect(ShareVisibility.where(id: @users_sv.map(&:id))).to be_empty
|
||||
expect(ShareVisibility.where(id: @persons_sv.map(&:id))).to be_empty
|
||||
end
|
||||
|
||||
it "deletes all of bobs aspect visiblites" do
|
||||
expect(AspectVisibility.where(id: @aspect_vis.map(&:id))).to be_empty
|
||||
end
|
||||
|
||||
it "deletes all aspects" do
|
||||
expect(bob.aspects).to be_empty
|
||||
end
|
||||
|
||||
it "deletes all user contacts" do
|
||||
expect(bob.contacts).to be_empty
|
||||
end
|
||||
|
||||
it "clears the account fields" do
|
||||
bob.send(:clearable_fields).each do |field|
|
||||
expect(bob.reload[field]).to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
it_should_behave_like "it removes the person associations"
|
||||
describe "deleteing account", type: :request do
|
||||
def account_removal_method
|
||||
AccountDeleter.new(subject.diaspora_handle).perform!
|
||||
subject.reload
|
||||
end
|
||||
|
||||
context "remote person" do
|
||||
context "of local user" do
|
||||
subject(:user) { FactoryGirl.create(:user_with_aspect) }
|
||||
|
||||
before do
|
||||
@person = remote_raphael
|
||||
|
||||
# contacts
|
||||
@contacts = @person.contacts
|
||||
|
||||
# posts
|
||||
@posts = (1..3).map do
|
||||
FactoryGirl.create(:status_message, author: @person)
|
||||
end
|
||||
|
||||
@persons_sv = @posts.each do |post|
|
||||
@contacts.each do |contact|
|
||||
ShareVisibility.create!(user_id: contact.user.id, shareable: post)
|
||||
end
|
||||
end
|
||||
|
||||
# photos
|
||||
@photo = FactoryGirl.create(:photo, author: @person)
|
||||
|
||||
# mentions
|
||||
@mentions = 3.times do
|
||||
FactoryGirl.create(:mention, person: @person)
|
||||
end
|
||||
|
||||
# conversations
|
||||
create_conversation_with_message(alice, @person, "Subject", "Hey bob")
|
||||
|
||||
AccountDeleter.new(@person.diaspora_handle).perform!
|
||||
@person.reload
|
||||
DataGenerator.create(subject, :generic_user_data)
|
||||
end
|
||||
|
||||
it_should_behave_like "it removes the person associations"
|
||||
it "deletes all of the user data" do
|
||||
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" do
|
||||
subject(:person) { user.person }
|
||||
end
|
||||
end
|
||||
|
||||
context "of remote person" do
|
||||
subject(:person) { remote_raphael }
|
||||
|
||||
before do
|
||||
DataGenerator.create(subject, :generic_person_data)
|
||||
end
|
||||
|
||||
it_behaves_like "it removes the person associations"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
444
spec/integration/exporter_spec.rb
Normal file
444
spec/integration/exporter_spec.rb
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
# Copyright (c) 2010-2011, Diaspora Inc. This file is
|
||||
# licensed under the Affero General Public License version 3 or later. See
|
||||
# the COPYRIGHT file.
|
||||
|
||||
describe Diaspora::Exporter do
|
||||
let(:user) { FactoryGirl.create(:user_with_aspect) }
|
||||
|
||||
context "output json" do
|
||||
let(:json) { Diaspora::Exporter.new(user).execute }
|
||||
|
||||
it "matches archive schema" do
|
||||
DataGenerator.create(
|
||||
user,
|
||||
%i[generic_user_data activity status_messages_flavours work_aspect]
|
||||
)
|
||||
|
||||
expect(JSON.parse(json)).to match_json_schema(:archive_schema)
|
||||
end
|
||||
|
||||
it "contains basic user data" do
|
||||
user_properties = build_property_hash(
|
||||
user,
|
||||
%i[email username language disable_mail show_community_spotlight_in_stream auto_follow_back
|
||||
auto_follow_back_aspect strip_exif],
|
||||
private_key: :serialized_private_key
|
||||
)
|
||||
|
||||
user_properties[:profile] = {
|
||||
entity_type: "profile",
|
||||
entity_data: build_property_hash(
|
||||
user.profile,
|
||||
%i[first_name last_name gender bio location image_url birthday searchable nsfw tag_string],
|
||||
author: :diaspora_handle
|
||||
)
|
||||
}
|
||||
|
||||
expect(json).to include_json(user: user_properties)
|
||||
end
|
||||
|
||||
it "contains aspects" do
|
||||
DataGenerator.create(user, :work_aspect)
|
||||
|
||||
expect(json).to include_json(
|
||||
user: {
|
||||
"contact_groups": [
|
||||
{
|
||||
"name": "generic",
|
||||
"contacts_visible": true,
|
||||
"chat_enabled": false
|
||||
},
|
||||
{
|
||||
"name": "Work",
|
||||
"contacts_visible": false,
|
||||
"chat_enabled": false
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it "contains contacts" do
|
||||
friends = DataGenerator.create(user, Array.new(2, :mutual_friend))
|
||||
serialized_contacts = friends.map {|friend|
|
||||
contact = Contact.find_by(person_id: friend.person_id)
|
||||
hash = build_property_hash(
|
||||
contact,
|
||||
%i[sharing receiving person_guid person_name],
|
||||
following: :sharing, followed: :receiving, account_id: :person_diaspora_handle
|
||||
)
|
||||
hash[:public_key] = contact.person.serialized_public_key
|
||||
hash[:contact_groups_membership] = contact.aspects.map(&:name)
|
||||
hash
|
||||
}
|
||||
|
||||
expect(json).to include_json(user: {contacts: serialized_contacts})
|
||||
end
|
||||
|
||||
it "contains a public status message" do
|
||||
status_message = FactoryGirl.create(:status_message, author: user.person, public: true)
|
||||
serialized = {
|
||||
"subscribed_pods_uris": [AppConfig.pod_uri.to_s],
|
||||
"entity_type": "status_message",
|
||||
"entity_data": {
|
||||
"author": user.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]})
|
||||
end
|
||||
|
||||
it "contains a status message with subscribers" do
|
||||
subscriber, status_message = DataGenerator.create(user, :status_message_with_subscriber)
|
||||
serialized = {
|
||||
"subscribed_users_ids": [subscriber.diaspora_handle],
|
||||
"entity_type": "status_message",
|
||||
"entity_data": {
|
||||
"author": user.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: {posts: [serialized]})
|
||||
end
|
||||
|
||||
it "contains a status message with a poll" do
|
||||
status_message = FactoryGirl.create(:status_message_with_poll, author: user.person)
|
||||
serialized = {
|
||||
"entity_type": "status_message",
|
||||
"entity_data": {
|
||||
"author": user.diaspora_handle,
|
||||
"guid": status_message.guid,
|
||||
"created_at": status_message.created_at.iso8601,
|
||||
"text": status_message.text,
|
||||
"poll": {
|
||||
"entity_type": "poll",
|
||||
"entity_data": {
|
||||
"guid": status_message.poll.guid,
|
||||
"question": status_message.poll.question,
|
||||
"poll_answers": status_message.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: {posts: [serialized]})
|
||||
end
|
||||
|
||||
it "contains a status message with a photo" do
|
||||
status_message = FactoryGirl.create(:status_message_with_photo, author: user.person)
|
||||
|
||||
serialized = {
|
||||
"entity_type": "status_message",
|
||||
"entity_data": {
|
||||
"author": user.diaspora_handle,
|
||||
"guid": status_message.guid,
|
||||
"created_at": status_message.created_at.iso8601,
|
||||
"text": status_message.text,
|
||||
"photos": [
|
||||
{
|
||||
"entity_type": "photo",
|
||||
"entity_data": {
|
||||
"guid": status_message.photos.first.guid,
|
||||
"author": user.diaspora_handle,
|
||||
"public": false,
|
||||
"created_at": status_message.photos.first.created_at.iso8601,
|
||||
"remote_photo_path": "#{AppConfig.pod_uri}uploads\/images\/",
|
||||
"remote_photo_name": status_message.photos.first.remote_photo_name,
|
||||
"status_message_guid": status_message.guid,
|
||||
"height": 42,
|
||||
"width": 23
|
||||
}
|
||||
}
|
||||
],
|
||||
"public": false
|
||||
}
|
||||
}
|
||||
|
||||
expect(json).to include_json(user: {posts: [serialized]})
|
||||
end
|
||||
|
||||
it "contains a status message with a location" do
|
||||
status_message = FactoryGirl.create(:status_message_with_location, author: user.person)
|
||||
|
||||
serialized = {
|
||||
"entity_type": "status_message",
|
||||
"entity_data": {
|
||||
"author": user.diaspora_handle,
|
||||
"guid": status_message.guid,
|
||||
"created_at": status_message.created_at.iso8601,
|
||||
"text": status_message.text,
|
||||
"location": {
|
||||
"entity_type": "location",
|
||||
"entity_data": {
|
||||
"address": status_message.location.address,
|
||||
"lat": status_message.location.lat,
|
||||
"lng": status_message.location.lng
|
||||
}
|
||||
},
|
||||
"public": false
|
||||
}
|
||||
}
|
||||
|
||||
expect(json).to include_json(user: {posts: [serialized]})
|
||||
end
|
||||
|
||||
it "contains a reshare and its root" do
|
||||
reshare = FactoryGirl.create(:reshare, author: user.person)
|
||||
serialized_reshare = {
|
||||
"subscribed_pods_uris": [reshare.root.author.pod.url_to(""), AppConfig.pod_uri.to_s],
|
||||
"entity_type": "reshare",
|
||||
"entity_data": {
|
||||
"author": user.diaspora_handle,
|
||||
"guid": reshare.guid,
|
||||
"created_at": reshare.created_at.iso8601,
|
||||
"public": true,
|
||||
"root_author": reshare.root_author.diaspora_handle,
|
||||
"root_guid": reshare.root_guid
|
||||
}
|
||||
}
|
||||
|
||||
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]}
|
||||
)
|
||||
end
|
||||
|
||||
it "contains followed tags" do
|
||||
tag_following = DataGenerator.create(user, :tag_following)
|
||||
expect(json).to include_json(user: {followed_tags: [tag_following.tag.name]})
|
||||
end
|
||||
|
||||
it "contains post subscriptions" do
|
||||
subscription = DataGenerator.create(user, :subscription)
|
||||
expect(json).to include_json(user: {post_subscriptions: [subscription.target.guid]})
|
||||
end
|
||||
|
||||
it "contains a comment and the commented post" do
|
||||
comment = FactoryGirl.create(:comment, author: user.person)
|
||||
serialized_comment = {
|
||||
"entity_type": "comment",
|
||||
"entity_data": {
|
||||
"author": user.diaspora_handle,
|
||||
"guid": comment.guid,
|
||||
"parent_guid": comment.parent.guid,
|
||||
"text": comment.text,
|
||||
"created_at": comment.created_at.iso8601
|
||||
},
|
||||
"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]}
|
||||
)
|
||||
end
|
||||
|
||||
it "contains a like and the liked post" do
|
||||
like = FactoryGirl.create(:like, author: user.person)
|
||||
serialized_like = {
|
||||
"entity_type": "like",
|
||||
"entity_data": {
|
||||
"author": user.diaspora_handle,
|
||||
"guid": like.guid,
|
||||
"parent_guid": like.parent.guid,
|
||||
"parent_type": like.target_type,
|
||||
"positive": like.positive
|
||||
},
|
||||
"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]}
|
||||
)
|
||||
end
|
||||
|
||||
it "contains a poll participation and post with this poll" do
|
||||
poll_participation = FactoryGirl.create(:poll_participation, author: user.person)
|
||||
serialized_participation = {
|
||||
"entity_type": "poll_participation",
|
||||
"entity_data": {
|
||||
"author": user.diaspora_handle,
|
||||
"guid": poll_participation.guid,
|
||||
"parent_guid": poll_participation.parent.guid,
|
||||
"poll_answer_guid": poll_participation.poll_answer.guid
|
||||
},
|
||||
"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]}
|
||||
)
|
||||
end
|
||||
|
||||
it "contains a comment for the user's post" do
|
||||
status_message, comment = DataGenerator.create(user, :status_message_with_comment)
|
||||
serialized = {
|
||||
"entity_type": "comment",
|
||||
"entity_data": {
|
||||
"author": comment.diaspora_handle,
|
||||
"guid": comment.guid,
|
||||
"parent_guid": status_message.guid,
|
||||
"text": comment.text,
|
||||
"created_at": comment.created_at.iso8601,
|
||||
"author_signature": Diaspora::Federation::Entities.build(comment).to_h[:author_signature]
|
||||
},
|
||||
"property_order": %w[author guid parent_guid text created_at]
|
||||
}
|
||||
|
||||
expect(json).to include_json(others_data: {relayables: [serialized]})
|
||||
end
|
||||
|
||||
it "contains a like for the user's post" do
|
||||
status_message, like = DataGenerator.create(user, :status_message_with_like)
|
||||
serialized = {
|
||||
"entity_type": "like",
|
||||
"entity_data": {
|
||||
"author": like.diaspora_handle,
|
||||
"guid": like.guid,
|
||||
"parent_guid": status_message.guid,
|
||||
"parent_type": like.target_type,
|
||||
"positive": like.positive,
|
||||
"author_signature": Diaspora::Federation::Entities.build(like).to_h[:author_signature]
|
||||
},
|
||||
"property_order": %w[author guid parent_guid parent_type positive]
|
||||
}
|
||||
|
||||
expect(json).to include_json(others_data: {relayables: [serialized]})
|
||||
end
|
||||
|
||||
it "contains a poll participation for the user's post" do
|
||||
_, poll_participation = DataGenerator.create(user, :status_message_with_poll_participation)
|
||||
serialized = {
|
||||
"entity_type": "poll_participation",
|
||||
"entity_data": {
|
||||
"author": poll_participation.diaspora_handle,
|
||||
"guid": poll_participation.guid,
|
||||
"parent_guid": poll_participation.parent.guid,
|
||||
"poll_answer_guid": poll_participation.poll_answer.guid,
|
||||
"author_signature": Diaspora::Federation::Entities.build(poll_participation).to_h[:author_signature]
|
||||
},
|
||||
"property_order": %w[author guid parent_guid poll_answer_guid]
|
||||
}
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
def build_property_hash(object, direct_properties, aliased_properties={})
|
||||
props = direct_properties.map {|key|
|
||||
[key, transform_value(object.send(key))]
|
||||
}.to_h
|
||||
|
||||
aliased = aliased_properties.map {|key, key_alias|
|
||||
[key, object.send(key_alias)]
|
||||
}.to_h
|
||||
|
||||
props.merge(aliased)
|
||||
end
|
||||
end
|
||||
end
|
||||
25
spec/lib/diaspora/exporter/non_contact_authors_spec.rb
Normal file
25
spec/lib/diaspora/exporter/non_contact_authors_spec.rb
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
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
|
||||
35
spec/lib/diaspora/exporter/others_relayables_spec.rb
Normal file
35
spec/lib/diaspora/exporter/others_relayables_spec.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
describe Diaspora::Exporter::OthersRelayables do
|
||||
let(:status_message) { FactoryGirl.create(:status_message) }
|
||||
let(:person) { status_message.author }
|
||||
let(:instance) { Diaspora::Exporter::OthersRelayables.new(person.id) }
|
||||
|
||||
describe "#comments" do
|
||||
let(:comment) { FactoryGirl.create(:comment, post: status_message) }
|
||||
|
||||
it "has a comment in the data set" do
|
||||
expect(instance.comments).to eq([comment])
|
||||
end
|
||||
end
|
||||
|
||||
describe "#likes" do
|
||||
let(:like) { FactoryGirl.create(:like, target: status_message) }
|
||||
|
||||
it "has a like in the data set" do
|
||||
expect(instance.likes).to eq([like])
|
||||
end
|
||||
end
|
||||
|
||||
describe "#poll_participations" do
|
||||
let(:status_message) { FactoryGirl.create(:status_message_with_poll) }
|
||||
let(:poll_participation) {
|
||||
FactoryGirl.create(
|
||||
:poll_participation,
|
||||
poll_answer: status_message.poll.poll_answers.first
|
||||
)
|
||||
}
|
||||
|
||||
it "has a poll participation in the data set" do
|
||||
expect(instance.poll_participations).to eq([poll_participation])
|
||||
end
|
||||
end
|
||||
end
|
||||
24
spec/lib/diaspora/exporter/posts_with_activity_spec.rb
Normal file
24
spec/lib/diaspora/exporter/posts_with_activity_spec.rb
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
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
|
||||
|
|
@ -1,86 +1,15 @@
|
|||
# Copyright (c) 2010-2011, Diaspora Inc. This file is
|
||||
# licensed under the Affero General Public License version 3 or later. See
|
||||
# the COPYRIGHT file.
|
||||
|
||||
require Rails.root.join('lib', 'diaspora', 'exporter')
|
||||
|
||||
describe Diaspora::Exporter do
|
||||
describe "#execute" do
|
||||
it "calls exporters and forms JSON" 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")
|
||||
|
||||
before do
|
||||
@user1 = alice
|
||||
|
||||
@user1.person.profile.first_name = "<script>"
|
||||
@user1.person.profile.gender = "<script>"
|
||||
@user1.person.profile.bio = "<script>"
|
||||
@user1.person.profile.location = "<script>"
|
||||
@user1.person.profile.save
|
||||
|
||||
@aspect = @user1.aspects.first
|
||||
@aspect1 = @user1.aspects.create(:name => "Work", :contacts_visible => false)
|
||||
@aspect.name = "<script>"
|
||||
@aspect.save
|
||||
end
|
||||
|
||||
context "json" do
|
||||
|
||||
def json
|
||||
@json ||= JSON.parse Diaspora::Exporter.new(@user1).execute
|
||||
json = Diaspora::Exporter.new(nil).execute
|
||||
expect(json).to include_json(
|
||||
version: "2.0",
|
||||
user: "user_data",
|
||||
others_date: "others_data"
|
||||
)
|
||||
end
|
||||
|
||||
it { matches :version, to: '1.0' }
|
||||
it { matches :user, :name }
|
||||
it { matches :user, :email }
|
||||
it { matches :user, :username }
|
||||
it { matches :user, :language }
|
||||
it { matches :user, :disable_mail }
|
||||
it { matches :user, :show_community_spotlight_in_stream }
|
||||
it { matches :user, :auto_follow_back }
|
||||
it { matches :user, :auto_follow_back_aspect }
|
||||
it { matches :user, :strip_exif }
|
||||
|
||||
it { matches :user, :profile, :first_name, root: @user1.person.profile }
|
||||
it { matches :user, :profile, :last_name, root: @user1.person.profile }
|
||||
it { matches :user, :profile, :gender, root: @user1.person.profile }
|
||||
it { matches :user, :profile, :bio, root: @user1.person.profile }
|
||||
it { matches :user, :profile, :location, root: @user1.person.profile }
|
||||
it { matches :user, :profile, :image_url, root: @user1.person.profile }
|
||||
it { matches :user, :profile, :diaspora_handle, root: @user1.person.profile }
|
||||
it { matches :user, :profile, :searchable, root: @user1.person.profile }
|
||||
it { matches :user, :profile, :nsfw, root: @user1.person.profile }
|
||||
|
||||
it { matches_relation :aspects, :name,
|
||||
:contacts_visible,
|
||||
:chat_enabled }
|
||||
|
||||
it { matches_relation :contacts, :sharing,
|
||||
:receiving,
|
||||
:person_guid,
|
||||
:person_name,
|
||||
:person_first_name,
|
||||
:person_diaspora_handle }
|
||||
|
||||
private
|
||||
|
||||
def matches(*fields, to: nil, root: @user1)
|
||||
expected = to || root.send(fields.last)
|
||||
expect(recurse_field(json, fields)).to eq expected
|
||||
end
|
||||
|
||||
def matches_relation(relation, *fields, to: nil, root: @user1)
|
||||
array = json['user'][to || relation.to_s]
|
||||
fields.each do |field|
|
||||
expected = root.send(relation).map(&:"#{field}")
|
||||
expect(array.map { |f| f[field.to_s] }).to eq expected
|
||||
end
|
||||
end
|
||||
|
||||
def recurse_field(json, fields)
|
||||
if fields.any?
|
||||
recurse_field json[fields.shift.to_s], fields
|
||||
else
|
||||
json
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -175,6 +175,27 @@ describe Person, :type => :model do
|
|||
expect(result[1].id).to eq(person1.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".in_aspects" do
|
||||
it "returns person that is in the aspect" do
|
||||
aspect = FactoryGirl.create(:aspect)
|
||||
contact = FactoryGirl.create(:contact, user: aspect.user)
|
||||
aspect.contacts << contact
|
||||
expect(Person.in_aspects([aspect.id])).to include(contact.person)
|
||||
end
|
||||
|
||||
it "returns same person in multiple aspects only once" do
|
||||
user = bob
|
||||
contact = FactoryGirl.create(:contact, user: user)
|
||||
ids = Array.new(2) do
|
||||
aspect = FactoryGirl.create(:aspect, user: user, name: r_str)
|
||||
aspect.contacts << contact
|
||||
aspect.id
|
||||
end
|
||||
|
||||
expect(Person.in_aspects(ids)).to eq([contact.person])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "delegating" do
|
||||
|
|
|
|||
|
|
@ -174,6 +174,50 @@ describe Post, :type => :model do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".subscribed_by" do
|
||||
let(:user) { FactoryGirl.create(:user) }
|
||||
|
||||
context "when the user has a participation on a post" do
|
||||
let(:post) { FactoryGirl.create(:status_message_with_participations, participants: [user]) }
|
||||
|
||||
it "includes the post to the result set" do
|
||||
expect(Post.subscribed_by(user)).to eq([post])
|
||||
end
|
||||
end
|
||||
|
||||
context "when the user doens't have a participation on a post" do
|
||||
before do
|
||||
FactoryGirl.create(:status_message)
|
||||
end
|
||||
|
||||
it "returns empty result set" do
|
||||
expect(Post.subscribed_by(user)).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".reshared_by" do
|
||||
let(:person) { FactoryGirl.create(:person) }
|
||||
|
||||
context "when the person has a reshare for a post" do
|
||||
let(:post) { FactoryGirl.create(:reshare, author: person).root }
|
||||
|
||||
it "includes the post to the result set" do
|
||||
expect(Post.reshared_by(person)).to eq([post])
|
||||
end
|
||||
end
|
||||
|
||||
context "when the person has no reshare for a post" do
|
||||
before do
|
||||
FactoryGirl.create(:status_message)
|
||||
end
|
||||
|
||||
it "returns empty result set" do
|
||||
expect(Post.reshared_by(person)).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ describe StatusMessage, type: :model do
|
|||
let!(:aspect) { user.aspects.first }
|
||||
let(:status) { build(:status_message) }
|
||||
|
||||
it_behaves_like "a shareable" do
|
||||
let(:object) { status }
|
||||
end
|
||||
|
||||
describe "scopes" do
|
||||
describe ".where_person_is_mentioned" do
|
||||
it "returns status messages where the given person is mentioned" do
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
describe Export::CommentSerializer do
|
||||
let(:comment) { create(:comment) }
|
||||
subject(:json_output) { Export::CommentSerializer.new(comment).to_json }
|
||||
|
||||
it { is_expected.to include %("guid":"#{comment.guid}") }
|
||||
it { is_expected.to include %("post_guid":"#{comment.post.guid}") }
|
||||
it { is_expected.to include %("text":"#{comment.text}") }
|
||||
end
|
||||
12
spec/serializers/export/aspect_serializer_spec.rb
Normal file
12
spec/serializers/export/aspect_serializer_spec.rb
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
describe Export::AspectSerializer do
|
||||
let(:aspect) { FactoryGirl.create(:aspect) }
|
||||
let(:serializer) { Export::AspectSerializer.new(aspect) }
|
||||
|
||||
it "has aspect attributes" do
|
||||
expect(serializer.attributes).to eq(
|
||||
name: aspect.name,
|
||||
contacts_visible: aspect.contacts_visible,
|
||||
chat_enabled: aspect.chat_enabled
|
||||
)
|
||||
end
|
||||
end
|
||||
25
spec/serializers/export/contact_serializer_spec.rb
Normal file
25
spec/serializers/export/contact_serializer_spec.rb
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
describe Export::ContactSerializer do
|
||||
let(:contact) { FactoryGirl.create(:contact) }
|
||||
let(:serializer) { Export::ContactSerializer.new(contact) }
|
||||
let(:aspect) { FactoryGirl.create(:aspect) }
|
||||
|
||||
it "has contact attributes" do
|
||||
expect(serializer.attributes).to eq(
|
||||
sharing: contact.sharing,
|
||||
following: contact.sharing,
|
||||
receiving: contact.receiving,
|
||||
followed: contact.receiving,
|
||||
person_guid: contact.person_guid,
|
||||
person_name: contact.person_name,
|
||||
account_id: contact.person_diaspora_handle,
|
||||
public_key: contact.person.serialized_public_key
|
||||
)
|
||||
end
|
||||
|
||||
it "serializes aspects membership" do
|
||||
contact.aspects << aspect
|
||||
expect(Export::ContactSerializer).to serialize_association(:contact_groups_membership)
|
||||
.with_objects(contact.aspects.map(&:name))
|
||||
expect(serializer.associations[:contact_groups_membership]).to eq([aspect.name])
|
||||
end
|
||||
end
|
||||
43
spec/serializers/export/others_data_serializer_spec.rb
Normal file
43
spec/serializers/export/others_data_serializer_spec.rb
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
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)
|
||||
]
|
||||
}
|
||||
|
||||
it "uses FederationEntitySerializer for array serializing relayables" do
|
||||
sm = DataGenerator.new(user).status_message_with_activity
|
||||
|
||||
expect(Export::OthersDataSerializer).to serialize_association(:relayables)
|
||||
.with_each_serializer(FederationEntitySerializer)
|
||||
.with_objects([*sm.likes, *sm.comments, *sm.poll_participations])
|
||||
serializer.associations
|
||||
end
|
||||
|
||||
context "with user's activity" 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
|
||||
48
spec/serializers/export/own_post_serializer_spec.rb
Normal file
48
spec/serializers/export/own_post_serializer_spec.rb
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
describe Export::OwnPostSerializer do
|
||||
let(:author) { FactoryGirl.create(:user_with_aspect).person }
|
||||
|
||||
before do
|
||||
author.owner.share_with(FactoryGirl.create(:person), author.owner.aspects.first)
|
||||
end
|
||||
|
||||
it_behaves_like "a federation entity serializer" do
|
||||
let(:object) { create(:status_message_with_photo, author: author) }
|
||||
end
|
||||
|
||||
let(:json) { Export::OwnPostSerializer.new(post, root: false).to_json }
|
||||
|
||||
context "with private post" do
|
||||
let(:post) { create(:status_message_in_aspect, author: author) }
|
||||
|
||||
it "includes remote people subscriptions" do
|
||||
expect(JSON.parse(json)["subscribed_users_ids"]).not_to be_empty
|
||||
expect(json).to include_json(subscribed_users_ids: post.subscribers.map(&:diaspora_handle))
|
||||
end
|
||||
|
||||
it "doesn't include remote pods subscriptions" do
|
||||
expect(JSON.parse(json)).not_to have_key("subscribed_pods_uris")
|
||||
end
|
||||
end
|
||||
|
||||
context "with public post" do
|
||||
let(:post) {
|
||||
FactoryGirl.create(
|
||||
:status_message_with_participations,
|
||||
author: author,
|
||||
participants: Array.new(2) { FactoryGirl.create(:person) },
|
||||
public: true
|
||||
)
|
||||
}
|
||||
|
||||
it "includes pods subscriptions" do
|
||||
expect(JSON.parse(json)["subscribed_pods_uris"]).not_to be_empty
|
||||
expect(json).to include_json(
|
||||
subscribed_pods_uris: post.subscribed_pods_uris.push(AppConfig.pod_uri.to_s)
|
||||
)
|
||||
end
|
||||
|
||||
it "doesn't include remote people subscriptions" do
|
||||
expect(JSON.parse(json)).not_to have_key("subscribed_users_ids")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
describe Export::OwnRelayablesSerializer do
|
||||
let(:comment) { FactoryGirl.create(:comment) }
|
||||
let!(:signature) { FactoryGirl.create(:comment_signature, comment: comment) }
|
||||
let(:instance) { Export::OwnRelayablesSerializer.new(comment, root: false) }
|
||||
|
||||
it "doesn't include author signature to the entity data" do
|
||||
expect(JSON.parse(instance.to_json)["entity_data"]).not_to have_key("author_signature")
|
||||
end
|
||||
end
|
||||
12
spec/serializers/export/person_metadata_serializer_spec.rb
Normal file
12
spec/serializers/export/person_metadata_serializer_spec.rb
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
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
|
||||
79
spec/serializers/export/user_serializer_spec.rb
Normal file
79
spec/serializers/export/user_serializer_spec.rb
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
describe Export::UserSerializer do
|
||||
let(:user) { FactoryGirl.create(:user) }
|
||||
let(:serializer) { Export::UserSerializer.new(user, root: false) }
|
||||
|
||||
it "has basic user's attributes" do
|
||||
expect(serializer.attributes).to eq(
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
language: user.language,
|
||||
private_key: user.serialized_private_key,
|
||||
disable_mail: user.disable_mail,
|
||||
show_community_spotlight_in_stream: user.show_community_spotlight_in_stream,
|
||||
auto_follow_back: user.auto_follow_back,
|
||||
auto_follow_back_aspect: user.auto_follow_back_aspect,
|
||||
strip_exif: user.strip_exif
|
||||
)
|
||||
end
|
||||
|
||||
it "uses FederationEntitySerializer to serialize user profile" do
|
||||
expect(Export::UserSerializer).to serialize_association(:profile)
|
||||
.with_serializer(FederationEntitySerializer)
|
||||
.with_object(user.profile)
|
||||
serializer.associations
|
||||
end
|
||||
|
||||
it "uses AspectSerializer for array serializing contact_groups" do
|
||||
DataGenerator.create(user, %i[first_aspect work_aspect])
|
||||
expect(Export::UserSerializer).to serialize_association(:contact_groups)
|
||||
.with_each_serializer(Export::AspectSerializer)
|
||||
.with_objects([user.aspects.first, user.aspects.second])
|
||||
serializer.associations
|
||||
end
|
||||
|
||||
it "uses ContactSerializer for array serializing contacts" do
|
||||
DataGenerator.create(user, %i[mutual_friend mutual_friend])
|
||||
expect(Export::UserSerializer).to serialize_association(:contacts)
|
||||
.with_each_serializer(Export::ContactSerializer)
|
||||
.with_objects([user.contacts.first, user.contacts.second])
|
||||
serializer.associations
|
||||
end
|
||||
|
||||
it "uses OwnPostSerializer for array serializing posts" do
|
||||
DataGenerator.create(user, %i[public_status_message private_status_message])
|
||||
expect(Export::UserSerializer).to serialize_association(:posts)
|
||||
.with_each_serializer(Export::OwnPostSerializer)
|
||||
.with_objects([user.posts.first, user.posts.second])
|
||||
serializer.associations
|
||||
end
|
||||
|
||||
it "serializes followed tags" do
|
||||
DataGenerator.create(user, %i[tag_following tag_following])
|
||||
expect(Export::UserSerializer).to serialize_association(:followed_tags)
|
||||
.with_objects([user.followed_tags.first.name, user.followed_tags.second.name])
|
||||
serializer.associations
|
||||
end
|
||||
|
||||
it "uses OwnRelayablesSerializer for array serializing relayables" do
|
||||
DataGenerator.create(user, :activity)
|
||||
|
||||
objects = %i[comments likes poll_participations].map do |association|
|
||||
user.person.send(association).first
|
||||
end
|
||||
|
||||
expect(Export::UserSerializer).to serialize_association(:relayables)
|
||||
.with_each_serializer(Export::OwnRelayablesSerializer)
|
||||
.with_objects(objects)
|
||||
serializer.associations
|
||||
end
|
||||
|
||||
it "serializes post subscriptions" do
|
||||
DataGenerator.create(user, %i[participation participation])
|
||||
subscriptions = user.person.participations.map do |participation|
|
||||
participation.target.guid
|
||||
end
|
||||
expect(Export::UserSerializer).to serialize_association(:post_subscriptions)
|
||||
.with_objects(subscriptions)
|
||||
serializer.associations
|
||||
end
|
||||
end
|
||||
17
spec/serializers/federation_entity_serializer_spec.rb
Normal file
17
spec/serializers/federation_entity_serializer_spec.rb
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
describe FederationEntitySerializer do
|
||||
class TestEntity < DiasporaFederation::Entity
|
||||
property :test, :string
|
||||
end
|
||||
|
||||
let(:object) { double }
|
||||
|
||||
before do
|
||||
# Mock a builder for a TestEntity that we define for this test
|
||||
allow(Diaspora::Federation::Mappings).to receive(:builder_for).with(object.class).and_return(:test_entity)
|
||||
allow(Diaspora::Federation::Entities).to receive(:test_entity).with(object) {
|
||||
TestEntity.new(test: "asdf")
|
||||
}
|
||||
end
|
||||
|
||||
it_behaves_like "a federation entity serializer"
|
||||
end
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
describe Export::PostSerializer do
|
||||
let(:post) { create(:status_message_with_photo) }
|
||||
subject(:json_output) { Export::PostSerializer.new(post).to_json }
|
||||
|
||||
it { is_expected.to include %("guid":"#{post.guid}") }
|
||||
it { is_expected.to include %("text":"#{post.text}") }
|
||||
it { is_expected.to include %("public":#{post.public}) }
|
||||
it { is_expected.to include %("diaspora_handle":"#{post.diaspora_handle}") }
|
||||
it { is_expected.to include %("type":"#{post.type}") }
|
||||
it { is_expected.to include %("likes_count":#{post.likes_count}) }
|
||||
it { is_expected.to include %("comments_count":#{post.comments_count}) }
|
||||
it { is_expected.to include %("reshares_count":#{post.reshares_count}) }
|
||||
it { is_expected.to include %("created_at":"#{post.created_at.to_s[0, 4]}) }
|
||||
it { is_expected.to include %("created_at":"#{post.created_at.strftime('%FT%T.%LZ')}) }
|
||||
end
|
||||
33
spec/serializers/serializer_post_processing_spec.rb
Normal file
33
spec/serializers/serializer_post_processing_spec.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
describe SerializerPostProcessing do
|
||||
describe "#modify_serializable_object" do
|
||||
it "allows to modify serializable object of ActiveModel::Serializer ancestor" do
|
||||
class TestSerializer < ActiveModel::Serializer
|
||||
include SerializerPostProcessing
|
||||
|
||||
def modify_serializable_object(*)
|
||||
{
|
||||
custom_key: "custom_value"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
serializer = TestSerializer.new({}, root: false)
|
||||
expect(serializer).to receive(:modify_serializable_object).and_call_original
|
||||
expect(serializer.to_json).to eq("{\"custom_key\":\"custom_value\"}")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#except" do
|
||||
it "allows to except a key from attributes" do
|
||||
class TestSerializer2 < ActiveModel::Serializer
|
||||
include SerializerPostProcessing
|
||||
|
||||
attributes :key_to_exclude
|
||||
end
|
||||
|
||||
serializer = TestSerializer2.new({}, root: false)
|
||||
serializer.except = [:key_to_exclude]
|
||||
expect(serializer.to_json).to eq("{}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2,32 +2,27 @@
|
|||
# licensed under the Affero General Public License version 3 or later. See
|
||||
# the COPYRIGHT file.
|
||||
|
||||
shared_examples_for 'it removes the person associations' do
|
||||
it "removes all of the person's posts" do
|
||||
expect(Post.where(:author_id => @person.id).count).to eq(0)
|
||||
end
|
||||
shared_examples_for "it removes the person associations" do
|
||||
RSpec::Matchers.define_negated_matcher :remain, :change
|
||||
|
||||
it 'deletes all person contacts' do
|
||||
expect(Contact.where(:person_id => @person.id)).to be_empty
|
||||
end
|
||||
|
||||
it 'deletes all mentions' do
|
||||
expect(@person.mentions).to be_empty
|
||||
end
|
||||
|
||||
it "removes all of the person's photos" do
|
||||
expect(Photo.where(:author_id => @person.id)).to be_empty
|
||||
end
|
||||
|
||||
it 'sets the person object as closed and the profile is cleared' do
|
||||
expect(@person.reload.closed_account).to be true
|
||||
|
||||
expect(@person.profile.reload.first_name).to be_blank
|
||||
expect(@person.profile.reload.last_name).to be_blank
|
||||
end
|
||||
|
||||
it 'deletes only the converersation visibility for the deleted user' do
|
||||
expect(ConversationVisibility.where(:person_id => alice.person.id)).not_to be_empty
|
||||
expect(ConversationVisibility.where(:person_id => @person.id)).to be_empty
|
||||
it "removes all of the person associations" do
|
||||
expect {
|
||||
account_removal_method
|
||||
}.to change(nil, "posts empty?") { Post.where(author_id: person.id).empty? }.to(be_truthy)
|
||||
.and(change(nil, "contacts empty?") { Contact.where(person_id: person.id).empty? }.to(be_truthy))
|
||||
.and(change(nil, "mentions empty?") { person.mentions.empty? }.to(be_truthy))
|
||||
.and(change(nil, "photos empty?") { Photo.where(author_id: person.id).empty? }.to(be_truthy))
|
||||
.and(change(nil, "participations empty?") { Participation.where(author_id: person.id).empty? }.to(be_truthy))
|
||||
.and(change(nil, "roles empty?") { Role.where(person_id: person.id).empty? }.to(be_truthy))
|
||||
.and(change(person, :closed_account).to(be_truthy))
|
||||
.and(change(nil, "first name is blank?") { person.profile.first_name.blank? }.to(be_truthy))
|
||||
.and(change(nil, "last name is blank?") { person.profile.last_name.blank? }.to(be_truthy))
|
||||
.and(change(nil, "conversation visibilities empty?") {
|
||||
ConversationVisibility.where(person_id: person.id).empty?
|
||||
}.to(be_truthy))
|
||||
.and(remain(nil, "conversations empty?") { Conversation.where(author: person).empty? }.from(be_falsey))
|
||||
.and(remain(nil, "conversation visibilities of other participants empty?") {
|
||||
ConversationVisibility.where(conversation: Conversation.where(author: person)).empty?
|
||||
}.from(be_falsey))
|
||||
end
|
||||
end
|
||||
|
|
|
|||
14
spec/shared_behaviors/federation_entity_serializer.rb
Normal file
14
spec/shared_behaviors/federation_entity_serializer.rb
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
shared_examples_for "a federation entity serializer" do
|
||||
describe "#to_json" do
|
||||
it "contains JSON serialized entity object" do
|
||||
entity = nil
|
||||
expect(Diaspora::Federation::Entities).to receive(:build)
|
||||
.with(object)
|
||||
.and_wrap_original do |original, object, &block|
|
||||
entity = original.call(object, &block)
|
||||
end
|
||||
json = described_class.new(object, root: false).to_json
|
||||
expect(json).to include_json(entity.to_json)
|
||||
end
|
||||
end
|
||||
end
|
||||
18
spec/shared_behaviors/shareable.rb
Normal file
18
spec/shared_behaviors/shareable.rb
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
shared_examples_for "a shareable" do
|
||||
describe "#subscribed_pods_uris" do
|
||||
let(:pods) { Array.new(3) { FactoryGirl.create(:pod) } }
|
||||
let(:subscribers) {
|
||||
pods.map {|pod|
|
||||
FactoryGirl.create(:person, pod: pod)
|
||||
}
|
||||
}
|
||||
let(:pods_uris) {
|
||||
pods.map {|pod| pod.url_to("") }
|
||||
}
|
||||
|
||||
it "builds pod list basing on subscribers" do
|
||||
expect(object).to receive(:subscribers).and_return(subscribers)
|
||||
expect(object.subscribed_pods_uris).to match_array(pods_uris)
|
||||
end
|
||||
end
|
||||
end
|
||||
57
spec/spec/data_generator_spec.rb
Normal file
57
spec/spec/data_generator_spec.rb
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
RSpec::Matchers.define :have_subscribers do
|
||||
match do |posts|
|
||||
posts.map(&:subscribers).delete_if(&:empty?).any?
|
||||
end
|
||||
end
|
||||
|
||||
# verifications of data generation (protect us from possible false positives in case of poor data preset)
|
||||
describe DataGenerator do
|
||||
let(:user) { FactoryGirl.create(:user) }
|
||||
let(:generator) { DataGenerator.new(user) }
|
||||
|
||||
describe "#generic_user_data" do
|
||||
it "creates different data for user" do
|
||||
generator.generic_user_data
|
||||
expect(user.aspects).not_to be_empty
|
||||
expect(Post.subscribed_by(user)).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe "#status_messages_flavours" do
|
||||
let(:user) { FactoryGirl.create(:user_with_aspect) }
|
||||
|
||||
it "creates posts of different types" do
|
||||
expect(generator).to receive(:status_message_with_activity).and_call_original
|
||||
generator.status_messages_flavours
|
||||
expect(user.posts).not_to be_empty
|
||||
expect(user.posts.where(public: true)).to have_subscribers
|
||||
expect(user.posts.where(public: false)).to have_subscribers
|
||||
end
|
||||
end
|
||||
|
||||
describe "#status_message_with_activity" do
|
||||
it "creates a status message where presented all possible types of activity" do
|
||||
status_message = generator.status_message_with_activity
|
||||
expect(status_message.likes).not_to be_empty
|
||||
expect(status_message.comments).not_to be_empty
|
||||
expect(status_message.poll_participations).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe "#activity" do
|
||||
it "creates activity of different kinds" do
|
||||
generator.activity
|
||||
expect(user.posts.reshares).not_to be_empty
|
||||
expect(user.person.likes).not_to be_empty
|
||||
expect(user.person.comments).not_to be_empty
|
||||
expect(user.person.poll_participations).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe "#status_message_with_subscriber" do
|
||||
it "creates a status message with a subscriber" do
|
||||
subscriber, status_message = DataGenerator.create(user, :status_message_with_subscriber)
|
||||
expect(status_message.subscribers).to eq([subscriber.person])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2,23 +2,23 @@
|
|||
# licensed under the Affero General Public License version 3 or later. See
|
||||
# the COPYRIGHT file.
|
||||
|
||||
describe 'making sure the spec runner works' do
|
||||
it 'factory creates a user with a person saved' do
|
||||
describe "making sure the spec runner works" do
|
||||
it "factory creates a user with a person saved" do
|
||||
user = FactoryGirl.create(:user)
|
||||
loaded_user = User.find(user.id)
|
||||
expect(loaded_user.person.owner_id).to eq(user.id)
|
||||
end
|
||||
|
||||
describe 'fixtures' do
|
||||
it 'loads fixtures' do
|
||||
describe "fixtures" do
|
||||
it "loads fixtures" do
|
||||
expect(User.count).not_to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#connect_users" do
|
||||
before do
|
||||
@user1 = User.where(:username => 'alice').first
|
||||
@user2 = User.where(:username => 'eve').first
|
||||
@user1 = User.where(username: "alice").first
|
||||
@user2 = User.where(username: "eve").first
|
||||
|
||||
@aspect1 = @user1.aspects.first
|
||||
@aspect2 = @user2.aspects.first
|
||||
|
|
@ -26,7 +26,7 @@ describe 'making sure the spec runner works' do
|
|||
connect_users(@user1, @aspect1, @user2, @aspect2)
|
||||
end
|
||||
|
||||
it 'connects the first user to the second' do
|
||||
it "connects the first user to the second" do
|
||||
contact = @user1.contact_for @user2.person
|
||||
expect(contact).not_to be_nil
|
||||
expect(@user1.contacts.reload.include?(contact)).to be true
|
||||
|
|
@ -34,7 +34,7 @@ describe 'making sure the spec runner works' do
|
|||
expect(contact.aspects.include?(@aspect1)).to be true
|
||||
end
|
||||
|
||||
it 'connects the second user to the first' do
|
||||
it "connects the second user to the first" do
|
||||
contact = @user2.contact_for @user1.person
|
||||
expect(contact).not_to be_nil
|
||||
expect(@user2.contacts.reload.include?(contact)).to be true
|
||||
|
|
@ -42,8 +42,8 @@ describe 'making sure the spec runner works' do
|
|||
expect(contact.aspects.include?(@aspect2)).to be true
|
||||
end
|
||||
|
||||
it 'allows posting after running' do
|
||||
message = @user1.post(:status_message, :text => "Connection!", :to => @aspect1.id)
|
||||
it "allows posting after running" do
|
||||
message = @user1.post(:status_message, text: "Connection!", to: @aspect1.id)
|
||||
expect(@user2.reload.visible_shareables(Post)).to include message
|
||||
end
|
||||
end
|
||||
|
|
@ -71,14 +71,18 @@ describe 'making sure the spec runner works' do
|
|||
describe "#post" do
|
||||
it "creates a notification with a mention" do
|
||||
expect {
|
||||
alice.post(:status_message, :text => "@{Bob Grimn; #{bob.person.diaspora_handle}} you are silly", :to => alice.aspects.find_by_name('generic'))
|
||||
alice.post(
|
||||
:status_message,
|
||||
text: "@{Bob Grimn; #{bob.person.diaspora_handle}} you are silly",
|
||||
to: alice.aspects.find_by(name: "generic")
|
||||
)
|
||||
}.to change(Notification, :count).by(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#create_conversation_with_message" do
|
||||
it 'creates a conversation and a message' do
|
||||
conversation = create_conversation_with_message(alice, bob.person, "Subject", "Hey Bob")
|
||||
it "creates a conversation and a message" do
|
||||
conversation = create_conversation_with_message(alice.person, bob.person, "Subject", "Hey Bob")
|
||||
|
||||
expect(conversation.participants).to eq([alice.person, bob.person])
|
||||
expect(conversation.subject).to eq("Subject")
|
||||
|
|
@ -15,6 +15,7 @@ require "webmock/rspec"
|
|||
require "factory_girl"
|
||||
require "sidekiq/testing"
|
||||
require "shoulda/matchers"
|
||||
require "diaspora_federation/schemas"
|
||||
|
||||
include HelperMethods
|
||||
|
||||
|
|
@ -133,6 +134,16 @@ RSpec.configure do |config|
|
|||
end
|
||||
|
||||
config.include FactoryGirl::Syntax::Methods
|
||||
|
||||
config.include JSON::SchemaMatchers
|
||||
config.json_schemas[:archive_schema] = "lib/schemas/archive-format.json"
|
||||
|
||||
JSON::Validator.add_schema(
|
||||
JSON::Schema.new(
|
||||
DiasporaFederation::Schemas.federation_entities,
|
||||
Addressable::URI.parse(DiasporaFederation::Schemas::FEDERATION_ENTITIES_URI)
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
Shoulda::Matchers.configure do |config|
|
||||
|
|
|
|||
183
spec/support/data_generator.rb
Normal file
183
spec/support/data_generator.rb
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
# This is a helper class for tests that is capable of generating different sets of data, which are possibly
|
||||
# interrelated.
|
||||
class DataGenerator
|
||||
def person
|
||||
@person || user.person
|
||||
end
|
||||
|
||||
def user
|
||||
@user || person.owner
|
||||
end
|
||||
|
||||
def initialize(user_or_person)
|
||||
if user_or_person.is_a? User
|
||||
@user = user_or_person
|
||||
elsif user_or_person.is_a? Person
|
||||
@person = user_or_person
|
||||
else
|
||||
raise ArgumentError
|
||||
end
|
||||
end
|
||||
|
||||
def self.create(user_or_person, type)
|
||||
generator = new(user_or_person)
|
||||
if type.is_a? Symbol
|
||||
generator.send(type)
|
||||
elsif type.is_a? Array
|
||||
type.map {|type|
|
||||
generator.send(type)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def generic_user_data
|
||||
preferences
|
||||
notifications
|
||||
blocks
|
||||
service
|
||||
private_post_as_receipient
|
||||
tag_following
|
||||
generic_person_data
|
||||
end
|
||||
|
||||
def generic_person_data
|
||||
private_status_message
|
||||
mention
|
||||
photo
|
||||
conversations
|
||||
role
|
||||
participation
|
||||
end
|
||||
|
||||
def preferences
|
||||
%w[mentioned liked reshared].each do |pref|
|
||||
user.user_preferences.create!(email_type: pref)
|
||||
end
|
||||
end
|
||||
|
||||
def notifications
|
||||
FactoryGirl.create(:notification, recipient: user)
|
||||
end
|
||||
|
||||
def conversations
|
||||
a_friend = person.contacts.first.user.person
|
||||
create_conversation_with_message(a_friend, person, "Subject", "Hey #{person.name}")
|
||||
create_conversation_with_message(person, a_friend, "Subject", "Hey #{a_friend.name}")
|
||||
end
|
||||
|
||||
def blocks
|
||||
user.blocks.create!(person: eve.person)
|
||||
eve.blocks.create!(person: person)
|
||||
end
|
||||
|
||||
def service
|
||||
FactoryGirl.create(:service, user: user)
|
||||
end
|
||||
|
||||
def private_post_as_receipient
|
||||
friend = mutual_friend
|
||||
friend.post(
|
||||
:status_message,
|
||||
text: text_mentioning(user),
|
||||
to: friend.aspects.first
|
||||
)
|
||||
end
|
||||
|
||||
def tag_following
|
||||
TagFollowing.create!(tag: random_tag, user: user)
|
||||
end
|
||||
|
||||
def random_tag
|
||||
ActsAsTaggableOn::Tag.create!(name: "partytimeexcellent#{r_str}")
|
||||
end
|
||||
|
||||
def mutual_friend
|
||||
FactoryGirl.create(:user_with_aspect).tap {|friend|
|
||||
connect_users(user, first_aspect, friend, friend.aspects.first)
|
||||
}
|
||||
end
|
||||
|
||||
def first_aspect
|
||||
user.aspects.first || FactoryGirl.create(:aspect, user: user)
|
||||
end
|
||||
|
||||
def public_status_message
|
||||
FactoryGirl.create(:status_message, author: person, public: true)
|
||||
end
|
||||
|
||||
def private_status_message
|
||||
post = FactoryGirl.create(:status_message, author: person)
|
||||
|
||||
person.contacts.each do |contact|
|
||||
ShareVisibility.create!(user_id: contact.user_id, shareable: post)
|
||||
end
|
||||
end
|
||||
|
||||
%i[photo participation status_message_with_location status_message_with_poll status_message_with_photo
|
||||
status_message status_message_in_aspect reshare like comment poll_participation].each do |factory|
|
||||
define_method factory do
|
||||
FactoryGirl.create(factory, author: person)
|
||||
end
|
||||
end
|
||||
|
||||
alias subscription participation
|
||||
|
||||
%i[mention role].each do |factory|
|
||||
define_method factory do
|
||||
FactoryGirl.create(factory, person: person)
|
||||
end
|
||||
end
|
||||
|
||||
def status_message_with_activity
|
||||
status_message_with_poll.tap {|post|
|
||||
mutual_friend.like!(post)
|
||||
mutual_friend.comment!(post, "1")
|
||||
mutual_friend.participate_in_poll!(post, post.poll.poll_answers.first)
|
||||
}
|
||||
end
|
||||
|
||||
def status_message_with_comment
|
||||
post = status_message_in_aspect
|
||||
[post, mutual_friend.comment!(post, "some text")]
|
||||
end
|
||||
|
||||
def status_message_with_like
|
||||
post = status_message_in_aspect
|
||||
[post, mutual_friend.like!(post)]
|
||||
end
|
||||
|
||||
def status_message_with_poll_participation
|
||||
post = status_message_with_poll
|
||||
[
|
||||
post,
|
||||
mutual_friend.participate_in_poll!(post, post.poll.poll_answers.first)
|
||||
]
|
||||
end
|
||||
|
||||
def activity
|
||||
reshare
|
||||
like
|
||||
comment
|
||||
poll_participation
|
||||
end
|
||||
|
||||
def work_aspect
|
||||
user.aspects.create(name: "Work", contacts_visible: false)
|
||||
end
|
||||
|
||||
def status_messages_flavours
|
||||
public_status_message
|
||||
status_message_with_location
|
||||
status_message_with_activity
|
||||
status_message_with_photo
|
||||
status_message_with_poll
|
||||
status_message_in_aspect
|
||||
end
|
||||
|
||||
def status_message_with_subscriber
|
||||
[
|
||||
mutual_friend,
|
||||
status_message_in_aspect
|
||||
]
|
||||
end
|
||||
end
|
||||
79
spec/support/serializer_matchers.rb
Normal file
79
spec/support/serializer_matchers.rb
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# This file contains custom RSpec matchers for AMS.
|
||||
# NOTE: It was developed for AMS v0.9 and the API might be changed in future, so that should be examined when moving
|
||||
# between stable versions of AMS (e.g. 0.9 to 0.10, or 0.9 to a possible 1.0).
|
||||
|
||||
# This is a matcher that tests a ActiveModel Serializer derivatives to run a serializer for
|
||||
# an association with expected properties and data.
|
||||
#
|
||||
# This matcher is expected to be used in unit testing of ActiveModel Serializer derivatives.
|
||||
#
|
||||
# It is mostly a wrapper around RSpec::Mocks::Matchers::Receive matcher with expectations based on
|
||||
# ActiveModel::Serializer internal API knowledge.
|
||||
# NOTE: this matcher uses knowledge of AMS internals
|
||||
RSpec::Matchers.define :serialize_association do |association_name|
|
||||
match do |root_serializer_class|
|
||||
association = fetch_association(root_serializer_class, association_name)
|
||||
execute_receive_matcher_with(association)
|
||||
end
|
||||
|
||||
# Sets expectation for a specific serializer class to be used for has_one association serialization
|
||||
chain :with_serializer, :association_serializer_class
|
||||
|
||||
# Sets expectation for a specific serializer class to be user for has_many association serialization
|
||||
chain :with_each_serializer, :each_serializer_class
|
||||
|
||||
# Sets expectation for actual data to be passed to the serializer of the association
|
||||
chain :with_object, :association_object
|
||||
alias_method :with_objects, :with_object
|
||||
|
||||
private
|
||||
|
||||
# subject is what comes from the expect method argument in usual notation.
|
||||
# So this method is equivalent of calling `expect(subject).to receive(:build_serializer)` but valid within a custom
|
||||
# RSpec matcher's match method.
|
||||
def execute_receive_matcher_with(subject)
|
||||
receive_matcher.matches?(subject)
|
||||
end
|
||||
|
||||
def receive_matcher
|
||||
receive(:build_serializer).and_wrap_original do |original, object, options, &block|
|
||||
with_object_expectation(object)
|
||||
|
||||
original.call(object, options, &block).tap do |serializer|
|
||||
expect(serializer).to be_an_instance_of(serializer_class) unless serializer_class.nil?
|
||||
expect(serializer).to serialize_each_with(each_serializer_class) unless each_serializer_class.nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def with_object_expectation(object)
|
||||
if association_object.is_a?(Array)
|
||||
expect(object).to match_array(association_object)
|
||||
elsif !association_object.nil?
|
||||
expect(object).to eq(association_object)
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_association(serializer_class, association_name)
|
||||
serializer_class._associations[association_name]
|
||||
end
|
||||
|
||||
def serializer_class
|
||||
@serializer_class ||= pick_serializer_class
|
||||
end
|
||||
|
||||
def pick_serializer_class
|
||||
return association_serializer_class unless association_serializer_class.nil?
|
||||
return ActiveModel::ArraySerializer unless each_serializer_class.nil?
|
||||
end
|
||||
end
|
||||
|
||||
# This serializer tests that ActiveModel::ArraySerializer uses specific serializer for each member object of the array.
|
||||
# We could also set a mock expectation on each serializer, but it maybe overly complicated for our present
|
||||
# requirements.
|
||||
# NOTE: this matcher uses knowledge of AMS internals
|
||||
RSpec::Matchers.define :serialize_each_with do |expected|
|
||||
match do |actual|
|
||||
actual.is_a?(ActiveModel::ArraySerializer) && actual.instance_variable_get("@each_serializer") == expected
|
||||
end
|
||||
end
|
||||
Loading…
Reference in a new issue