diff options
author | Thibaut Girka <thib@sitedethib.com> | 2020-05-10 15:15:39 +0200 |
---|---|---|
committer | Thibaut Girka <thib@sitedethib.com> | 2020-05-10 16:19:56 +0200 |
commit | 4a70792b4a8393a0cfd83a7e70f72179899111fa (patch) | |
tree | d340885b5dfb0f990c81b1a29f529a258c49d591 /spec | |
parent | c6ff4c634caf718adf7280e04909c091d15add1d (diff) | |
parent | 4b766f984689311523b89e1b68d2a11dff3fc396 (diff) |
Merge branch 'master' into glitch-soc/merge-upstream
Conflicts: - `Gemfile.lock`: Not a real conflict, just a glitch-soc-only dependency too close to a dependency that got updated upstream. Updated as well. - `app/models/status.rb`: Not a real conflict, just a change too close to glitch-soc-changed code for optionally showing boosts in public timelines. Applied upstream changes. - `app/views/layouts/application.html.haml`: Upstream a new, static CSS file, conflict due to glitch-soc's theming system, include the file regardless of the theme. - `config/initializers/content_security_policy.rb`: Upstream dropped 'unsafe-inline' from the 'style-src' directive, but both files are very different. Removed 'unsafe-inline' as well.
Diffstat (limited to 'spec')
-rw-r--r-- | spec/controllers/accounts_controller_spec.rb | 632 | ||||
-rw-r--r-- | spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb | 23 | ||||
-rw-r--r-- | spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb | 23 | ||||
-rw-r--r-- | spec/controllers/remote_follow_controller_spec.rb | 10 | ||||
-rw-r--r-- | spec/controllers/settings/identity_proofs_controller_spec.rb | 20 | ||||
-rw-r--r-- | spec/lib/rss/serializer_spec.rb | 63 | ||||
-rw-r--r-- | spec/models/relationship_filter_spec.rb | 37 | ||||
-rw-r--r-- | spec/models/status_spec.rb | 56 |
8 files changed, 763 insertions, 101 deletions
diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb index 3d2a0665d..bd36f5494 100644 --- a/spec/controllers/accounts_controller_spec.rb +++ b/spec/controllers/accounts_controller_spec.rb @@ -3,108 +3,608 @@ require 'rails_helper' RSpec.describe AccountsController, type: :controller do render_views - let(:alice) { Fabricate(:account, username: 'alice', user: Fabricate(:user)) } - let(:eve) { Fabricate(:user) } + let(:account) { Fabricate(:user).account } describe 'GET #show' do - let!(:status1) { Status.create!(account: alice, text: 'Hello world') } - let!(:status2) { Status.create!(account: alice, text: 'Boop', thread: status1) } - let!(:status3) { Status.create!(account: alice, text: 'Picture!') } - let!(:status4) { Status.create!(account: alice, text: 'Mentioning @alice') } - let!(:status5) { Status.create!(account: alice, text: 'Kitsune') } - let!(:status6) { Status.create!(account: alice, text: 'Neko') } - let!(:status7) { Status.create!(account: alice, text: 'Tanuki') } - - let!(:status_pin1) { StatusPin.create!(account: alice, status: status5, created_at: 5.days.ago) } - let!(:status_pin2) { StatusPin.create!(account: alice, status: status6, created_at: 2.years.ago) } - let!(:status_pin3) { StatusPin.create!(account: alice, status: status7, created_at: 10.minutes.ago) } + let(:format) { 'html' } + + let!(:status) { Fabricate(:status, account: account) } + let!(:status_reply) { Fabricate(:status, account: account, thread: Fabricate(:status)) } + let!(:status_self_reply) { Fabricate(:status, account: account, thread: status) } + let!(:status_media) { Fabricate(:status, account: account) } + let!(:status_pinned) { Fabricate(:status, account: account) } + let!(:status_private) { Fabricate(:status, account: account, visibility: :private) } + let!(:status_direct) { Fabricate(:status, account: account, visibility: :direct) } + let!(:status_reblog) { Fabricate(:status, account: account, reblog: Fabricate(:status)) } before do - alice.block!(eve.account) - status3.media_attachments.create!(account: alice, file: fixture_file_upload('files/attachment.jpg', 'image/jpeg')) + status_media.media_attachments << Fabricate(:media_attachment, account: account, type: :image) + account.pinned_statuses << status_pinned end - shared_examples 'responses' do - before do - sign_in(current_user) if defined? current_user - get :show, params: { - username: alice.username, - max_id: (max_id if defined? max_id), - since_id: (since_id if defined? since_id), - current_user: (current_user if defined? current_user), - }, format: format + shared_examples 'preliminary checks' do + context 'when account is not approved' do + before do + account.user.update(approved: false) + end + + it 'returns http not found' do + get :show, params: { username: account.username, format: format } + expect(response).to have_http_status(404) + end + end + + context 'when account is suspended' do + before do + account.suspend! + end + + it 'returns http gone' do + get :show, params: { username: account.username, format: format } + expect(response).to have_http_status(410) + end end + end + + context 'as HTML' do + let(:format) { 'html' } + + it_behaves_like 'preliminary checks' + + shared_examples 'common response characteristics' do + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'returns Link header' do + expect(response.headers['Link'].to_s).to include ActivityPub::TagManager.instance.uri_for(account) + end - it 'assigns @account' do - expect(assigns(:account)).to eq alice + it 'renders show template' do + expect(response).to render_template(:show) + end end - it 'returns http success' do - expect(response).to have_http_status(200) + context do + before do + get :show, params: { username: account.username, format: format } + end + + it_behaves_like 'common response characteristics' + + it 'renders public status' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status)) + end + + it 'renders self-reply' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_self_reply)) + end + + it 'renders status with media' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_media)) + end + + it 'renders reblog' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog)) + end + + it 'renders pinned status' do + expect(response.body).to include(I18n.t('stream_entries.pinned')) + end + + it 'does not render private status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private)) + end + + it 'does not render direct status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct)) + end + + it 'does not render reply to someone else' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reply)) + end end - it 'returns correct format' do - expect(response.content_type).to eq content_type + context 'when signed-in' do + let(:user) { Fabricate(:user) } + + before do + sign_in(user) + end + + context 'when user follows account' do + before do + user.account.follow!(account) + get :show, params: { username: account.username, format: format } + end + + it 'does not render private status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private)) + end + end + + context 'when user is blocked' do + before do + account.block!(user.account) + get :show, params: { username: account.username, format: format } + end + + it 'renders unavailable message' do + expect(response.body).to include(I18n.t('accounts.unavailable')) + end + + it 'does not render public status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status)) + end + + it 'does not render self-reply' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_self_reply)) + end + + it 'does not render status with media' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_media)) + end + + it 'does not render reblog' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog)) + end + + it 'does not render pinned status' do + expect(response.body).to_not include(I18n.t('stream_entries.pinned')) + end + + it 'does not render private status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private)) + end + + it 'does not render direct status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct)) + end + + it 'does not render reply to someone else' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reply)) + end + end + end + + context 'with replies' do + before do + allow(controller).to receive(:replies_requested?).and_return(true) + get :show, params: { username: account.username, format: format } + end + + it_behaves_like 'common response characteristics' + + it 'renders public status' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status)) + end + + it 'renders self-reply' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_self_reply)) + end + + it 'renders status with media' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_media)) + end + + it 'renders reblog' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog)) + end + + it 'does not render pinned status' do + expect(response.body).to_not include(I18n.t('stream_entries.pinned')) + end + + it 'does not render private status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private)) + end + + it 'does not render direct status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct)) + end + + it 'renders reply to someone else' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_reply)) + end + end + + context 'with media' do + before do + allow(controller).to receive(:media_requested?).and_return(true) + get :show, params: { username: account.username, format: format } + end + + it_behaves_like 'common response characteristics' + + it 'does not render public status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status)) + end + + it 'does not render self-reply' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_self_reply)) + end + + it 'renders status with media' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_media)) + end + + it 'does not render reblog' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog)) + end + + it 'does not render pinned status' do + expect(response.body).to_not include(I18n.t('stream_entries.pinned')) + end + + it 'does not render private status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private)) + end + + it 'does not render direct status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct)) + end + + it 'does not render reply to someone else' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reply)) + end + end + + context 'with tag' do + let(:tag) { Fabricate(:tag) } + + let!(:status_tag) { Fabricate(:status, account: account) } + + before do + allow(controller).to receive(:tag_requested?).and_return(true) + status_tag.tags << tag + get :show, params: { username: account.username, format: format, tag: tag.to_param } + end + + it_behaves_like 'common response characteristics' + + it 'does not render public status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status)) + end + + it 'does not render self-reply' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_self_reply)) + end + + it 'does not render status with media' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_media)) + end + + it 'does not render reblog' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog)) + end + + it 'does not render pinned status' do + expect(response.body).to_not include(I18n.t('stream_entries.pinned')) + end + + it 'does not render private status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private)) + end + + it 'does not render direct status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct)) + end + + it 'does not render reply to someone else' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reply)) + end + + it 'renders status with tag' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_tag)) + end end end - context 'activitystreams2' do + context 'as JSON' do + let(:authorized_fetch_mode) { false } let(:format) { 'json' } - let(:content_type) { 'application/activity+json' } - include_examples 'responses' - end + before do + allow(controller).to receive(:authorized_fetch_mode?).and_return(authorized_fetch_mode) + end + + it_behaves_like 'preliminary checks' + + context do + before do + get :show, params: { username: account.username, format: format } + end + + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'returns application/activity+json' do + expect(response.content_type).to eq 'application/activity+json' + end + + it 'returns public Cache-Control header' do + expect(response.headers['Cache-Control']).to include 'public' + end + + it 'renders account' do + json = body_as_json + expect(json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary) + end + + context 'in authorized fetch mode' do + let(:authorized_fetch_mode) { true } - context 'html' do - let(:format) { nil } - let(:content_type) { 'text/html' } + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'returns application/activity+json' do + expect(response.content_type).to eq 'application/activity+json' + end + + it 'returns public Cache-Control header' do + expect(response.headers['Cache-Control']).to include 'public' + end + + it 'returns Vary header with Signature' do + expect(response.headers['Vary']).to include 'Signature' + end - shared_examples 'responsed statuses' do - it 'assigns @pinned_statuses' do - pinned_statuses = assigns(:pinned_statuses).to_a - expect(pinned_statuses.size).to eq expected_pinned_statuses.size - pinned_statuses.each.zip(expected_pinned_statuses.each) do |pinned_status, expected_pinned_status| - expect(pinned_status).to eq expected_pinned_status + it 'renders bare minimum account' do + json = body_as_json + expect(json).to include(:id, :type, :preferredUsername, :inbox, :publicKey) + expect(json).to_not include(:name, :summary) end end + end + + context 'when signed in' do + let(:user) { Fabricate(:user) } + + before do + sign_in(user) + get :show, params: { username: account.username, format: format } + end + + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'returns application/activity+json' do + expect(response.content_type).to eq 'application/activity+json' + end + + it 'returns public Cache-Control header' do + expect(response.headers['Cache-Control']).to include 'public' + end + + it 'renders account' do + json = body_as_json + expect(json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary) + end + end + + context 'with signature' do + let(:remote_account) { Fabricate(:account, domain: 'example.com') } + + before do + allow(controller).to receive(:signed_request_account).and_return(remote_account) + get :show, params: { username: account.username, format: format } + end + + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'returns application/activity+json' do + expect(response.content_type).to eq 'application/activity+json' + end + + it 'returns public Cache-Control header' do + expect(response.headers['Cache-Control']).to include 'public' + end + + it 'renders account' do + json = body_as_json + expect(json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary) + end + + context 'in authorized fetch mode' do + let(:authorized_fetch_mode) { true } - it 'assigns @statuses' do - statuses = assigns(:statuses).to_a - expect(statuses.size).to eq expected_statuses.size - statuses.each.zip(expected_statuses.each) do |status, expected_status| - expect(status).to eq expected_status + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'returns application/activity+json' do + expect(response.content_type).to eq 'application/activity+json' + end + + it 'returns private Cache-Control header' do + expect(response.headers['Cache-Control']).to include 'private' + end + + it 'returns Vary header with Signature' do + expect(response.headers['Vary']).to include 'Signature' + end + + it 'renders account' do + json = body_as_json + expect(json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary) end end end + end + + context 'as RSS' do + let(:format) { 'rss' } - include_examples 'responses' + it_behaves_like 'preliminary checks' + + shared_examples 'common response characteristics' do + it 'returns http success' do + expect(response).to have_http_status(200) + end - context 'with anonymous visitor' do - context 'without since_id nor max_id' do - let(:expected_statuses) { [status7, status6, status5, status4, status3, status2, status1] } - let(:expected_pinned_statuses) { [status7, status5, status6] } + it 'returns public Cache-Control header' do + expect(response.headers['Cache-Control']).to include 'public' + end + end - include_examples 'responsed statuses' + context do + before do + get :show, params: { username: account.username, format: format } end - context 'with since_id nor max_id' do - let(:max_id) { status4.id } - let(:since_id) { status1.id } - let(:expected_statuses) { [status3, status2] } - let(:expected_pinned_statuses) { [] } + it_behaves_like 'common response characteristics' + + it 'renders public status' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status)) + end - include_examples 'responsed statuses' + it 'renders self-reply' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_self_reply)) + end + + it 'renders status with media' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_media)) + end + + it 'does not render reblog' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog)) + end + + it 'does not render private status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private)) + end + + it 'does not render direct status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct)) + end + + it 'does not render reply to someone else' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reply)) + end + end + + context 'with replies' do + before do + allow(controller).to receive(:replies_requested?).and_return(true) + get :show, params: { username: account.username, format: format } + end + + it_behaves_like 'common response characteristics' + + it 'renders public status' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status)) + end + + it 'renders self-reply' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_self_reply)) + end + + it 'renders status with media' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_media)) + end + + it 'does not render reblog' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog)) + end + + it 'does not render private status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private)) + end + + it 'does not render direct status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct)) + end + + it 'renders reply to someone else' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_reply)) end end - context 'with blocked visitor' do - let(:current_user) { eve } + context 'with media' do + before do + allow(controller).to receive(:media_requested?).and_return(true) + get :show, params: { username: account.username, format: format } + end + + it_behaves_like 'common response characteristics' + + it 'does not render public status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status)) + end - context 'without since_id nor max_id' do - let(:expected_statuses) { [] } - let(:expected_pinned_statuses) { [] } + it 'does not render self-reply' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_self_reply)) + end + + it 'renders status with media' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_media)) + end + + it 'does not render reblog' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog)) + end + + it 'does not render private status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private)) + end + + it 'does not render direct status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct)) + end + + it 'does not render reply to someone else' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reply)) + end + end + + context 'with tag' do + let(:tag) { Fabricate(:tag) } + + let!(:status_tag) { Fabricate(:status, account: account) } + + before do + allow(controller).to receive(:tag_requested?).and_return(true) + status_tag.tags << tag + get :show, params: { username: account.username, format: format, tag: tag.to_param } + end + + it_behaves_like 'common response characteristics' + + it 'does not render public status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status)) + end + + it 'does not render self-reply' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_self_reply)) + end + + it 'does not render status with media' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_media)) + end + + it 'does not render reblog' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog)) + end + + it 'does not render private status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private)) + end + + it 'does not render direct status' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct)) + end + + it 'does not render reply to someone else' do + expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reply)) + end - include_examples 'responsed statuses' + it 'renders status with tag' do + expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_tag)) end end end diff --git a/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb b/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb index 54587187f..482a19ef2 100644 --- a/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb @@ -36,5 +36,28 @@ describe Api::V1::Accounts::FollowerAccountsController do expect(body_as_json.size).to eq 1 expect(body_as_json[0][:id]).to eq alice.id.to_s end + + context 'when requesting user is blocked' do + before do + account.block!(user.account) + end + + it 'hides results' do + get :index, params: { account_id: account.id, limit: 2 } + expect(body_as_json.size).to eq 0 + end + end + + context 'when requesting user is the account owner' do + let(:user) { Fabricate(:user, account: account) } + + it 'returns all accounts, including muted accounts' do + user.account.mute!(bob) + get :index, params: { account_id: account.id, limit: 2 } + + expect(body_as_json.size).to eq 2 + expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s]) + end + end end end diff --git a/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb b/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb index a580a7368..e35b625fe 100644 --- a/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb @@ -36,5 +36,28 @@ describe Api::V1::Accounts::FollowingAccountsController do expect(body_as_json.size).to eq 1 expect(body_as_json[0][:id]).to eq alice.id.to_s end + + context 'when requesting user is blocked' do + before do + account.block!(user.account) + end + + it 'hides results' do + get :index, params: { account_id: account.id, limit: 2 } + expect(body_as_json.size).to eq 0 + end + end + + context 'when requesting user is the account owner' do + let(:user) { Fabricate(:user, account: account) } + + it 'returns all accounts, including muted accounts' do + user.account.mute!(bob) + get :index, params: { account_id: account.id, limit: 2 } + + expect(body_as_json.size).to eq 2 + expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s]) + end + end end end diff --git a/spec/controllers/remote_follow_controller_spec.rb b/spec/controllers/remote_follow_controller_spec.rb index d79dd2949..3ef8f14d9 100644 --- a/spec/controllers/remote_follow_controller_spec.rb +++ b/spec/controllers/remote_follow_controller_spec.rb @@ -35,7 +35,7 @@ describe RemoteFollowController do context 'when webfinger values are wrong' do it 'renders new when redirect url is nil' do resource_with_nil_link = double(link: nil) - allow(Goldfinger).to receive(:finger).with('acct:user@example.com').and_return(resource_with_nil_link) + allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@example.com').and_return(resource_with_nil_link) post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@example.com' } } expect(response).to render_template(:new) @@ -45,7 +45,7 @@ describe RemoteFollowController do it 'renders new when template is nil' do link_with_nil_template = double(template: nil) resource_with_link = double(link: link_with_nil_template) - allow(Goldfinger).to receive(:finger).with('acct:user@example.com').and_return(resource_with_link) + allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@example.com').and_return(resource_with_link) post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@example.com' } } expect(response).to render_template(:new) @@ -57,7 +57,7 @@ describe RemoteFollowController do before do link_with_template = double(template: 'http://example.com/follow_me?acct={uri}') resource_with_link = double(link: link_with_template) - allow(Goldfinger).to receive(:finger).with('acct:user@example.com').and_return(resource_with_link) + allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@example.com').and_return(resource_with_link) post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@example.com' } } end @@ -79,7 +79,7 @@ describe RemoteFollowController do end it 'renders new with error when goldfinger fails' do - allow(Goldfinger).to receive(:finger).with('acct:user@example.com').and_raise(Goldfinger::Error) + allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@example.com').and_raise(Goldfinger::Error) post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@example.com' } } expect(response).to render_template(:new) @@ -87,7 +87,7 @@ describe RemoteFollowController do end it 'renders new when occur HTTP::ConnectionError' do - allow(Goldfinger).to receive(:finger).with('acct:user@unknown').and_raise(HTTP::ConnectionError) + allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@unknown').and_raise(HTTP::ConnectionError) post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@unknown' } } expect(response).to render_template(:new) diff --git a/spec/controllers/settings/identity_proofs_controller_spec.rb b/spec/controllers/settings/identity_proofs_controller_spec.rb index 261e980d4..16f236227 100644 --- a/spec/controllers/settings/identity_proofs_controller_spec.rb +++ b/spec/controllers/settings/identity_proofs_controller_spec.rb @@ -151,7 +151,7 @@ describe Settings::IdentityProofsController do @proof1 = Fabricate(:account_identity_proof, account: user.account) @proof2 = Fabricate(:account_identity_proof, account: user.account) allow_any_instance_of(AccountIdentityProof).to receive(:badge) { double(avatar_url: '', profile_url: '', proof_url: '') } - allow_any_instance_of(AccountIdentityProof).to receive(:refresh!) { } + allow_any_instance_of(AccountIdentityProof).to receive(:refresh!) {} end it 'has the first proof username on the page' do @@ -165,4 +165,22 @@ describe Settings::IdentityProofsController do end end end + + describe 'DELETE #destroy' do + before do + allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:valid?) { true } + @proof1 = Fabricate(:account_identity_proof, account: user.account) + allow_any_instance_of(AccountIdentityProof).to receive(:badge) { double(avatar_url: '', profile_url: '', proof_url: '') } + allow_any_instance_of(AccountIdentityProof).to receive(:refresh!) {} + delete :destroy, params: { id: @proof1.id } + end + + it 'redirects to :index' do + expect(response).to redirect_to settings_identity_proofs_path + end + + it 'removes the proof' do + expect(AccountIdentityProof.where(id: @proof1.id).count).to eq 0 + end + end end diff --git a/spec/lib/rss/serializer_spec.rb b/spec/lib/rss/serializer_spec.rb new file mode 100644 index 000000000..0364d13de --- /dev/null +++ b/spec/lib/rss/serializer_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe RSS::Serializer do + describe '#status_title' do + let(:text) { 'This is a toot' } + let(:spoiler) { '' } + let(:sensitive) { false } + let(:reblog) { nil } + let(:account) { Fabricate(:account) } + let(:status) { Fabricate(:status, account: account, text: text, spoiler_text: spoiler, sensitive: sensitive, reblog: reblog) } + + subject { RSS::Serializer.new.send(:status_title, status) } + + context 'if destroyed?' do + it 'returns "#{account.acct} deleted status"' do + status.destroy! + expect(subject).to eq "#{account.acct} deleted status" + end + end + + context 'on a toot with long text' do + let(:text) { "This toot's text is longer than the allowed number of characters" } + + it 'truncates toot text appropriately' do + expect(subject).to eq "#{account.acct}: “This toot's text is longer tha…”" + end + end + + context 'on a toot with long text with a newline' do + let(:text) { "This toot's text is longer\nthan the allowed number of characters" } + + it 'truncates toot text appropriately' do + expect(subject).to eq "#{account.acct}: “This toot's text is longer…”" + end + end + + context 'on a toot with a content warning' do + let(:spoiler) { 'long toot' } + + it 'displays spoiler text instead of toot content' do + expect(subject).to eq "#{account.acct}: CW “long toot”" + end + end + + context 'on a toot with sensitive media' do + let(:sensitive) { true } + + it 'displays that the media is sensitive' do + expect(subject).to eq "#{account.acct}: “This is a toot” (sensitive)" + end + end + + context 'on a reblog' do + let(:reblog) { Fabricate(:status, text: 'This is a toot') } + + it 'display that the toot is a reblog' do + expect(subject).to eq "#{account.acct} boosted #{reblog.account.acct}: “This is a toot”" + end + end + end +end diff --git a/spec/models/relationship_filter_spec.rb b/spec/models/relationship_filter_spec.rb new file mode 100644 index 000000000..7c0f37a06 --- /dev/null +++ b/spec/models/relationship_filter_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe RelationshipFilter do + let(:account) { Fabricate(:account) } + + describe '#results' do + context 'when default params are used' do + let(:subject) do + RelationshipFilter.new(account, 'order' => 'active').results + end + + before do + add_following_account_with(last_status_at: 7.days.ago) + add_following_account_with(last_status_at: 1.day.ago) + add_following_account_with(last_status_at: 3.days.ago) + end + + it 'returns followings ordered by last activity' do + expected_result = account.following.eager_load(:account_stat).reorder(nil).by_recent_status + + expect(subject).to eq expected_result + end + end + end + + def add_following_account_with(last_status_at:) + following_account = Fabricate(:account) + Fabricate(:account_stat, account: following_account, + last_status_at: last_status_at, + statuses_count: 1, + following_count: 0, + followers_count: 0) + Fabricate(:follow, account: account, target_account: following_account).account + end +end diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb index 02f533287..041021d34 100644 --- a/spec/models/status_spec.rb +++ b/spec/models/status_spec.rb @@ -82,35 +82,6 @@ RSpec.describe Status, type: :model do end end - describe '#title' do - # rubocop:disable Style/InterpolationCheck - - let(:account) { subject.account } - - context 'if destroyed?' do - it 'returns "#{account.acct} deleted status"' do - subject.destroy! - expect(subject.title).to eq "#{account.acct} deleted status" - end - end - - context 'unless destroyed?' do - context 'if reblog?' do - it 'returns "#{account.acct} shared a status by #{reblog.account.acct}"' do - reblog = subject.reblog = other - expect(subject.title).to eq "#{account.acct} shared a status by #{reblog.account.acct}" - end - end - - context 'unless reblog?' do - it 'returns "New status by #{account.acct}"' do - subject.reblog = nil - expect(subject.title).to eq "New status by #{account.acct}" - end - end - end - end - describe '#hidden?' do context 'if private_visibility?' do it 'returns true' do @@ -490,6 +461,33 @@ RSpec.describe Status, type: :model do end end + context 'with a remote_only option set' do + let!(:local_account) { Fabricate(:account, domain: nil) } + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { Fabricate(:status, account: local_account) } + let!(:remote_status) { Fabricate(:status, account: remote_account) } + + subject { Status.as_public_timeline(viewer, :remote) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'does not include local instances statuses' do + expect(subject).not_to include(local_status) + expect(subject).to include(remote_status) + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'does not include local instances statuses' do + expect(subject).not_to include(local_status) + expect(subject).to include(remote_status) + end + end + end + describe 'with an account passed in' do before do @account = Fabricate(:account) |