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.
This commit is contained in:
@@ -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(:format) { 'html' }
|
||||
|
||||
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!(: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
|
||||
|
||||
it 'assigns @account' do
|
||||
expect(assigns(:account)).to eq alice
|
||||
end
|
||||
context 'when account is suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns correct format' do
|
||||
expect(response.content_type).to eq content_type
|
||||
it 'returns http gone' do
|
||||
get :show, params: { username: account.username, format: format }
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'activitystreams2' do
|
||||
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 'renders show template' do
|
||||
expect(response).to render_template(:show)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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 'as JSON' do
|
||||
let(:authorized_fetch_mode) { false }
|
||||
let(:format) { 'json' }
|
||||
let(:content_type) { 'application/activity+json' }
|
||||
|
||||
include_examples 'responses'
|
||||
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 }
|
||||
|
||||
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
|
||||
|
||||
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 '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 'html' do
|
||||
let(:format) { nil }
|
||||
let(:content_type) { 'text/html' }
|
||||
context 'as RSS' do
|
||||
let(:format) { 'rss' }
|
||||
|
||||
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
|
||||
end
|
||||
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 '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
|
||||
end
|
||||
it 'returns public Cache-Control header' do
|
||||
expect(response.headers['Cache-Control']).to include 'public'
|
||||
end
|
||||
end
|
||||
|
||||
include_examples 'responses'
|
||||
|
||||
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] }
|
||||
|
||||
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'
|
||||
|
||||
include_examples 'responsed statuses'
|
||||
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 '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 blocked visitor' do
|
||||
let(:current_user) { eve }
|
||||
context 'with replies' do
|
||||
before do
|
||||
allow(controller).to receive(:replies_requested?).and_return(true)
|
||||
get :show, params: { username: account.username, format: format }
|
||||
end
|
||||
|
||||
context 'without since_id nor max_id' do
|
||||
let(:expected_statuses) { [] }
|
||||
let(:expected_pinned_statuses) { [] }
|
||||
it_behaves_like 'common response characteristics'
|
||||
|
||||
include_examples 'responsed statuses'
|
||||
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 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 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
|
||||
|
||||
it 'renders status with tag' do
|
||||
expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_tag))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
63
spec/lib/rss/serializer_spec.rb
Normal file
63
spec/lib/rss/serializer_spec.rb
Normal file
@@ -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
|
37
spec/models/relationship_filter_spec.rb
Normal file
37
spec/models/relationship_filter_spec.rb
Normal file
@@ -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
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user