From c3852a8e9c0dd142a57a14fbf8d79cdb7cff5d6f Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 21 Nov 2018 16:47:00 -0500 Subject: [PATCH] Users Controller and unit tests complete --- app/controllers/api/v1/users_controller.rb | 85 +++ app/models/profile.rb | 1 + app/presenters/person_presenter.rb | 33 ++ app/presenters/photo_presenter.rb | 6 +- app/presenters/profile_presenter.rb | 34 ++ app/services/aspects_membership_service.rb | 6 + config/locales/diaspora/en.yml | 3 + config/routes.rb | 8 + spec/integration/api/users_controller_spec.rb | 486 ++++++++++++++++++ spec/presenters/person_presenter_spec.rb | 74 +++ .../aspects_membership_service_spec.rb | 13 + 11 files changed, 747 insertions(+), 2 deletions(-) create mode 100644 app/controllers/api/v1/users_controller.rb create mode 100644 spec/integration/api/users_controller_spec.rb diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb new file mode 100644 index 000000000..be05d7110 --- /dev/null +++ b/app/controllers/api/v1/users_controller.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +module Api + module V1 + class UsersController < Api::V1::BaseController + include TagsHelper + + before_action except: %i[update] do + require_access_token %w[read] + end + + before_action only: %i[update] do + require_access_token %w[write] + end + + rescue_from ActiveRecord::RecordNotFound do + render json: I18n.t("api.endpoint_errors.users.not_found"), status: :not_found + end + + def show + person = if params.has_key?(:id) + Person.find_by!(guid: params[:id]) + else + current_user.person + end + render json: PersonPresenter.new(person, current_user).profile_hash_as_api_json + end + + def update + raise RuntimeError if params.has_key?(:id) + params_to_update = profile_update_params + if params_to_update && current_user.update_profile(params_to_update) + render json: PersonPresenter.new(current_user.person, current_user).profile_hash_as_api_json + else + render json: I18n.t("api.endpoint_errors.users.cant_update"), status: :unprocessable_entity + end + rescue RuntimeError + render json: I18n.t("api.endpoint_errors.users.cant_update"), status: :unprocessable_entity + end + + def contacts + if params[:user_id] != current_user.guid + render json: I18n.t("api.endpoint_errors.users.not_found"), status: :not_found + return + end + + contacts_with_profile = AspectsMembershipService.new(current_user).all_contacts + render json: contacts_with_profile.map {|c| PersonPresenter.new(c.person).as_api_json } + end + + def photos + person = Person.find_by!(guid: params[:user_id]) + photos = Photo.visible(current_user, person, :all, Time.current) + render json: photos.map {|photo| PhotoPresenter.new(photo).as_api_json(false) } + end + + def posts + person = Person.find_by!(guid: params[:user_id]) + posts = current_user.posts_from(person) + render json: posts.map {|post| PostPresenter.new(post, current_user).as_api_response } + end + + private + + def profile_update_params + updates = params.permit(:bio, :birthday, :gender, :location, :first_name, :last_name, + :searchable, :show_profile_info, :nsfw, :tags).to_h || {} + if updates.has_key?(:show_profile_info) + updates[:public_details] = updates[:show_profile_info] + updates.delete(:show_profile_info) + end + process_tags_updates(updates) + updates + end + + def process_tags_updates(updates) + return unless params.has_key?(:tags) + raise RuntimeError if params[:tags].length > Profile::MAX_TAGS + tags = params[:tags].map {|tag| "#" + normalize_tag_name(tag) }.join(" ") + updates[:tag_string] = tags + updates.delete(:tags) + end + end + end +end diff --git a/app/models/profile.rb b/app/models/profile.rb index ad58f59b1..ea035eef9 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -5,6 +5,7 @@ # the COPYRIGHT file. class Profile < ApplicationRecord + MAX_TAGS = 5 self.include_root_in_json = false include Diaspora::Federated::Base diff --git a/app/presenters/person_presenter.rb b/app/presenters/person_presenter.rb index eb7f0a785..4227b272b 100644 --- a/app/presenters/person_presenter.rb +++ b/app/presenters/person_presenter.rb @@ -28,6 +28,20 @@ class PersonPresenter < BasePresenter ) end + def profile_hash_as_api_json + if own_profile? + ProfilePresenter.new(profile).as_self_api_json.merge(guid: guid) + else + show_detailed = @presentable.public_details? || person_is_following_current_user + ProfilePresenter.new(profile).as_other_api_json(show_detailed).merge( + guid: guid, + blocked: is_blocked?, + relationship: relationship_detailed, + aspects: aspects_detailed + ) + end + end + def as_json(_options={}) full_hash_with_profile end @@ -72,6 +86,25 @@ class PersonPresenter < BasePresenter end || :not_sharing end + def relationship_detailed + status = {receiving: false, sharing: false} + return status unless current_user + + contact = current_user_person_contact + return status unless contact + + status.keys.each do |key| + status[key] = contact.public_send("#{key}?") + end + status + end + + def aspects_detailed + return [] unless current_user_person_contact + aspects_for_person = current_user.aspects_with_person(@presentable) + aspects_for_person.map {|a| AspectPresenter.new(a).as_api_json(false) } + end + def person_is_following_current_user return false unless current_user contact = current_user_person_contact diff --git a/app/presenters/photo_presenter.rb b/app/presenters/photo_presenter.rb index 8eb6070d5..91159d1dc 100644 --- a/app/presenters/photo_presenter.rb +++ b/app/presenters/photo_presenter.rb @@ -17,8 +17,8 @@ class PhotoPresenter < BasePresenter } end - def as_api_json - { + def as_api_json(no_guid=true) + based_data = { dimensions: { height: height, width: width @@ -29,5 +29,7 @@ class PhotoPresenter < BasePresenter large: url(:scaled_full) } } + return based_data if no_guid + based_data.merge(guid: guid) end end diff --git a/app/presenters/profile_presenter.rb b/app/presenters/profile_presenter.rb index 5ee690e4c..ab269f4a5 100644 --- a/app/presenters/profile_presenter.rb +++ b/app/presenters/profile_presenter.rb @@ -24,6 +24,19 @@ class ProfilePresenter < BasePresenter } end + def as_self_api_json + base_api_json.merge(added_details_api_json).merge( + searchable: searchable, + show_profile_info: public_details, + nsfw: nsfw + ) + end + + def as_other_api_json(all_details) + return base_api_json unless all_details + base_api_json.merge(added_details_api_json) + end + def private_hash public_hash.merge( bio: bio_message.plain_text_for_json, @@ -36,4 +49,25 @@ class ProfilePresenter < BasePresenter def formatted_birthday birthday_format(birthday) if birthday end + + private + + def base_api_json + { + first_name: first_name, + last_name: last_name, + diaspora_id: diaspora_handle, + avatar: AvatarPresenter.new(@presentable).base_hash, + tags: tags.pluck(:name) + } + end + + def added_details_api_json + { + birthday: formatted_birthday, + gender: gender, + location: location_message.plain_text_for_json, + bio: bio_message.plain_text_for_json + } + end end diff --git a/app/services/aspects_membership_service.rb b/app/services/aspects_membership_service.rb index 02b0f07d4..38f026052 100644 --- a/app/services/aspects_membership_service.rb +++ b/app/services/aspects_membership_service.rb @@ -43,6 +43,12 @@ class AspectsMembershipService ).includes(person: :profile).order(order) end + def all_contacts + order = ["profiles.first_name ASC", "profiles.last_name ASC", + "profiles.diaspora_handle ASC"] + @user.contacts.includes(person: :profile).order(order) + end + private def destroy(aspect, contact) diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml index b5f736696..49376ad4c 100644 --- a/config/locales/diaspora/en.yml +++ b/config/locales/diaspora/en.yml @@ -982,6 +982,9 @@ en: failed_create: "Failed to create the post" failed_delete: "Not allowed to delete the post" cant_report: "Failed to create report on this post" + users: + cant_update: "Failed to update the user settings" + not_found: "User not found" tags: cant_process: "Failed to process the tag followings request" diff --git a/config/routes.rb b/config/routes.rb index 6dc858556..8dd8e4e96 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -240,6 +240,14 @@ Rails.application.routes.draw do resources :messages, only: %i[index create] end resources :notifications, only: %i[index show update] + + patch "user" => "users#update" + get "user" => "users#show" + resources :users, only: %i[show] do + get :contacts + get :photos + get :posts + end resources :tag_followings, only: %i[index create destroy] get "streams/activity" => "streams#activity", :as => "activity_stream" get "streams/main" => "streams#multi", :as => "stream" diff --git a/spec/integration/api/users_controller_spec.rb b/spec/integration/api/users_controller_spec.rb new file mode 100644 index 000000000..77d61e636 --- /dev/null +++ b/spec/integration/api/users_controller_spec.rb @@ -0,0 +1,486 @@ +# frozen_sTring_literal: true + +require "spec_helper" + +describe Api::V1::UsersController do + include PeopleHelper + + let(:auth) { FactoryGirl.create(:auth_with_read_and_write) } + let(:auth_read_only) { FactoryGirl.create(:auth_with_read) } + let!(:access_token) { auth.create_access_token.to_s } + let!(:access_token_read_only) { auth_read_only.create_access_token.to_s } + + describe "#show" do + context "Current User" do + it "succeeds when logged in" do + get( + api_v1_user_path, + params: {access_token: access_token} + ) + user = JSON.parse(response.body) + expect(response.status).to eq(200) + expect(user["guid"]).to eq(auth.user.guid) + confirm_self_data_format(user) + end + + it "fails if invalid token" do + get( + api_v1_user_path, + params: {access_token: "999_999_999"} + ) + expect(response.status).to eq(401) + end + end + + context "Single User" do + it "succeeds with public user not in Aspect" do + alice.profile[:public_details] = true + alice.profile.save + get( + "/api/v1/users/#{alice.guid}", + params: {access_token: access_token} + ) + user = JSON.parse(response.body) + expect(response.status).to eq(200) + expect(user["guid"]).to eq(alice.person.guid) + confirm_public_profile_hash(user) + end + + it "succeeds with in Aspect valid user" do + alice.profile[:public_details] = true + alice.profile.save + auth.user.share_with(alice.person, auth.user.aspects.first) + get( + "/api/v1/users/#{alice.guid}", + params: {access_token: access_token} + ) + user = JSON.parse(response.body) + expect(response.status).to eq(200) + expect(user["guid"]).to eq(alice.person.guid) + confirm_public_profile_hash(user) + end + + it "succeeds with limited data on non-public/not shared" do + eve.profile[:public_details] = false + eve.profile.save + get( + "/api/v1/users/#{eve.guid}", + params: {access_token: access_token} + ) + user = JSON.parse(response.body) + expect(response.status).to eq(200) + expect(user["guid"]).to eq(eve.person.guid) + confirm_private_profile_hash(user) + + eve.aspects.create(name: "new aspect") + eve.share_with(auth.user.person, eve.aspects.first) + get( + "/api/v1/users/#{eve.guid}", + params: {access_token: access_token} + ) + user = JSON.parse(response.body) + expect(response.status).to eq(200) + expect(user["guid"]).to eq(eve.person.guid) + confirm_public_profile_hash(user) + end + + it "fails if invalid token" do + get( + api_v1_user_path(alice.person.guid), + params: {access_token: "999_999_999"} + ) + expect(response.status).to eq(401) + end + + it "fails with invalid user GUID" do + get( + "/api/v1/users/999_999_999", + params: {access_token: access_token} + ) + expect(response.status).to eq(404) + expect(response.body).to eq(I18n.t("api.endpoint_errors.users.not_found")) + end + end + end + + describe "#update" do + context "Partial with all valid fields" do + it "succeeds when logged in" do + new_location = "New Location" + new_bio = "New Bio" + patch( + api_v1_user_path, + params: {location: new_location, bio: new_bio, access_token: access_token} + ) + expect(response.status).to eq(200) + user = JSON.parse(response.body) + confirm_self_data_format(user) + expect(user["bio"]).to eq(new_bio) + expect(user["location"]).to eq(new_location) + auth.user.profile.reload + expect(auth.user.profile[:location]).to eq(new_location) + expect(auth.user.profile[:bio]).to eq(new_bio) + end + end + + context "Full with all valid fields" do + it "succeeds" do + new_bio = "new bio" + new_birthday = Time.current + 8_640_000 + new_gender = "ask1" + new_location = "new location" + new_first_name = "new first" + new_last_name = "new last" + new_searchable = !auth.user.profile[:searchable] + new_show_profile_info = !auth.user.profile[:public_details] + new_nsfw = !auth.user.profile[:nsfw] + new_tags = %w[tag1 tag2] + patch( + api_v1_user_path, + params: { + bio: new_bio, + location: new_location, + gender: new_gender, + birthday: new_birthday, + first_name: new_first_name, + last_name: new_last_name, + searchable: new_searchable, + show_profile_info: new_show_profile_info, + nsfw: new_nsfw, + tags: new_tags, + access_token: access_token + } + ) + expect(response.status).to eq(200) + user = JSON.parse(response.body) + confirm_self_data_format(user) + expect(user["bio"]).to eq(new_bio) + expect(user["birthday"]).to eq(birthday_format(new_birthday)) + expect(user["location"]).to eq(new_location) + expect(user["gender"]).to eq(new_gender) + expect(user["first_name"]).to eq(new_first_name) + expect(user["last_name"]).to eq(new_last_name) + expect(user["searchable"]).to eq(new_searchable) + expect(user["show_profile_info"]).to eq(new_show_profile_info) + expect(user["nsfw"]).to eq(new_nsfw) + expect(user["tags"]).to eq(new_tags) + auth.user.profile.reload + expect(auth.user.profile[:location]).to eq(new_location) + expect(auth.user.profile[:bio]).to eq(new_bio) + expect(birthday_format(auth.user.profile[:birthday])).to eq(birthday_format(new_birthday)) + expect(auth.user.profile[:gender]).to eq(new_gender) + expect(auth.user.profile[:first_name]).to eq(new_first_name) + expect(auth.user.profile[:last_name]).to eq(new_last_name) + expect(auth.user.profile[:searchable]).to eq(new_searchable) + expect(auth.user.profile[:public_details]).to eq(new_show_profile_info) + expect(auth.user.profile[:nsfw]).to eq(new_nsfw) + expect(user["tags"]).to eq(new_tags) + end + end + + context "fails" do + it "skips invalid fields" do + new_bio = "new bio" + patch( + api_v1_user_path, + params: { + bio: new_bio, + no_idea_what_field_this_is: "some value", + access_token: access_token + } + ) + expect(response.status).to eq(200) + user = JSON.parse(response.body) + expect(user["bio"]).to eq(new_bio) + expect(user.has_key?("no_idea_what_field_this_is")).to be_falsey + end + + it "fails to update guid" do + new_bio = "new bio" + original_guid = auth.user.guid.to_s + patch( + api_v1_user_path, + params: { + bio: new_bio, + guid: "999_999_999", + access_token: access_token + } + ) + expect(response.status).to eq(200) + user = JSON.parse(response.body) + expect(user["bio"]).to eq(new_bio) + expect(user["guid"]).to eq(original_guid) + end + + it "fails if invalid token" do + patch( + api_v1_user_path, + params: {location: "New Location", bio: "New Bio", access_token: "999_999_999"} + ) + expect(response.status).to eq(401) + end + + it "fails if read only token" do + patch( + api_v1_user_path, + params: {location: "New Location", bio: "New Bio", access_token: access_token_read_only} + ) + expect(response.status).to eq(403) + end + end + end + + describe "#contacts" do + it "succeeds when logged in and ask for own" do + get( + api_v1_user_contacts_path(auth.user.guid), + params: {access_token: access_token} + ) + expect(response.status).to eq(200) + contacts = JSON.parse(response.body) + expect(contacts.length).to eq(0) + + auth.user.share_with(alice.person, auth.user.aspects.first) + get( + api_v1_user_contacts_path(auth.user.guid), + params: {access_token: access_token} + ) + expect(response.status).to eq(200) + contacts = JSON.parse(response.body) + expect(contacts.length).to eq(1) + confirm_person_format(contacts[0], alice) + end + + it "fails with invalid GUID" do + get( + api_v1_user_contacts_path("999_999_999"), + params: {access_token: access_token} + ) + expect(response.status).to eq(404) + expect(response.body).to eq(I18n.t("api.endpoint_errors.users.not_found")) + end + + it "fails with other user's GUID" do + get( + api_v1_user_contacts_path(alice.guid), + params: {access_token: access_token} + ) + expect(response.status).to eq(404) + expect(response.body).to eq(I18n.t("api.endpoint_errors.users.not_found")) + end + + it "fails if invalid token" do + get( + api_v1_user_contacts_path(alice.guid), + params: {access_token: "999_999_999"} + ) + expect(response.status).to eq(401) + end + end + + describe "#photos" do + before do + alice_private_spec = alice.aspects.create(name: "private aspect") + alice.share_with(eve.person, alice_private_spec) + alice.share_with(auth.user.person, alice.aspects.first) + + auth.user.post(:photo, pending: false, user_file: File.open(photo_fixture_name), to: "all") + auth.user.post(:photo, pending: false, user_file: File.open(photo_fixture_name), to: "all") + @public_photo1 = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name), to: "all") + @public_photo2 = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name), to: "all") + @shared_photo1 = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name), + to: alice.aspects.first.id) + @private_photo1 = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name), + to: alice_private_spec.id) + end + + context "logged in" do + it "returns only visible photos of other user" do + get( + api_v1_user_photos_path(alice.guid), + params: {access_token: access_token} + ) + expect(response.status).to eq(200) + photos = JSON.parse(response.body) + expect(photos.length).to eq(3) + guids = photos.map {|photo| photo["guid"] } + expect(guids).to include(@public_photo1.guid, @public_photo2.guid, @shared_photo1.guid) + expect(guids).not_to include(@private_photo1.guid) + confirm_photos(photos) + end + + it "returns logged in user's photos" do + get( + api_v1_user_photos_path(auth.user.guid), + params: {access_token: access_token} + ) + expect(response.status).to eq(200) + photos = JSON.parse(response.body) + expect(photos.length).to eq(2) + end + end + + it "fails with invalid GUID" do + get( + api_v1_user_photos_path("999_999_999"), + params: {access_token: access_token} + ) + expect(response.status).to eq(404) + expect(response.body).to eq(I18n.t("api.endpoint_errors.users.not_found")) + end + + it "fails if invalid token" do + get( + api_v1_user_photos_path(alice.guid), + params: {access_token: "999_999_999"} + ) + expect(response.status).to eq(401) + end + end + + describe "#posts" do + before do + alice_private_spec = alice.aspects.create(name: "private aspect") + alice.share_with(eve.person, alice_private_spec) + alice.share_with(auth.user.person, alice.aspects.first) + + auth.user.post(:status_message, text: "auth user message1", public: true, to: "all") + auth.user.post(:status_message, text: "auth user message2", public: true, to: "all") + @public_post1 = alice.post(:status_message, text: "alice public message1", public: true, to: "all") + @public_post2 = alice.post(:status_message, text: "alice public message2", public: true, to: "all") + @shared_post1 = alice.post(:status_message, text: "alice limited to auth user message", + public: false, to: alice.aspects.first.id) + @private_post1 = alice.post(:status_message, text: "alice limited hidden from auth user message", + public: false, to: alice_private_spec.id) + end + + context "logged in" do + it "returns only visible posts of other user" do + get( + api_v1_user_posts_path(alice.guid), + params: {access_token: access_token} + ) + expect(response.status).to eq(200) + posts = JSON.parse(response.body) + expect(posts.length).to eq(3) + guids = posts.map {|post| post["guid"] } + expect(guids).to include(@public_post1.guid, @public_post2.guid, @shared_post1.guid) + expect(guids).not_to include(@private_post1.guid) + post = posts.select {|p| p["guid"] == @public_post1.guid } + confirm_post_format(post[0], alice, @public_post1) + end + + it "returns logged in user's photos" do + get( + api_v1_user_posts_path(auth.user.guid), + params: {access_token: access_token} + ) + expect(response.status).to eq(200) + posts = JSON.parse(response.body) + expect(posts.length).to eq(2) + end + end + + it "fails with invalid GUID" do + get( + api_v1_user_posts_path("999_999_999"), + params: {access_token: access_token} + ) + expect(response.status).to eq(404) + expect(response.body).to eq(I18n.t("api.endpoint_errors.users.not_found")) + end + + it "fails if invalid token" do + get( + api_v1_user_posts_path(alice.person.guid), + params: {access_token: "999_999_999"} + ) + expect(response.status).to eq(401) + end + end + + def confirm_self_data_format(json) + confirm_common_profile_elements(json) + confirm_profile_details(json) + expect(json).to have_key("searchable") + expect(json).to have_key("show_profile_info") + end + + def confirm_public_profile_hash(json) + confirm_common_profile_elements(json) + confirm_profile_details(json) + expect(json).to have_key("blocked") + expect(json).to have_key("relationship") + expect(json).to have_key("aspects") + end + + # rubocop:disable Metrics/AbcSize + def confirm_private_profile_hash(json) + confirm_common_profile_elements(json) + expect(json).to have_key("blocked") + expect(json).to have_key("relationship") + expect(json).to have_key("aspects") + expect(json).not_to have_key("birthday") + expect(json).not_to have_key("gender") + expect(json).not_to have_key("location") + expect(json).not_to have_key("bio") + end + + def confirm_common_profile_elements(json) + expect(json).to have_key("guid") + expect(json).to have_key("diaspora_id") + expect(json).to have_key("first_name") + expect(json).to have_key("last_name") + expect(json).to have_key("avatar") + expect(json).to have_key("tags") + end + + def confirm_profile_details(json) + expect(json).to have_key("birthday") + expect(json).to have_key("gender") + expect(json).to have_key("location") + expect(json).to have_key("bio") + end + + def confirm_post_format(post, user, reference_post) + confirm_post_top_level(post, reference_post) + confirm_person_format(post["author"], user) + confirm_interactions(post["interaction_counters"], reference_post) + end + + def confirm_post_top_level(post, reference_post) + expect(post["guid"]).to eq(reference_post.guid) + expect(post.has_key?("created_at")).to be_truthy + expect(post["created_at"]).not_to be_nil + expect(post["title"]).to eq(reference_post.message.title) + expect(post["body"]).to eq(reference_post.message.plain_text_for_json) + expect(post["post_type"]).to eq(reference_post.post_type) + expect(post["provider_display_name"]).to eq(reference_post.provider_display_name) + expect(post["public"]).to eq(reference_post.public) + expect(post["nsfw"]).to eq(reference_post.nsfw) + end + + def confirm_interactions(interactions, reference_post) + expect(interactions["comments"]).to eq(reference_post.comments_count) + expect(interactions["likes"]).to eq(reference_post.likes_count) + expect(interactions["reshares"]).to eq(reference_post.reshares_count) + end + + def confirm_person_format(post_person, user) + expect(post_person["guid"]).to eq(user.guid) + expect(post_person["diaspora_id"]).to eq(user.diaspora_handle) + expect(post_person["name"]).to eq(user.name) + expect(post_person["avatar"]).to eq(user.profile.image_url) + end + + def confirm_photos(photos) + photos.each do |photo| + expect(photo.has_key?("guid")).to be_truthy + expect(photo["dimensions"].has_key?("height")).to be_truthy + expect(photo["sizes"]["small"]).to be_truthy + expect(photo["sizes"]["medium"]).to be_truthy + expect(photo["sizes"]["large"]).to be_truthy + end + end + # rubocop:enable Metrics/AbcSize +end diff --git a/spec/presenters/person_presenter_spec.rb b/spec/presenters/person_presenter_spec.rb index e2a6da75a..db3277ca4 100644 --- a/spec/presenters/person_presenter_spec.rb +++ b/spec/presenters/person_presenter_spec.rb @@ -142,4 +142,78 @@ describe PersonPresenter do expect(presenter.hovercard[:contact]).to have_key(:aspect_memberships) end end + + describe "#profile_hash_as_api_json" do + let(:current_user) { FactoryGirl.create(:user) } + + it "contains internal profile if self" do + profile_hash = PersonPresenter.new(current_user.person, current_user).profile_hash_as_api_json + expect(profile_hash[:diaspora_id]).to eq(current_user.profile.diaspora_handle) + confirm_self_data_format(profile_hash) + end + + it "contains full data only if private profile is Sharing to me" do + alice.profile[:public_details] = false + profile_hash = PersonPresenter.new(alice.person, current_user).profile_hash_as_api_json + expect(profile_hash[:diaspora_id]).to eq(alice.profile.diaspora_handle) + confirm_private_profile_hash(profile_hash) + + alice.share_with(current_user.person, alice.aspects.first) + profile_hash = PersonPresenter.new(alice.person, current_user).profile_hash_as_api_json + expect(profile_hash[:diaspora_id]).to eq(alice.profile.diaspora_handle) + confirm_public_profile_hash(profile_hash) + end + + it "contains full profile data for public profile" do + alice.profile[:public_details] = true + profile_hash = PersonPresenter.new(alice.person, current_user).profile_hash_as_api_json + expect(profile_hash[:diaspora_id]).to eq(alice.profile.diaspora_handle) + confirm_public_profile_hash(profile_hash) + end + end + + def confirm_self_data_format(profile_hash) + confirm_common_profile_elements(profile_hash) + confirm_profile_details(profile_hash) + expect(profile_hash).to have_key(:searchable) + expect(profile_hash).to have_key(:show_profile_info) + expect(profile_hash).to have_key(:nsfw) + end + + def confirm_public_profile_hash(profile_hash) + confirm_common_profile_elements(profile_hash) + confirm_profile_details(profile_hash) + expect(profile_hash).to have_key(:blocked) + expect(profile_hash).to have_key(:relationship) + expect(profile_hash).to have_key(:aspects) + end + + # rubocop:disable Metrics/AbcSize + def confirm_private_profile_hash(profile_hash) + confirm_common_profile_elements(profile_hash) + expect(profile_hash).to have_key(:blocked) + expect(profile_hash).to have_key(:relationship) + expect(profile_hash).to have_key(:aspects) + expect(profile_hash).not_to have_key(:birthday) + expect(profile_hash).not_to have_key(:gender) + expect(profile_hash).not_to have_key(:location) + expect(profile_hash).not_to have_key(:bio) + end + # rubocop:enable Metrics/AbcSize + + def confirm_common_profile_elements(profile_hash) + expect(profile_hash).to have_key(:guid) + expect(profile_hash).to have_key(:diaspora_id) + expect(profile_hash).to have_key(:first_name) + expect(profile_hash).to have_key(:last_name) + expect(profile_hash).to have_key(:avatar) + expect(profile_hash).to have_key(:tags) + end + + def confirm_profile_details(profile_hash) + expect(profile_hash).to have_key(:birthday) + expect(profile_hash).to have_key(:gender) + expect(profile_hash).to have_key(:location) + expect(profile_hash).to have_key(:bio) + end end diff --git a/spec/services/aspects_membership_service_spec.rb b/spec/services/aspects_membership_service_spec.rb index 9657af0e9..d291b7a7b 100644 --- a/spec/services/aspects_membership_service_spec.rb +++ b/spec/services/aspects_membership_service_spec.rb @@ -131,6 +131,19 @@ describe AspectsMembershipService do end end + describe "#all_contacts" do + before do + aspects_membership_service.create(@alice_aspect2.id, bob.person.id) + aspects_membership_service.create(@alice_aspect2.id, eve.person.id) + @alice_aspect3 = alice.aspects.create(name: "empty aspect") + end + + it "returns all user's contacts" do + contacts = aspects_membership_service.all_contacts + expect(contacts.length).to eq(2) + end + end + def aspects_membership_service(user=alice) AspectsMembershipService.new(user) end