Merge tag 'v3.0.0' into hometown-dev
This commit is contained in:
@ -48,37 +48,6 @@ RSpec.describe AccountsController, type: :controller do
|
||||
end
|
||||
end
|
||||
|
||||
context 'atom' do
|
||||
let(:format) { 'atom' }
|
||||
let(:content_type) { 'application/atom+xml' }
|
||||
|
||||
shared_examples 'responsed streams' do
|
||||
it 'assigns @entries' do
|
||||
entries = assigns(:entries).to_a
|
||||
expect(entries.size).to eq expected_statuses.size
|
||||
entries.each.zip(expected_statuses.each) do |entry, expected_status|
|
||||
expect(entry.status).to eq expected_status
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
include_examples 'responses'
|
||||
|
||||
context 'without max_id nor since_id' do
|
||||
let(:expected_statuses) { [status7, status6, status5, status4, status3, status2, status1] }
|
||||
|
||||
include_examples 'responsed streams'
|
||||
end
|
||||
|
||||
context 'with max_id and since_id' do
|
||||
let(:max_id) { status4.stream_entry.id }
|
||||
let(:since_id) { status1.stream_entry.id }
|
||||
let(:expected_statuses) { [status3, status2] }
|
||||
|
||||
include_examples 'responsed streams'
|
||||
end
|
||||
end
|
||||
|
||||
context 'activitystreams2' do
|
||||
let(:format) { 'json' }
|
||||
let(:content_type) { 'application/activity+json' }
|
||||
|
@ -4,7 +4,7 @@ require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::InboxesController, type: :controller do
|
||||
describe 'POST #create' do
|
||||
context 'if signed_request_account' do
|
||||
context 'with signed_request_account' do
|
||||
it 'returns 202' do
|
||||
allow(controller).to receive(:signed_request_account) do
|
||||
Fabricate(:account)
|
||||
@ -15,7 +15,7 @@ RSpec.describe ActivityPub::InboxesController, type: :controller do
|
||||
end
|
||||
end
|
||||
|
||||
context 'not signed_request_account' do
|
||||
context 'without signed_request_account' do
|
||||
it 'returns 401' do
|
||||
allow(controller).to receive(:signed_request_account) do
|
||||
false
|
||||
|
@ -75,44 +75,6 @@ RSpec.describe Admin::AccountsController, type: :controller do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #subscribe' do
|
||||
subject { post :subscribe, params: { id: account.id } }
|
||||
|
||||
let(:current_user) { Fabricate(:user, admin: admin) }
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
context 'when user is admin' do
|
||||
let(:admin) { true }
|
||||
|
||||
it { is_expected.to redirect_to admin_account_path(account.id) }
|
||||
end
|
||||
|
||||
context 'when user is not admin' do
|
||||
let(:admin) { false }
|
||||
|
||||
it { is_expected.to have_http_status :forbidden }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #unsubscribe' do
|
||||
subject { post :unsubscribe, params: { id: account.id } }
|
||||
|
||||
let(:current_user) { Fabricate(:user, admin: admin) }
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
context 'when user is admin' do
|
||||
let(:admin) { true }
|
||||
|
||||
it { is_expected.to redirect_to admin_account_path(account.id) }
|
||||
end
|
||||
|
||||
context 'when user is not admin' do
|
||||
let(:admin) { false }
|
||||
|
||||
it { is_expected.to have_http_status :forbidden }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #memorialize' do
|
||||
subject { post :memorialize, params: { id: account.id } }
|
||||
|
||||
|
@ -52,64 +52,4 @@ describe Admin::CustomEmojisController do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test') }
|
||||
let(:image) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'files', 'emojo.png'), 'image/png') }
|
||||
|
||||
before do
|
||||
put :update, params: { id: custom_emoji.id, custom_emoji: params }
|
||||
end
|
||||
|
||||
context 'when parameter is valid' do
|
||||
let(:params) { { shortcode: 'updated', image: image } }
|
||||
|
||||
it 'succeeds in updating custom emoji' do
|
||||
expect(flash[:notice]).to eq I18n.t('admin.custom_emojis.updated_msg')
|
||||
expect(custom_emoji.reload).to have_attributes(shortcode: 'updated')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when parameter is invalid' do
|
||||
let(:params) { { shortcode: 'u', image: image } }
|
||||
|
||||
it 'fails to update custom emoji' do
|
||||
expect(flash[:alert]).to eq I18n.t('admin.custom_emojis.update_failed_msg')
|
||||
expect(custom_emoji.reload).to have_attributes(shortcode: 'test')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #copy' do
|
||||
subject { post :copy, params: { id: custom_emoji.id } }
|
||||
|
||||
let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test') }
|
||||
|
||||
it 'copies custom emoji' do
|
||||
expect { subject }.to change { CustomEmoji.where(shortcode: 'test').count }.by(1)
|
||||
expect(flash[:notice]).to eq I18n.t('admin.custom_emojis.copied_msg')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #enable' do
|
||||
let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test', disabled: true) }
|
||||
|
||||
before { post :enable, params: { id: custom_emoji.id } }
|
||||
|
||||
it 'enables custom emoji' do
|
||||
expect(response).to redirect_to admin_custom_emojis_path
|
||||
expect(custom_emoji.reload).to have_attributes(disabled: false)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #disable' do
|
||||
let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test', disabled: false) }
|
||||
|
||||
before { post :disable, params: { id: custom_emoji.id } }
|
||||
|
||||
it 'enables custom emoji' do
|
||||
expect(response).to redirect_to admin_custom_emojis_path
|
||||
expect(custom_emoji.reload).to have_attributes(disabled: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -47,7 +47,7 @@ describe Admin::ReportedStatusesController do
|
||||
it 'removes a status' do
|
||||
allow(RemovalWorker).to receive(:perform_async)
|
||||
subject.call
|
||||
expect(RemovalWorker).to have_received(:perform_async).with(status_ids.first)
|
||||
expect(RemovalWorker).to have_received(:perform_async).with(status_ids.first, immediate: true)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,7 +65,7 @@ describe Admin::StatusesController do
|
||||
it 'removes a status' do
|
||||
allow(RemovalWorker).to receive(:perform_async)
|
||||
subject.call
|
||||
expect(RemovalWorker).to have_received(:perform_async).with(status_ids.first)
|
||||
expect(RemovalWorker).to have_received(:perform_async).with(status_ids.first, immediate: true)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,32 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::SubscriptionsController, type: :controller do
|
||||
render_views
|
||||
|
||||
describe 'GET #index' do
|
||||
around do |example|
|
||||
default_per_page = Subscription.default_per_page
|
||||
Subscription.paginates_per 1
|
||||
example.run
|
||||
Subscription.paginates_per default_per_page
|
||||
end
|
||||
|
||||
before do
|
||||
sign_in Fabricate(:user, admin: true), scope: :user
|
||||
end
|
||||
|
||||
it 'renders subscriptions' do
|
||||
Fabricate(:subscription)
|
||||
specified = Fabricate(:subscription)
|
||||
|
||||
get :index
|
||||
|
||||
subscriptions = assigns(:subscriptions)
|
||||
expect(subscriptions.count).to eq 1
|
||||
expect(subscriptions[0]).to eq specified
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
@ -10,62 +10,14 @@ RSpec.describe Admin::TagsController, type: :controller do
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
before do
|
||||
account_tag_stat = Fabricate(:tag).account_tag_stat
|
||||
account_tag_stat.update(hidden: hidden, accounts_count: 1)
|
||||
get :index, params: { hidden: hidden }
|
||||
end
|
||||
|
||||
context 'with hidden tags' do
|
||||
let(:hidden) { true }
|
||||
|
||||
it 'returns status 200' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without hidden tags' do
|
||||
let(:hidden) { false }
|
||||
|
||||
it 'returns status 200' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #hide' do
|
||||
let(:tag) { Fabricate(:tag) }
|
||||
let!(:tag) { Fabricate(:tag) }
|
||||
|
||||
before do
|
||||
tag.account_tag_stat.update(hidden: false)
|
||||
post :hide, params: { id: tag.id }
|
||||
get :index
|
||||
end
|
||||
|
||||
it 'hides tag' do
|
||||
tag.reload
|
||||
expect(tag).to be_hidden
|
||||
end
|
||||
|
||||
it 'redirects to admin_tags_path' do
|
||||
expect(response).to redirect_to(admin_tags_path(controller.instance_variable_get(:@filter_params)))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #unhide' do
|
||||
let(:tag) { Fabricate(:tag) }
|
||||
|
||||
before do
|
||||
tag.account_tag_stat.update(hidden: true)
|
||||
post :unhide, params: { id: tag.id }
|
||||
end
|
||||
|
||||
it 'unhides tag' do
|
||||
tag.reload
|
||||
expect(tag).not_to be_hidden
|
||||
end
|
||||
|
||||
it 'redirects to admin_tags_path' do
|
||||
expect(response).to redirect_to(admin_tags_path(controller.instance_variable_get(:@filter_params)))
|
||||
it 'returns status 200' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -15,7 +15,7 @@ describe Api::BaseController do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Forgery protection' do
|
||||
describe 'forgery protection' do
|
||||
before do
|
||||
routes.draw { post 'success' => 'api/base#success' }
|
||||
end
|
||||
@ -27,7 +27,45 @@ describe Api::BaseController do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Error handling' do
|
||||
describe 'non-functional accounts handling' do
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
|
||||
|
||||
controller do
|
||||
before_action :require_user!
|
||||
end
|
||||
|
||||
before do
|
||||
routes.draw { post 'success' => 'api/base#success' }
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
it 'returns http forbidden for unconfirmed accounts' do
|
||||
user.update(confirmed_at: nil)
|
||||
post 'success'
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
|
||||
it 'returns http forbidden for pending accounts' do
|
||||
user.update(approved: false)
|
||||
post 'success'
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
|
||||
it 'returns http forbidden for disabled accounts' do
|
||||
user.update(disabled: true)
|
||||
post 'success'
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
|
||||
it 'returns http forbidden for suspended accounts' do
|
||||
user.account.suspend!
|
||||
post 'success'
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'error handling' do
|
||||
ERRORS_WITH_CODES = {
|
||||
ActiveRecord::RecordInvalid => 422,
|
||||
Mastodon::ValidationError => 422,
|
||||
|
@ -9,7 +9,7 @@ RSpec.describe Api::OEmbedController, type: :controller do
|
||||
describe 'GET #show' do
|
||||
before do
|
||||
request.host = Rails.configuration.x.local_domain
|
||||
get :show, params: { url: account_stream_entry_url(alice, status.stream_entry) }, format: :json
|
||||
get :show, params: { url: short_account_status_url(alice, status) }, format: :json
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
|
@ -1,59 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::PushController, type: :controller do
|
||||
describe 'POST #update' do
|
||||
context 'with hub.mode=subscribe' do
|
||||
it 'creates a subscription' do
|
||||
service = double(call: ['', 202])
|
||||
allow(Pubsubhubbub::SubscribeService).to receive(:new).and_return(service)
|
||||
account = Fabricate(:account)
|
||||
account_topic_url = "https://#{Rails.configuration.x.local_domain}/users/#{account.username}.atom"
|
||||
post :update, params: {
|
||||
'hub.mode' => 'subscribe',
|
||||
'hub.topic' => account_topic_url,
|
||||
'hub.callback' => 'https://callback.host/api',
|
||||
'hub.lease_seconds' => '3600',
|
||||
'hub.secret' => 'as1234df',
|
||||
}
|
||||
|
||||
expect(service).to have_received(:call).with(
|
||||
account,
|
||||
'https://callback.host/api',
|
||||
'as1234df',
|
||||
'3600',
|
||||
nil
|
||||
)
|
||||
expect(response).to have_http_status(202)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with hub.mode=unsubscribe' do
|
||||
it 'unsubscribes the account' do
|
||||
service = double(call: ['', 202])
|
||||
allow(Pubsubhubbub::UnsubscribeService).to receive(:new).and_return(service)
|
||||
account = Fabricate(:account)
|
||||
account_topic_url = "https://#{Rails.configuration.x.local_domain}/users/#{account.username}.atom"
|
||||
post :update, params: {
|
||||
'hub.mode' => 'unsubscribe',
|
||||
'hub.topic' => account_topic_url,
|
||||
'hub.callback' => 'https://callback.host/api',
|
||||
}
|
||||
|
||||
expect(service).to have_received(:call).with(
|
||||
account,
|
||||
'https://callback.host/api',
|
||||
)
|
||||
expect(response).to have_http_status(202)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with unknown mode' do
|
||||
it 'returns an unknown mode error' do
|
||||
post :update, params: { 'hub.mode' => 'fake' }
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
expect(response.body).to match(/Unknown mode/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,65 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::SalmonController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:account) { Fabricate(:user, account: Fabricate(:account, username: 'catsrgr8')).account }
|
||||
|
||||
before do
|
||||
stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
|
||||
stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no").to_return(request_fixture('webfinger.txt'))
|
||||
stub_request(:get, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
|
||||
stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
|
||||
end
|
||||
|
||||
describe 'POST #update' do
|
||||
context 'with valid post data' do
|
||||
before do
|
||||
post :update, params: { id: account.id }, body: File.read(Rails.root.join('spec', 'fixtures', 'salmon', 'mention.xml'))
|
||||
end
|
||||
|
||||
it 'contains XML in the request body' do
|
||||
expect(request.body.read).to be_a String
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(202)
|
||||
end
|
||||
|
||||
it 'creates remote account' do
|
||||
expect(Account.find_by(username: 'gargron', domain: 'quitter.no')).to_not be_nil
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
expect(Status.find_by(uri: 'tag:quitter.no,2016-03-20:noticeId=1276923:objectType=note')).to_not be_nil
|
||||
end
|
||||
|
||||
it 'creates mention for target account' do
|
||||
expect(account.mentions.count).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'with empty post data' do
|
||||
before do
|
||||
post :update, params: { id: account.id }, body: ''
|
||||
end
|
||||
|
||||
it 'returns http client error' do
|
||||
expect(response).to have_http_status(400)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid post data' do
|
||||
before do
|
||||
service = double(call: false)
|
||||
allow(VerifySalmonService).to receive(:new).and_return(service)
|
||||
|
||||
post :update, params: { id: account.id }, body: File.read(Rails.root.join('spec', 'fixtures', 'salmon', 'mention.xml'))
|
||||
end
|
||||
|
||||
it 'returns http client error' do
|
||||
expect(response).to have_http_status(401)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,68 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::SubscriptionsController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:account) { Fabricate(:account, username: 'gargron', domain: 'quitter.no', remote_url: 'topic_url', secret: 'abc') }
|
||||
|
||||
describe 'GET #show' do
|
||||
context 'with valid subscription' do
|
||||
before do
|
||||
get :show, params: { :id => account.id, 'hub.topic' => 'topic_url', 'hub.challenge' => '456', 'hub.lease_seconds' => "#{86400 * 30}" }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'echoes back the challenge' do
|
||||
expect(response.body).to match '456'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid subscription' do
|
||||
before do
|
||||
expect_any_instance_of(Account).to receive_message_chain(:subscription, :valid?).and_return(false)
|
||||
get :show, params: { :id => account.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #update' do
|
||||
let(:feed) { File.read(Rails.root.join('spec', 'fixtures', 'push', 'feed.atom')) }
|
||||
|
||||
before do
|
||||
stub_request(:post, "https://quitter.no/main/push/hub").to_return(:status => 200, :body => "", :headers => {})
|
||||
stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
|
||||
stub_request(:get, "https://quitter.no/notice/1269244").to_return(status: 404)
|
||||
stub_request(:get, "https://quitter.no/notice/1265331").to_return(status: 404)
|
||||
stub_request(:get, "https://community.highlandarrow.com/notice/54411").to_return(status: 404)
|
||||
stub_request(:get, "https://community.highlandarrow.com/notice/53857").to_return(status: 404)
|
||||
stub_request(:get, "https://community.highlandarrow.com/notice/51852").to_return(status: 404)
|
||||
stub_request(:get, "https://social.umeahackerspace.se/notice/424348").to_return(status: 404)
|
||||
stub_request(:get, "https://community.highlandarrow.com/notice/50467").to_return(status: 404)
|
||||
stub_request(:get, "https://quitter.no/notice/1243309").to_return(status: 404)
|
||||
stub_request(:get, "https://quitter.no/user/7477").to_return(status: 404)
|
||||
stub_request(:any, "https://community.highlandarrow.com/user/1").to_return(status: 404)
|
||||
stub_request(:any, "https://social.umeahackerspace.se/user/2").to_return(status: 404)
|
||||
stub_request(:any, "https://gs.kawa-kun.com/user/2").to_return(status: 404)
|
||||
stub_request(:any, "https://mastodon.social/users/Gargron").to_return(status: 404)
|
||||
|
||||
request.env['HTTP_X_HUB_SIGNATURE'] = "sha1=#{OpenSSL::HMAC.hexdigest('sha1', 'abc', feed)}"
|
||||
|
||||
post :update, params: { id: account.id }, body: feed
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'creates statuses for feed' do
|
||||
expect(account.statuses.count).to_not eq 0
|
||||
end
|
||||
end
|
||||
end
|
@ -38,6 +38,12 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do
|
||||
it 'allows follower to follow' do
|
||||
expect(follower.following?(user.account)).to be true
|
||||
end
|
||||
|
||||
it 'returns JSON with followed_by=true' do
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:followed_by]).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #reject' do
|
||||
@ -54,5 +60,11 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do
|
||||
it 'removes follow request' do
|
||||
expect(FollowRequest.where(target_account: user.account, account: follower).count).to eq 0
|
||||
end
|
||||
|
||||
it 'returns JSON with followed_by=false' do
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:followed_by]).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,51 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::FollowsController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:follows') }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
before do
|
||||
stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
|
||||
stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no").to_return(request_fixture('webfinger.txt'))
|
||||
stub_request(:head, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(:status => 405, :body => "", :headers => {})
|
||||
stub_request(:get, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
|
||||
stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
|
||||
stub_request(:post, "https://quitter.no/main/push/hub").to_return(:status => 200, :body => "", :headers => {})
|
||||
stub_request(:post, "https://quitter.no/main/salmon/user/7477").to_return(:status => 200, :body => "", :headers => {})
|
||||
|
||||
post :create, params: { uri: 'gargron@quitter.no' }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'creates account for remote user' do
|
||||
expect(Account.find_by(username: 'gargron', domain: 'quitter.no')).to_not be_nil
|
||||
end
|
||||
|
||||
it 'creates a follow relation between user and remote user' do
|
||||
expect(user.account.following?(Account.find_by(username: 'gargron', domain: 'quitter.no'))).to be true
|
||||
end
|
||||
|
||||
it 'sends a salmon slap to the remote user' do
|
||||
expect(a_request(:post, "https://quitter.no/main/salmon/user/7477")).to have_been_made
|
||||
end
|
||||
|
||||
it 'subscribes to remote hub' do
|
||||
expect(a_request(:post, "https://quitter.no/main/push/hub")).to have_been_made
|
||||
end
|
||||
|
||||
it 'returns http success if already following, too' do
|
||||
post :create, params: { uri: 'gargron@quitter.no' }
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
65
spec/controllers/api/v1/markers_controller_spec.rb
Normal file
65
spec/controllers/api/v1/markers_controller_spec.rb
Normal file
@ -0,0 +1,65 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::MarkersController, type: :controller do
|
||||
render_views
|
||||
|
||||
let!(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let!(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses write:statuses') }
|
||||
|
||||
before { allow(controller).to receive(:doorkeeper_token) { token } }
|
||||
|
||||
describe 'GET #index' do
|
||||
before do
|
||||
Fabricate(:marker, timeline: 'home', last_read_id: 123, user: user)
|
||||
Fabricate(:marker, timeline: 'notifications', last_read_id: 456, user: user)
|
||||
|
||||
get :index, params: { timeline: %w(home notifications) }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns markers' do
|
||||
json = body_as_json
|
||||
|
||||
expect(json.key?(:home)).to be true
|
||||
expect(json[:home][:last_read_id]).to eq '123'
|
||||
expect(json.key?(:notifications)).to be true
|
||||
expect(json[:notifications][:last_read_id]).to eq '456'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
context 'when no marker exists' do
|
||||
before do
|
||||
post :create, params: { home: { last_read_id: '69420' } }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'creates a marker' do
|
||||
expect(user.markers.first.timeline).to eq 'home'
|
||||
expect(user.markers.first.last_read_id).to eq 69420
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a marker exists' do
|
||||
before do
|
||||
post :create, params: { home: { last_read_id: '69420' } }
|
||||
post :create, params: { home: { last_read_id: '70120' } }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'updates a marker' do
|
||||
expect(user.markers.first.timeline).to eq 'home'
|
||||
expect(user.markers.first.last_read_id).to eq 70120
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,22 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::SearchController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:search') }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
it 'returns http success' do
|
||||
get :index, params: { q: 'test' }
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
@ -91,13 +91,6 @@ RSpec.describe Api::V1::StatusesController, type: :controller do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #card' do
|
||||
it 'returns http unautharized' do
|
||||
get :card, params: { id: status.id }
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a public status' do
|
||||
@ -120,13 +113,6 @@ RSpec.describe Api::V1::StatusesController, type: :controller do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #card' do
|
||||
it 'returns http success' do
|
||||
get :card, params: { id: status.id }
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,17 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::Timelines::DirectController, type: :controller do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses') }
|
||||
|
||||
describe 'GET #show' do
|
||||
it 'returns 200' do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
get :show
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
@ -110,6 +110,7 @@ describe ApplicationController, type: :controller do
|
||||
sign_in current_user
|
||||
|
||||
allow(Setting).to receive(:[]).with('theme').and_return 'contrast'
|
||||
allow(Setting).to receive(:[]).with('noindex').and_return false
|
||||
|
||||
expect(controller.view_context.current_theme).to eq 'contrast'
|
||||
end
|
||||
@ -187,10 +188,10 @@ describe ApplicationController, type: :controller do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns http 403 if user who signed in is suspended' do
|
||||
it 'redirects to account status page' do
|
||||
sign_in(Fabricate(:user, account: Fabricate(:account, suspended: true)))
|
||||
get 'success'
|
||||
expect(response).to have_http_status(403)
|
||||
expect(response).to redirect_to(edit_user_registration_path)
|
||||
end
|
||||
end
|
||||
|
||||
@ -360,9 +361,5 @@ describe ApplicationController, type: :controller do
|
||||
context 'Status' do
|
||||
include_examples 'cacheable', :status, Status
|
||||
end
|
||||
|
||||
context 'StreamEntry' do
|
||||
include_examples 'receives :with_includes', :stream_entry, StreamEntry
|
||||
end
|
||||
end
|
||||
end
|
||||
|
46
spec/controllers/auth/challenges_controller_spec.rb
Normal file
46
spec/controllers/auth/challenges_controller_spec.rb
Normal file
@ -0,0 +1,46 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe Auth::ChallengesController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:password) { 'foobar12345' }
|
||||
let(:user) { Fabricate(:user, password: password) }
|
||||
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
let(:return_to) { edit_user_registration_path }
|
||||
|
||||
context 'with correct password' do
|
||||
before { post :create, params: { form_challenge: { return_to: return_to, current_password: password } } }
|
||||
|
||||
it 'redirects back' do
|
||||
expect(response).to redirect_to(return_to)
|
||||
end
|
||||
|
||||
it 'sets session' do
|
||||
expect(session[:challenge_passed_at]).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with incorrect password' do
|
||||
before { post :create, params: { form_challenge: { return_to: return_to, current_password: 'hhfggjjd562' } } }
|
||||
|
||||
it 'renders challenge' do
|
||||
expect(response).to render_template('auth/challenges/new')
|
||||
end
|
||||
|
||||
it 'displays error' do
|
||||
expect(response.body).to include 'Invalid password'
|
||||
end
|
||||
|
||||
it 'does not set session' do
|
||||
expect(session[:challenge_passed_at]).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -50,45 +50,4 @@ describe Auth::ConfirmationsController, type: :controller do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #finish_signup' do
|
||||
subject { get :finish_signup }
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
before do
|
||||
sign_in user, scope: :user
|
||||
@request.env['devise.mapping'] = Devise.mappings[:user]
|
||||
end
|
||||
|
||||
it 'renders finish_signup' do
|
||||
is_expected.to render_template :finish_signup
|
||||
expect(assigns(:user)).to have_attributes id: user.id
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH #finish_signup' do
|
||||
subject { patch :finish_signup, params: { user: { email: email } } }
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
before do
|
||||
sign_in user, scope: :user
|
||||
@request.env['devise.mapping'] = Devise.mappings[:user]
|
||||
end
|
||||
|
||||
context 'when email is valid' do
|
||||
let(:email) { 'new_' + user.email }
|
||||
|
||||
it 'redirects to root_path' do
|
||||
is_expected.to redirect_to root_path
|
||||
end
|
||||
end
|
||||
|
||||
context 'when email is invalid' do
|
||||
let(:email) { '' }
|
||||
|
||||
it 'renders finish_signup' do
|
||||
is_expected.to render_template :finish_signup
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -46,6 +46,15 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
|
||||
post :update
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
context 'when suspended' do
|
||||
it 'returns http forbidden' do
|
||||
request.env["devise.mapping"] = Devise.mappings[:user]
|
||||
sign_in(Fabricate(:user, account_attributes: { username: 'test', suspended_at: Time.now.utc }), scope: :user)
|
||||
post :update
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
@ -94,9 +103,9 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
|
||||
post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678' } }
|
||||
end
|
||||
|
||||
it 'redirects to login page' do
|
||||
it 'redirects to setup' do
|
||||
subject
|
||||
expect(response).to redirect_to new_user_session_path
|
||||
expect(response).to redirect_to auth_setup_path
|
||||
end
|
||||
|
||||
it 'creates user' do
|
||||
@ -120,9 +129,9 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
|
||||
post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678' } }
|
||||
end
|
||||
|
||||
it 'redirects to login page' do
|
||||
it 'redirects to setup' do
|
||||
subject
|
||||
expect(response).to redirect_to new_user_session_path
|
||||
expect(response).to redirect_to auth_setup_path
|
||||
end
|
||||
|
||||
it 'creates user' do
|
||||
@ -148,9 +157,9 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
|
||||
post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', 'invite_code': invite.code } }
|
||||
end
|
||||
|
||||
it 'redirects to login page' do
|
||||
it 'redirects to setup' do
|
||||
subject
|
||||
expect(response).to redirect_to new_user_session_path
|
||||
expect(response).to redirect_to auth_setup_path
|
||||
end
|
||||
|
||||
it 'creates user' do
|
||||
@ -176,9 +185,9 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
|
||||
post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', 'invite_code': invite.code } }
|
||||
end
|
||||
|
||||
it 'redirects to login page' do
|
||||
it 'redirects to setup' do
|
||||
subject
|
||||
expect(response).to redirect_to new_user_session_path
|
||||
expect(response).to redirect_to auth_setup_path
|
||||
end
|
||||
|
||||
it 'creates user' do
|
||||
|
@ -5,11 +5,11 @@ require 'rails_helper'
|
||||
RSpec.describe Auth::SessionsController, type: :controller do
|
||||
render_views
|
||||
|
||||
describe 'GET #new' do
|
||||
before do
|
||||
request.env['devise.mapping'] = Devise.mappings[:user]
|
||||
end
|
||||
before do
|
||||
request.env['devise.mapping'] = Devise.mappings[:user]
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
it 'returns http success' do
|
||||
get :new
|
||||
expect(response).to have_http_status(200)
|
||||
@ -19,10 +19,6 @@ RSpec.describe Auth::SessionsController, type: :controller do
|
||||
describe 'DELETE #destroy' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
request.env['devise.mapping'] = Devise.mappings[:user]
|
||||
end
|
||||
|
||||
context 'with a regular user' do
|
||||
it 'redirects to home after sign out' do
|
||||
sign_in(user, scope: :user)
|
||||
@ -51,10 +47,6 @@ RSpec.describe Auth::SessionsController, type: :controller do
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
before do
|
||||
request.env['devise.mapping'] = Devise.mappings[:user]
|
||||
end
|
||||
|
||||
context 'using PAM authentication', if: ENV['PAM_ENABLED'] == 'true' do
|
||||
context 'using a valid password' do
|
||||
before do
|
||||
@ -88,7 +80,7 @@ RSpec.describe Auth::SessionsController, type: :controller do
|
||||
let(:user) do
|
||||
account = Fabricate.build(:account, username: 'pam_user1')
|
||||
account.save!(validate: false)
|
||||
user = Fabricate(:user, email: 'pam@example.com', password: nil, account: account)
|
||||
user = Fabricate(:user, email: 'pam@example.com', password: nil, account: account, external: true)
|
||||
user
|
||||
end
|
||||
|
||||
@ -160,8 +152,8 @@ RSpec.describe Auth::SessionsController, type: :controller do
|
||||
let(:unconfirmed_user) { user.tap { |u| u.update!(confirmed_at: nil) } }
|
||||
let(:accept_language) { 'fr' }
|
||||
|
||||
it 'shows a translated login error' do
|
||||
expect(flash[:alert]).to eq(I18n.t('devise.failure.unconfirmed', locale: accept_language))
|
||||
it 'redirects to home' do
|
||||
expect(response).to redirect_to(root_path)
|
||||
end
|
||||
end
|
||||
|
||||
@ -191,11 +183,11 @@ RSpec.describe Auth::SessionsController, type: :controller do
|
||||
end
|
||||
|
||||
context 'using two-factor authentication' do
|
||||
let(:user) do
|
||||
Fabricate(:user, email: 'x@y.com', password: 'abcdefgh',
|
||||
otp_required_for_login: true, otp_secret: User.generate_otp_secret(32))
|
||||
let!(:user) do
|
||||
Fabricate(:user, email: 'x@y.com', password: 'abcdefgh', otp_required_for_login: true, otp_secret: User.generate_otp_secret(32))
|
||||
end
|
||||
let(:recovery_codes) do
|
||||
|
||||
let!(:recovery_codes) do
|
||||
codes = user.generate_otp_backup_codes!
|
||||
user.save
|
||||
return codes
|
||||
|
@ -41,7 +41,7 @@ describe ApplicationController, type: :controller do
|
||||
it 'sets link headers' do
|
||||
account = Fabricate(:account, username: 'username', user: Fabricate(:user))
|
||||
get 'success', params: { account_username: 'username' }
|
||||
expect(response.headers['Link'].to_s).to eq '<http://test.host/.well-known/webfinger?resource=acct%3Ausername%40cb6e6126.ngrok.io>; rel="lrdd"; type="application/xrd+xml", <http://test.host/users/username.atom>; rel="alternate"; type="application/atom+xml", <https://cb6e6126.ngrok.io/users/username>; rel="alternate"; type="application/activity+json"'
|
||||
expect(response.headers['Link'].to_s).to eq '<http://test.host/.well-known/webfinger?resource=acct%3Ausername%40cb6e6126.ngrok.io>; rel="lrdd"; type="application/jrd+json", <https://cb6e6126.ngrok.io/users/username>; rel="alternate"; type="application/activity+json"'
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
|
114
spec/controllers/concerns/challengable_concern_spec.rb
Normal file
114
spec/controllers/concerns/challengable_concern_spec.rb
Normal file
@ -0,0 +1,114 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ChallengableConcern, type: :controller do
|
||||
controller(ApplicationController) do
|
||||
include ChallengableConcern
|
||||
|
||||
before_action :require_challenge!
|
||||
|
||||
def foo
|
||||
render plain: 'foo'
|
||||
end
|
||||
|
||||
def bar
|
||||
render plain: 'bar'
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
routes.draw do
|
||||
get 'foo' => 'anonymous#foo'
|
||||
post 'bar' => 'anonymous#bar'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a no-password user' do
|
||||
let(:user) { Fabricate(:user, external: true, password: nil) }
|
||||
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
context 'for GET requests' do
|
||||
before { get :foo }
|
||||
|
||||
it 'does not ask for password' do
|
||||
expect(response.body).to eq 'foo'
|
||||
end
|
||||
end
|
||||
|
||||
context 'for POST requests' do
|
||||
before { post :bar }
|
||||
|
||||
it 'does not ask for password' do
|
||||
expect(response.body).to eq 'bar'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with recent challenge in session' do
|
||||
let(:password) { 'foobar12345' }
|
||||
let(:user) { Fabricate(:user, password: password) }
|
||||
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
context 'for GET requests' do
|
||||
before { get :foo, session: { challenge_passed_at: Time.now.utc } }
|
||||
|
||||
it 'does not ask for password' do
|
||||
expect(response.body).to eq 'foo'
|
||||
end
|
||||
end
|
||||
|
||||
context 'for POST requests' do
|
||||
before { post :bar, session: { challenge_passed_at: Time.now.utc } }
|
||||
|
||||
it 'does not ask for password' do
|
||||
expect(response.body).to eq 'bar'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a password user' do
|
||||
let(:password) { 'foobar12345' }
|
||||
let(:user) { Fabricate(:user, password: password) }
|
||||
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
context 'for GET requests' do
|
||||
before { get :foo }
|
||||
|
||||
it 'renders challenge' do
|
||||
expect(response).to render_template('auth/challenges/new')
|
||||
end
|
||||
|
||||
# See Auth::ChallengesControllerSpec
|
||||
end
|
||||
|
||||
context 'for POST requests' do
|
||||
before { post :bar }
|
||||
|
||||
it 'renders challenge' do
|
||||
expect(response).to render_template('auth/challenges/new')
|
||||
end
|
||||
|
||||
it 'accepts correct password' do
|
||||
post :bar, params: { form_challenge: { current_password: password } }
|
||||
expect(response.body).to eq 'bar'
|
||||
expect(session[:challenge_passed_at]).to_not be_nil
|
||||
end
|
||||
|
||||
it 'rejects wrong password' do
|
||||
post :bar, params: { form_challenge: { current_password: 'dddfff888123' } }
|
||||
expect(response.body).to render_template('auth/challenges/new')
|
||||
expect(session[:challenge_passed_at]).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -38,7 +38,7 @@ describe ApplicationController, type: :controller do
|
||||
end
|
||||
|
||||
context 'with signature header' do
|
||||
let!(:author) { Fabricate(:account) }
|
||||
let!(:author) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/actor') }
|
||||
|
||||
context 'without body' do
|
||||
before do
|
||||
|
@ -27,16 +27,6 @@ RSpec.describe HomeController, type: :controller do
|
||||
subject
|
||||
expect(assigns(:body_classes)).to eq 'app-body'
|
||||
end
|
||||
|
||||
it 'assigns @initial_state_json' do
|
||||
subject
|
||||
initial_state_json = json_str_to_hash(assigns(:initial_state_json))
|
||||
expect(initial_state_json[:meta]).to_not be_nil
|
||||
expect(initial_state_json[:compose]).to_not be_nil
|
||||
expect(initial_state_json[:accounts]).to_not be_nil
|
||||
expect(initial_state_json[:settings]).to_not be_nil
|
||||
expect(initial_state_json[:media_attachments]).to_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -66,9 +66,7 @@ describe RemoteFollowController do
|
||||
end
|
||||
|
||||
it 'redirects to the remote location' do
|
||||
address = "http://example.com/follow_me?acct=test_user%40#{Rails.configuration.x.local_domain}"
|
||||
|
||||
expect(response).to redirect_to(address)
|
||||
expect(response).to redirect_to("http://example.com/follow_me?acct=https%3A%2F%2F#{Rails.configuration.x.local_domain}%2Fusers%2Ftest_user")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,38 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe RemoteUnfollowsController do
|
||||
render_views
|
||||
|
||||
describe '#create' do
|
||||
subject { post :create, params: { acct: acct } }
|
||||
|
||||
let(:current_user) { Fabricate(:user, account: current_account) }
|
||||
let(:current_account) { Fabricate(:account) }
|
||||
let(:remote_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox')).account }
|
||||
before do
|
||||
sign_in current_user
|
||||
current_account.follow!(remote_account)
|
||||
stub_request(:post, 'http://example.com/inbox') { { status: 200 } }
|
||||
end
|
||||
|
||||
context 'when successfully unfollow remote account' do
|
||||
let(:acct) { "acct:#{remote_account.username}@#{remote_account.domain}" }
|
||||
|
||||
it do
|
||||
is_expected.to render_template :success
|
||||
expect(current_account.following?(remote_account)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when fails to unfollow remote account' do
|
||||
let(:acct) { "acct:#{remote_account.username + '_test'}@#{remote_account.domain}" }
|
||||
|
||||
it do
|
||||
is_expected.to render_template :error
|
||||
expect(current_account.following?(remote_account)).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -15,6 +15,15 @@ describe Settings::DeletesController do
|
||||
get :show
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
context 'when suspended' do
|
||||
let(:user) { Fabricate(:user, account_attributes: { username: 'alice', suspended_at: Time.now.utc }) }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
get :show
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not signed in' do
|
||||
@ -49,6 +58,14 @@ describe Settings::DeletesController do
|
||||
it 'marks account as suspended' do
|
||||
expect(user.account.reload).to be_suspended
|
||||
end
|
||||
|
||||
context 'when suspended' do
|
||||
let(:user) { Fabricate(:user, account_attributes: { username: 'alice', suspended_at: Time.now.utc }) }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with incorrect password' do
|
||||
|
@ -8,8 +8,8 @@ describe Settings::IdentityProofsController do
|
||||
let(:valid_token) { '1'*66 }
|
||||
let(:kbname) { 'kbuser' }
|
||||
let(:provider) { 'keybase' }
|
||||
let(:findable_id) { Faker::Number.number(5) }
|
||||
let(:unfindable_id) { Faker::Number.number(5) }
|
||||
let(:findable_id) { Faker::Number.number(digits: 5) }
|
||||
let(:unfindable_id) { Faker::Number.number(digits: 5) }
|
||||
let(:new_proof_params) do
|
||||
{ provider: provider, provider_username: kbname, token: valid_token, username: user.account.username }
|
||||
end
|
||||
|
@ -21,6 +21,7 @@ describe Settings::MigrationsController do
|
||||
|
||||
let(:user) { Fabricate(:user, account: account) }
|
||||
let(:account) { Fabricate(:account, moved_to_account: moved_to_account) }
|
||||
|
||||
before { sign_in user, scope: :user }
|
||||
|
||||
context 'when user does not have moved to account' do
|
||||
@ -32,7 +33,7 @@ describe Settings::MigrationsController do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user does not have moved to account' do
|
||||
context 'when user has a moved to account' do
|
||||
let(:moved_to_account) { Fabricate(:account) }
|
||||
|
||||
it 'renders show page' do
|
||||
@ -43,21 +44,22 @@ describe Settings::MigrationsController do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
describe 'POST #create' do
|
||||
context 'when user is not sign in' do
|
||||
subject { put :update }
|
||||
subject { post :create }
|
||||
|
||||
it_behaves_like 'authenticate user'
|
||||
end
|
||||
|
||||
context 'when user is sign in' do
|
||||
subject { put :update, params: { migration: { acct: acct } } }
|
||||
subject { post :create, params: { account_migration: { acct: acct, current_password: '12345678' } } }
|
||||
|
||||
let(:user) { Fabricate(:user, password: '12345678') }
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
before { sign_in user, scope: :user }
|
||||
|
||||
context 'when migration account is changed' do
|
||||
let(:acct) { Fabricate(:account) }
|
||||
let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) }
|
||||
|
||||
it 'updates moved to account' do
|
||||
is_expected.to redirect_to settings_migration_path
|
||||
|
@ -24,7 +24,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do
|
||||
context 'when signed in' do
|
||||
subject do
|
||||
sign_in user, scope: :user
|
||||
get :new
|
||||
get :new, session: { challenge_passed_at: Time.now.utc }
|
||||
end
|
||||
|
||||
include_examples 'renders :new'
|
||||
@ -37,7 +37,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do
|
||||
|
||||
it 'redirects if user do not have otp_secret' do
|
||||
sign_in user_without_otp_secret, scope: :user
|
||||
get :new
|
||||
get :new, session: { challenge_passed_at: Time.now.utc }
|
||||
expect(response).to redirect_to('/settings/two_factor_authentication')
|
||||
end
|
||||
end
|
||||
@ -50,7 +50,8 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do
|
||||
|
||||
describe 'when form_two_factor_confirmation parameter is not provided' do
|
||||
it 'raises ActionController::ParameterMissing' do
|
||||
expect { post :create, params: {} }.to raise_error(ActionController::ParameterMissing)
|
||||
post :create, params: {}, session: { challenge_passed_at: Time.now.utc }
|
||||
expect(response).to have_http_status(400)
|
||||
end
|
||||
end
|
||||
|
||||
@ -67,7 +68,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do
|
||||
true
|
||||
end
|
||||
|
||||
post :create, params: { form_two_factor_confirmation: { code: '123456' } }
|
||||
post :create, params: { form_two_factor_confirmation: { otp_attempt: '123456' } }, session: { challenge_passed_at: Time.now.utc }
|
||||
|
||||
expect(assigns(:recovery_codes)).to eq otp_backup_codes
|
||||
expect(flash[:notice]).to eq 'Two-factor authentication successfully enabled'
|
||||
@ -84,7 +85,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do
|
||||
false
|
||||
end
|
||||
|
||||
post :create, params: { form_two_factor_confirmation: { code: '123456' } }
|
||||
post :create, params: { form_two_factor_confirmation: { otp_attempt: '123456' } }, session: { challenge_passed_at: Time.now.utc }
|
||||
end
|
||||
|
||||
it 'renders the new view' do
|
||||
@ -98,7 +99,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do
|
||||
|
||||
context 'when not signed in' do
|
||||
it 'redirects if not signed in' do
|
||||
post :create, params: { form_two_factor_confirmation: { code: '123456' } }
|
||||
post :create, params: { form_two_factor_confirmation: { otp_attempt: '123456' } }
|
||||
expect(response).to redirect_to('/auth/sign_in')
|
||||
end
|
||||
end
|
||||
|
@ -15,7 +15,7 @@ describe Settings::TwoFactorAuthentication::RecoveryCodesController do
|
||||
end
|
||||
|
||||
sign_in user, scope: :user
|
||||
post :create
|
||||
post :create, session: { challenge_passed_at: Time.now.utc }
|
||||
|
||||
expect(assigns(:recovery_codes)).to eq otp_backup_codes
|
||||
expect(flash[:notice]).to eq 'Recovery codes successfully regenerated'
|
||||
|
@ -58,7 +58,7 @@ describe Settings::TwoFactorAuthenticationsController do
|
||||
describe 'when creation succeeds' do
|
||||
it 'updates user secret' do
|
||||
before = user.otp_secret
|
||||
post :create
|
||||
post :create, session: { challenge_passed_at: Time.now.utc }
|
||||
|
||||
expect(user.reload.otp_secret).not_to eq(before)
|
||||
expect(response).to redirect_to(new_settings_two_factor_authentication_confirmation_path)
|
||||
@ -91,7 +91,7 @@ describe Settings::TwoFactorAuthenticationsController do
|
||||
true
|
||||
end
|
||||
|
||||
post :destroy, params: { form_two_factor_confirmation: { code: '123456' } }
|
||||
post :destroy, params: { form_two_factor_confirmation: { otp_attempt: '123456' } }
|
||||
|
||||
expect(response).to redirect_to(settings_two_factor_authentication_path)
|
||||
user.reload
|
||||
@ -105,14 +105,15 @@ describe Settings::TwoFactorAuthenticationsController do
|
||||
false
|
||||
end
|
||||
|
||||
post :destroy, params: { form_two_factor_confirmation: { code: '057772' } }
|
||||
post :destroy, params: { form_two_factor_confirmation: { otp_attempt: '057772' } }
|
||||
|
||||
user.reload
|
||||
expect(user.otp_required_for_login).to eq(true)
|
||||
end
|
||||
|
||||
it 'raises ActionController::ParameterMissing if code is missing' do
|
||||
expect { post :destroy }.to raise_error(ActionController::ParameterMissing)
|
||||
post :destroy
|
||||
expect(response).to have_http_status(400)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -7,15 +7,12 @@ describe SharesController do
|
||||
before { sign_in user }
|
||||
|
||||
describe 'GTE #show' do
|
||||
subject(:initial_state_json) { JSON.parse(assigns(:initial_state_json), symbolize_names: true) }
|
||||
subject(:body_classes) { assigns(:body_classes) }
|
||||
|
||||
before { get :show, params: { title: 'test title', text: 'test text', url: 'url1 url2' } }
|
||||
|
||||
it 'assigns json' do
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status :ok
|
||||
expect(initial_state_json[:compose][:text]).to eq 'test title test text url1 url2'
|
||||
expect(initial_state_json[:meta][:me]).to eq user.account.id.to_s
|
||||
expect(body_classes).to eq 'modal-layout compose-standalone'
|
||||
end
|
||||
end
|
||||
|
@ -55,18 +55,6 @@ describe StatusesController do
|
||||
expect(assigns(:status)).to eq status
|
||||
end
|
||||
|
||||
it 'assigns @stream_entry' do
|
||||
status = Fabricate(:status)
|
||||
get :show, params: { account_username: status.account.username, id: status.id }
|
||||
expect(assigns(:stream_entry)).to eq status.stream_entry
|
||||
end
|
||||
|
||||
it 'assigns @type' do
|
||||
status = Fabricate(:status)
|
||||
get :show, params: { account_username: status.account.username, id: status.id }
|
||||
expect(assigns(:type)).to eq 'status'
|
||||
end
|
||||
|
||||
it 'assigns @ancestors for ancestors of the status if it is a reply' do
|
||||
ancestor = Fabricate(:status)
|
||||
status = Fabricate(:status, in_reply_to_id: ancestor.id)
|
||||
@ -104,7 +92,7 @@ describe StatusesController do
|
||||
end
|
||||
|
||||
it 'assigns @max_descendant_thread_id for the last thread if it is hitting the status limit' do
|
||||
stub_const 'StatusesController::DESCENDANTS_LIMIT', 1
|
||||
stub_const 'StatusControllerConcern::DESCENDANTS_LIMIT', 1
|
||||
status = Fabricate(:status)
|
||||
child = Fabricate(:status, in_reply_to_id: status.id)
|
||||
|
||||
@ -115,7 +103,7 @@ describe StatusesController do
|
||||
end
|
||||
|
||||
it 'assigns @descendant_threads for threads with :next_status key if they are hitting the depth limit' do
|
||||
stub_const 'StatusesController::DESCENDANTS_DEPTH_LIMIT', 2
|
||||
stub_const 'StatusControllerConcern::DESCENDANTS_DEPTH_LIMIT', 2
|
||||
status = Fabricate(:status)
|
||||
child0 = Fabricate(:status, in_reply_to_id: status.id)
|
||||
child1 = Fabricate(:status, in_reply_to_id: child0.id)
|
||||
@ -135,10 +123,10 @@ describe StatusesController do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'renders stream_entries/show' do
|
||||
it 'renders statuses/show' do
|
||||
status = Fabricate(:status)
|
||||
get :show, params: { account_username: status.account.username, id: status.id }
|
||||
expect(response).to render_template 'stream_entries/show'
|
||||
expect(response).to render_template 'statuses/show'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,95 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe StreamEntriesController, type: :controller do
|
||||
render_views
|
||||
|
||||
shared_examples 'before_action' do |route|
|
||||
context 'when account is not suspended and stream_entry is available' do
|
||||
it 'assigns instance variables' do
|
||||
status = Fabricate(:status)
|
||||
|
||||
get route, params: { account_username: status.account.username, id: status.stream_entry.id }
|
||||
|
||||
expect(assigns(:account)).to eq status.account
|
||||
expect(assigns(:stream_entry)).to eq status.stream_entry
|
||||
expect(assigns(:type)).to eq 'status'
|
||||
end
|
||||
|
||||
it 'sets Link headers' do
|
||||
alice = Fabricate(:account, username: 'alice')
|
||||
status = Fabricate(:status, account: alice)
|
||||
|
||||
get route, params: { account_username: alice.username, id: status.stream_entry.id }
|
||||
|
||||
expect(response.headers['Link'].to_s).to eq "<http://test.host/users/alice/updates/#{status.stream_entry.id}.atom>; rel=\"alternate\"; type=\"application/atom+xml\", <https://cb6e6126.ngrok.io/users/alice/statuses/#{status.id}>; rel=\"alternate\"; type=\"application/activity+json\""
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is suspended' do
|
||||
it 'returns http status 410' do
|
||||
account = Fabricate(:account, suspended: true)
|
||||
status = Fabricate(:status, account: account)
|
||||
|
||||
get route, params: { account_username: account.username, id: status.stream_entry.id }
|
||||
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when activity is nil' do
|
||||
it 'raises ActiveRecord::RecordNotFound' do
|
||||
account = Fabricate(:account)
|
||||
stream_entry = Fabricate.build(:stream_entry, account: account, activity: nil, activity_type: 'Status')
|
||||
stream_entry.save!(validate: false)
|
||||
|
||||
get route, params: { account_username: account.username, id: stream_entry.id }
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is hidden and it is not permitted' do
|
||||
it 'raises ActiveRecord::RecordNotFound' do
|
||||
status = Fabricate(:status)
|
||||
user = Fabricate(:user)
|
||||
status.account.block!(user.account)
|
||||
status.stream_entry.update!(hidden: true)
|
||||
|
||||
sign_in(user)
|
||||
get route, params: { account_username: status.account.username, id: status.stream_entry.id }
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
include_examples 'before_action', :show
|
||||
|
||||
it 'redirects to status page' do
|
||||
status = Fabricate(:status)
|
||||
|
||||
get :show, params: { account_username: status.account.username, id: status.stream_entry.id }
|
||||
|
||||
expect(response).to redirect_to(short_account_status_url(status.account, status))
|
||||
end
|
||||
|
||||
it 'returns http success with Atom' do
|
||||
status = Fabricate(:status)
|
||||
get :show, params: { account_username: status.account.username, id: status.stream_entry.id }, format: 'atom'
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #embed' do
|
||||
include_examples 'before_action', :embed
|
||||
|
||||
it 'redirects to new embed page' do
|
||||
status = Fabricate(:status)
|
||||
|
||||
get :embed, params: { account_username: status.account.username, id: status.stream_entry.id }
|
||||
|
||||
expect(response).to redirect_to(embed_short_account_status_url(status.account, status))
|
||||
end
|
||||
end
|
||||
end
|
36
spec/controllers/well_known/nodeinfo_controller_spec.rb
Normal file
36
spec/controllers/well_known/nodeinfo_controller_spec.rb
Normal file
@ -0,0 +1,36 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe WellKnown::NodeInfoController, type: :controller do
|
||||
render_views
|
||||
|
||||
describe 'GET #index' do
|
||||
it 'returns json document pointing to node info' do
|
||||
get :index
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.content_type).to eq 'application/json'
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:links]).to be_an Array
|
||||
expect(json[:links][0][:rel]).to eq 'http://nodeinfo.diaspora.software/ns/schema/2.0'
|
||||
expect(json[:links][0][:href]).to include 'nodeinfo/2.0'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
it 'returns json document with node info properties' do
|
||||
get :show
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.content_type).to eq 'application/json'
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:version]).to eq '2.0'
|
||||
expect(json[:usage]).to be_a Hash
|
||||
expect(json[:software]).to be_a Hash
|
||||
expect(json[:protocols]).to be_an Array
|
||||
end
|
||||
end
|
||||
end
|
@ -56,17 +56,6 @@ PEM
|
||||
expect(json[:aliases]).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice')
|
||||
end
|
||||
|
||||
it 'returns JSON when account can be found' do
|
||||
get :show, params: { resource: alice.to_webfinger_s }, format: :xml
|
||||
|
||||
xml = Nokogiri::XML(response.body)
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.content_type).to eq 'application/xrd+xml'
|
||||
expect(xml.at_xpath('//xmlns:Subject').content).to eq 'acct:alice@cb6e6126.ngrok.io'
|
||||
expect(xml.xpath('//xmlns:Alias').map(&:content)).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice')
|
||||
end
|
||||
|
||||
it 'returns http not found when account cannot be found' do
|
||||
get :show, params: { resource: 'acct:not@existing.com' }, format: :json
|
||||
|
||||
|
5
spec/fabricators/account_alias_fabricator.rb
Normal file
5
spec/fabricators/account_alias_fabricator.rb
Normal file
@ -0,0 +1,5 @@
|
||||
Fabricator(:account_alias) do
|
||||
account
|
||||
acct 'test@example.com'
|
||||
uri 'https://example.com/users/test'
|
||||
end
|
@ -4,7 +4,7 @@ private_key = keypair.to_pem
|
||||
|
||||
Fabricator(:account) do
|
||||
transient :suspended, :silenced
|
||||
username { sequence(:username) { |i| "#{Faker::Internet.user_name(nil, %w(_))}#{i}" } }
|
||||
username { sequence(:username) { |i| "#{Faker::Internet.user_name(separators: %w(_))}#{i}" } }
|
||||
last_webfingered_at { Time.now.utc }
|
||||
public_key { public_key }
|
||||
private_key { private_key }
|
||||
|
@ -1,7 +1,7 @@
|
||||
Fabricator(:account_identity_proof) do
|
||||
account
|
||||
provider 'keybase'
|
||||
provider_username { sequence(:provider_username) { |i| "#{Faker::Lorem.characters(15)}" } }
|
||||
provider_username { sequence(:provider_username) { |i| "#{Faker::Lorem.characters(number: 15)}" } }
|
||||
token { sequence(:token) { |i| "#{i}#{Faker::Crypto.sha1()*2}"[0..65] } }
|
||||
verified false
|
||||
live false
|
||||
|
6
spec/fabricators/account_migration_fabricator.rb
Normal file
6
spec/fabricators/account_migration_fabricator.rb
Normal file
@ -0,0 +1,6 @@
|
||||
Fabricator(:account_migration) do
|
||||
account
|
||||
target_account
|
||||
followers_count 1234
|
||||
acct 'test@example.com'
|
||||
end
|
3
spec/fabricators/custom_emoji_category_fabricator.rb
Normal file
3
spec/fabricators/custom_emoji_category_fabricator.rb
Normal file
@ -0,0 +1,3 @@
|
||||
Fabricator(:custom_emoji_category) do
|
||||
name "MyString"
|
||||
end
|
3
spec/fabricators/domain_allow_fabricator.rb
Normal file
3
spec/fabricators/domain_allow_fabricator.rb
Normal file
@ -0,0 +1,3 @@
|
||||
Fabricator(:domain_allow) do
|
||||
domain "MyString"
|
||||
end
|
6
spec/fabricators/marker_fabricator.rb
Normal file
6
spec/fabricators/marker_fabricator.rb
Normal file
@ -0,0 +1,6 @@
|
||||
Fabricator(:marker) do
|
||||
user
|
||||
timeline 'home'
|
||||
last_read_id 0
|
||||
lock_version 0
|
||||
end
|
@ -1,5 +0,0 @@
|
||||
Fabricator(:stream_entry) do
|
||||
account
|
||||
activity { Fabricate(:status) }
|
||||
hidden { [true, false].sample }
|
||||
end
|
@ -1,7 +0,0 @@
|
||||
Fabricator(:subscription) do
|
||||
account
|
||||
callback_url "http://example.com/callback"
|
||||
secret "foobar"
|
||||
expires_at "2016-11-28 11:30:07"
|
||||
confirmed false
|
||||
end
|
@ -31,12 +31,12 @@ feature "Log in" do
|
||||
context do
|
||||
given(:confirmed_at) { nil }
|
||||
|
||||
scenario "A unconfirmed user is not able to log in" do
|
||||
scenario "A unconfirmed user is able to log in" do
|
||||
fill_in "user_email", with: email
|
||||
fill_in "user_password", with: password
|
||||
click_on I18n.t('auth.login')
|
||||
|
||||
is_expected.to have_css(".flash-message", text: failure_message("unconfirmed"))
|
||||
is_expected.to have_css("div.admin-wrapper")
|
||||
end
|
||||
end
|
||||
|
||||
|
2
spec/fixtures/requests/webfinger.txt
vendored
2
spec/fixtures/requests/webfinger.txt
vendored
@ -8,4 +8,4 @@ Access-Control-Allow-Origin: *
|
||||
Vary: Accept-Encoding,Cookie
|
||||
Strict-Transport-Security: max-age=31536000; includeSubdomains;
|
||||
|
||||
{"subject":"acct:gargron@quitter.no","aliases":["https:\/\/quitter.no\/user\/7477","https:\/\/quitter.no\/gargron","https:\/\/quitter.no\/index.php\/user\/7477","https:\/\/quitter.no\/index.php\/gargron"],"links":[{"rel":"http:\/\/webfinger.net\/rel\/profile-page","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/gmpg.org\/xfn\/11","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"describedby","type":"application\/rdf+xml","href":"https:\/\/quitter.no\/gargron\/foaf"},{"rel":"http:\/\/apinamespace.org\/atom","type":"application\/atomsvc+xml","href":"https:\/\/quitter.no\/api\/statusnet\/app\/service\/gargron.xml"},{"rel":"http:\/\/apinamespace.org\/twitter","href":"https:\/\/quitter.no\/api\/"},{"rel":"http:\/\/specs.openid.net\/auth\/2.0\/provider","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/schemas.google.com\/g\/2010#updates-from","type":"application\/atom+xml","href":"https:\/\/quitter.no\/api\/statuses\/user_timeline\/7477.atom"},{"rel":"magic-public-key","href":"data:application\/magic-public-key,RSA.1ZBkHTavLvxH3FzlKv4O6WtlILKRFfNami3_Rcu8EuogtXSYiS-bB6hElZfUCSHbC4uLemOA34PEhz__CDMozax1iI_t8dzjDnh1x0iFSup7pSfW9iXk_WU3Dm74yWWW2jildY41vWgrEstuQ1dJ8vVFfSJ9T_tO4c-T9y8vDI8=.AQAB"},{"rel":"salmon","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-replies","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-mention","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/ostatus.org\/schema\/1.0\/subscribe","template":"https:\/\/quitter.no\/main\/ostatussub?profile={uri}"}]}
|
||||
{"subject":"acct:gargron@quitter.no","aliases":["https:\/\/quitter.no\/user\/7477","https:\/\/quitter.no\/gargron","https:\/\/quitter.no\/index.php\/user\/7477","https:\/\/quitter.no\/index.php\/gargron"],"links":[{"rel":"http:\/\/webfinger.net\/rel\/profile-page","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/gmpg.org\/xfn\/11","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"describedby","type":"application\/rdf+xml","href":"https:\/\/quitter.no\/gargron\/foaf"},{"rel":"http:\/\/apinamespace.org\/atom","type":"application\/atomsvc+xml","href":"https:\/\/quitter.no\/api\/statusnet\/app\/service\/gargron.xml"},{"rel":"http:\/\/apinamespace.org\/twitter","href":"https:\/\/quitter.no\/api\/"},{"rel":"http:\/\/specs.openid.net\/auth\/2.0\/provider","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/schemas.google.com\/g\/2010#updates-from","type":"application\/atom+xml","href":"https:\/\/quitter.no\/api\/statuses\/user_timeline\/7477.atom"},{"rel":"magic-public-key","href":"data:application\/magic-public-key,RSA.1ZBkHTavLvxH3FzlKv4O6WtlILKRFfNami3_Rcu8EuogtXSYiS-bB6hElZfUCSHbC4uLemOA34PEhz__CDMozax1iI_t8dzjDnh1x0iFSup7pSfW9iXk_WU3Dm74yWWW2jildY41vWgrEstuQ1dJ8vVFfSJ9T_tO4c-T9y8vDI8=.AQAB"},{"rel":"salmon","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-replies","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-mention","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/ostatus.org\/schema\/1.0\/subscribe","template":"https:\/\/quitter.no\/main\/ostatussub?profile={uri}"}]}
|
||||
|
@ -3,7 +3,7 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::AccountModerationNotesHelper, type: :helper do
|
||||
include StreamEntriesHelper
|
||||
include StatusesHelper
|
||||
|
||||
describe '#admin_account_link_to' do
|
||||
context 'account is nil' do
|
||||
|
@ -1,6 +1,6 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe StreamEntriesHelper, type: :helper do
|
||||
RSpec.describe StatusesHelper, type: :helper do
|
||||
describe '#display_name' do
|
||||
it 'uses the display name when it exists' do
|
||||
account = Account.new(display_name: "Display", username: "Username")
|
||||
@ -70,13 +70,13 @@ RSpec.describe StreamEntriesHelper, type: :helper do
|
||||
end
|
||||
|
||||
def set_not_embedded_view
|
||||
params[:controller] = "not_#{StreamEntriesHelper::EMBEDDED_CONTROLLER}"
|
||||
params[:action] = "not_#{StreamEntriesHelper::EMBEDDED_ACTION}"
|
||||
params[:controller] = "not_#{StatusesHelper::EMBEDDED_CONTROLLER}"
|
||||
params[:action] = "not_#{StatusesHelper::EMBEDDED_ACTION}"
|
||||
end
|
||||
|
||||
def set_embedded_view
|
||||
params[:controller] = StreamEntriesHelper::EMBEDDED_CONTROLLER
|
||||
params[:action] = StreamEntriesHelper::EMBEDDED_ACTION
|
||||
params[:controller] = StatusesHelper::EMBEDDED_CONTROLLER
|
||||
params[:action] = StatusesHelper::EMBEDDED_ACTION
|
||||
end
|
||||
|
||||
describe '#style_classes' do
|
@ -31,6 +31,36 @@ RSpec.describe ActivityPub::Activity::Follow do
|
||||
end
|
||||
end
|
||||
|
||||
context 'silenced account following an unlocked account' do
|
||||
before do
|
||||
sender.touch(:silenced_at)
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'does not create a follow from sender to recipient' do
|
||||
expect(sender.following?(recipient)).to be false
|
||||
end
|
||||
|
||||
it 'creates a follow request' do
|
||||
expect(sender.requested?(recipient)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'unlocked account muting the sender' do
|
||||
before do
|
||||
recipient.mute!(sender)
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'creates a follow from sender to recipient' do
|
||||
expect(sender.following?(recipient)).to be true
|
||||
end
|
||||
|
||||
it 'does not create a follow request' do
|
||||
expect(sender.requested?(recipient)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'locked account' do
|
||||
before do
|
||||
recipient.update(locked: true)
|
||||
|
@ -19,7 +19,7 @@ RSpec.describe ActivityPub::Activity::Update do
|
||||
end
|
||||
|
||||
let(:actor_json) do
|
||||
ActiveModelSerializers::SerializableResource.new(modified_sender, serializer: ActivityPub::ActorSerializer, key_transform: :camel_lower).as_json
|
||||
ActiveModelSerializers::SerializableResource.new(modified_sender, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter).as_json
|
||||
end
|
||||
|
||||
let(:json) do
|
||||
|
@ -143,12 +143,6 @@ RSpec.describe ActivityPub::TagManager do
|
||||
expect(subject.uri_to_resource(OStatus::TagManager.instance.uri_for(status), Status)).to eq status
|
||||
end
|
||||
|
||||
it 'returns the local status for OStatus StreamEntry URL' do
|
||||
status = Fabricate(:status)
|
||||
stream_entry_url = account_stream_entry_url(status.account, status.stream_entry)
|
||||
expect(subject.uri_to_resource(stream_entry_url, Status)).to eq status
|
||||
end
|
||||
|
||||
it 'returns the remote status by matching URI without fragment part' do
|
||||
status = Fabricate(:status, uri: 'https://example.com/123')
|
||||
expect(subject.uri_to_resource('https://example.com/123#456', Status)).to eq status
|
||||
|
28
spec/lib/connection_pool/shared_connection_pool_spec.rb
Normal file
28
spec/lib/connection_pool/shared_connection_pool_spec.rb
Normal file
@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe ConnectionPool::SharedConnectionPool do
|
||||
class MiniConnection
|
||||
attr_reader :site
|
||||
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
end
|
||||
|
||||
subject { described_class.new(size: 5, timeout: 5) { |site| MiniConnection.new(site) } }
|
||||
|
||||
describe '#with' do
|
||||
it 'runs a block with a connection' do
|
||||
block_run = false
|
||||
|
||||
subject.with('foo') do |connection|
|
||||
expect(connection).to be_a MiniConnection
|
||||
block_run = true
|
||||
end
|
||||
|
||||
expect(block_run).to be true
|
||||
end
|
||||
end
|
||||
end
|
61
spec/lib/connection_pool/shared_timed_stack_spec.rb
Normal file
61
spec/lib/connection_pool/shared_timed_stack_spec.rb
Normal file
@ -0,0 +1,61 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe ConnectionPool::SharedTimedStack do
|
||||
class MiniConnection
|
||||
attr_reader :site
|
||||
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
end
|
||||
|
||||
subject { described_class.new(5) { |site| MiniConnection.new(site) } }
|
||||
|
||||
describe '#push' do
|
||||
it 'keeps the connection in the stack' do
|
||||
subject.push(MiniConnection.new('foo'))
|
||||
expect(subject.size).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
describe '#pop' do
|
||||
it 'returns a connection' do
|
||||
expect(subject.pop('foo')).to be_a MiniConnection
|
||||
end
|
||||
|
||||
it 'returns the same connection that was pushed in' do
|
||||
connection = MiniConnection.new('foo')
|
||||
subject.push(connection)
|
||||
expect(subject.pop('foo')).to be connection
|
||||
end
|
||||
|
||||
it 'does not create more than maximum amount of connections' do
|
||||
expect { 6.times { subject.pop('foo', 0) } }.to raise_error Timeout::Error
|
||||
end
|
||||
|
||||
it 'repurposes a connection for a different site when maximum amount is reached' do
|
||||
5.times { subject.push(MiniConnection.new('foo')) }
|
||||
expect(subject.pop('bar')).to be_a MiniConnection
|
||||
end
|
||||
end
|
||||
|
||||
describe '#empty?' do
|
||||
it 'returns true when no connections on the stack' do
|
||||
expect(subject.empty?).to be true
|
||||
end
|
||||
|
||||
it 'returns false when there are connections on the stack' do
|
||||
subject.push(MiniConnection.new('foo'))
|
||||
expect(subject.empty?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#size' do
|
||||
it 'returns the number of connections on the stack' do
|
||||
2.times { subject.push(MiniConnection.new('foo')) }
|
||||
expect(subject.size).to eq 2
|
||||
end
|
||||
end
|
||||
end
|
File diff suppressed because it is too large
Load Diff
63
spec/lib/request_pool_spec.rb
Normal file
63
spec/lib/request_pool_spec.rb
Normal file
@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe RequestPool do
|
||||
subject { described_class.new }
|
||||
|
||||
describe '#with' do
|
||||
it 'returns a HTTP client for a host' do
|
||||
subject.with('http://example.com') do |http_client|
|
||||
expect(http_client).to be_a HTTP::Client
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns the same instance of HTTP client within the same thread for the same host' do
|
||||
test_client = nil
|
||||
|
||||
subject.with('http://example.com') { |http_client| test_client = http_client }
|
||||
expect(test_client).to_not be_nil
|
||||
subject.with('http://example.com') { |http_client| expect(http_client).to be test_client }
|
||||
end
|
||||
|
||||
it 'returns different HTTP clients for different hosts' do
|
||||
test_client = nil
|
||||
|
||||
subject.with('http://example.com') { |http_client| test_client = http_client }
|
||||
expect(test_client).to_not be_nil
|
||||
subject.with('http://example.org') { |http_client| expect(http_client).to_not be test_client }
|
||||
end
|
||||
|
||||
it 'grows to the number of threads accessing it' do
|
||||
stub_request(:get, 'http://example.com/').to_return(status: 200, body: 'Hello!')
|
||||
|
||||
subject
|
||||
|
||||
threads = 20.times.map do |i|
|
||||
Thread.new do
|
||||
20.times do
|
||||
subject.with('http://example.com') do |http_client|
|
||||
http_client.get('/').flush
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
threads.map(&:join)
|
||||
|
||||
expect(subject.size).to be > 1
|
||||
end
|
||||
|
||||
it 'closes idle connections' do
|
||||
stub_request(:get, 'http://example.com/').to_return(status: 200, body: 'Hello!')
|
||||
|
||||
subject.with('http://example.com') do |http_client|
|
||||
http_client.get('/').flush
|
||||
end
|
||||
|
||||
expect(subject.size).to eq 1
|
||||
sleep RequestPool::MAX_IDLE_TIME + 30 + 1
|
||||
expect(subject.size).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
196
spec/lib/spam_check_spec.rb
Normal file
196
spec/lib/spam_check_spec.rb
Normal file
@ -0,0 +1,196 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe SpamCheck do
|
||||
let!(:sender) { Fabricate(:account) }
|
||||
let!(:alice) { Fabricate(:account, username: 'alice') }
|
||||
let!(:bob) { Fabricate(:account, username: 'bob') }
|
||||
|
||||
def status_with_html(text, options = {})
|
||||
status = PostStatusService.new.call(sender, { text: text }.merge(options))
|
||||
status.update_columns(text: Formatter.instance.format(status), local: false)
|
||||
status
|
||||
end
|
||||
|
||||
describe '#hashable_text' do
|
||||
it 'removes mentions from HTML for remote statuses' do
|
||||
status = status_with_html('@alice Hello')
|
||||
expect(described_class.new(status).hashable_text).to eq 'hello'
|
||||
end
|
||||
|
||||
it 'removes mentions from text for local statuses' do
|
||||
status = PostStatusService.new.call(alice, text: "Hey @#{sender.username}, how are you?")
|
||||
expect(described_class.new(status).hashable_text).to eq 'hey , how are you?'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#insufficient_data?' do
|
||||
it 'returns true when there is no text' do
|
||||
status = status_with_html('@alice')
|
||||
expect(described_class.new(status).insufficient_data?).to be true
|
||||
end
|
||||
|
||||
it 'returns false when there is text' do
|
||||
status = status_with_html('@alice h')
|
||||
expect(described_class.new(status).insufficient_data?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#digest' do
|
||||
it 'returns a string' do
|
||||
status = status_with_html('@alice Hello world')
|
||||
expect(described_class.new(status).digest).to be_a String
|
||||
end
|
||||
end
|
||||
|
||||
describe '#spam?' do
|
||||
it 'returns false for a unique status' do
|
||||
status = status_with_html('@alice Hello')
|
||||
expect(described_class.new(status).spam?).to be false
|
||||
end
|
||||
|
||||
it 'returns false for different statuses to the same recipient' do
|
||||
status1 = status_with_html('@alice Hello')
|
||||
described_class.new(status1).remember!
|
||||
status2 = status_with_html('@alice Are you available to talk?')
|
||||
expect(described_class.new(status2).spam?).to be false
|
||||
end
|
||||
|
||||
it 'returns false for statuses with different content warnings' do
|
||||
status1 = status_with_html('@alice Are you available to talk?')
|
||||
described_class.new(status1).remember!
|
||||
status2 = status_with_html('@alice Are you available to talk?', spoiler_text: 'This is a completely different matter than what I was talking about previously, I swear!')
|
||||
expect(described_class.new(status2).spam?).to be false
|
||||
end
|
||||
|
||||
it 'returns false for different statuses to different recipients' do
|
||||
status1 = status_with_html('@alice How is it going?')
|
||||
described_class.new(status1).remember!
|
||||
status2 = status_with_html('@bob Are you okay?')
|
||||
expect(described_class.new(status2).spam?).to be false
|
||||
end
|
||||
|
||||
it 'returns false for very short different statuses to different recipients' do
|
||||
status1 = status_with_html('@alice 🙄')
|
||||
described_class.new(status1).remember!
|
||||
status2 = status_with_html('@bob Huh?')
|
||||
expect(described_class.new(status2).spam?).to be false
|
||||
end
|
||||
|
||||
it 'returns false for statuses with no text' do
|
||||
status1 = status_with_html('@alice')
|
||||
described_class.new(status1).remember!
|
||||
status2 = status_with_html('@bob')
|
||||
expect(described_class.new(status2).spam?).to be false
|
||||
end
|
||||
|
||||
it 'returns true for duplicate statuses to the same recipient' do
|
||||
described_class::THRESHOLD.times do
|
||||
status1 = status_with_html('@alice Hello')
|
||||
described_class.new(status1).remember!
|
||||
end
|
||||
|
||||
status2 = status_with_html('@alice Hello')
|
||||
expect(described_class.new(status2).spam?).to be true
|
||||
end
|
||||
|
||||
it 'returns true for duplicate statuses to different recipients' do
|
||||
described_class::THRESHOLD.times do
|
||||
status1 = status_with_html('@alice Hello')
|
||||
described_class.new(status1).remember!
|
||||
end
|
||||
|
||||
status2 = status_with_html('@bob Hello')
|
||||
expect(described_class.new(status2).spam?).to be true
|
||||
end
|
||||
|
||||
it 'returns true for nearly identical statuses with random numbers' do
|
||||
source_text = 'Sodium, atomic number 11, was first isolated by Humphry Davy in 1807. A chemical component of salt, he named it Na in honor of the saltiest region on earth, North America.'
|
||||
|
||||
described_class::THRESHOLD.times do
|
||||
status1 = status_with_html('@alice ' + source_text + ' 1234')
|
||||
described_class.new(status1).remember!
|
||||
end
|
||||
|
||||
status2 = status_with_html('@bob ' + source_text + ' 9568')
|
||||
expect(described_class.new(status2).spam?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#skip?' do
|
||||
it 'returns true when the sender is already silenced' do
|
||||
status = status_with_html('@alice Hello')
|
||||
sender.silence!
|
||||
expect(described_class.new(status).skip?).to be true
|
||||
end
|
||||
|
||||
it 'returns true when the mentioned person follows the sender' do
|
||||
status = status_with_html('@alice Hello')
|
||||
alice.follow!(sender)
|
||||
expect(described_class.new(status).skip?).to be true
|
||||
end
|
||||
|
||||
it 'returns false when even one mentioned person doesn\'t follow the sender' do
|
||||
status = status_with_html('@alice @bob Hello')
|
||||
alice.follow!(sender)
|
||||
expect(described_class.new(status).skip?).to be false
|
||||
end
|
||||
|
||||
it 'returns true when the sender is replying to a status that mentions the sender' do
|
||||
parent = PostStatusService.new.call(alice, text: "Hey @#{sender.username}, how are you?")
|
||||
status = status_with_html('@alice @bob Hello', thread: parent)
|
||||
expect(described_class.new(status).skip?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#remember!' do
|
||||
let(:status) { status_with_html('@alice') }
|
||||
let(:spam_check) { described_class.new(status) }
|
||||
let(:redis_key) { spam_check.send(:redis_key) }
|
||||
|
||||
it 'remembers' do
|
||||
expect(Redis.current.exists(redis_key)).to be true
|
||||
spam_check.remember!
|
||||
expect(Redis.current.exists(redis_key)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reset!' do
|
||||
let(:status) { status_with_html('@alice') }
|
||||
let(:spam_check) { described_class.new(status) }
|
||||
let(:redis_key) { spam_check.send(:redis_key) }
|
||||
|
||||
before do
|
||||
spam_check.remember!
|
||||
end
|
||||
|
||||
it 'resets' do
|
||||
expect(Redis.current.exists(redis_key)).to be true
|
||||
spam_check.reset!
|
||||
expect(Redis.current.exists(redis_key)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#flag!' do
|
||||
let!(:status1) { status_with_html('@alice General Kenobi you are a bold one') }
|
||||
let!(:status2) { status_with_html('@alice @bob General Kenobi, you are a bold one') }
|
||||
|
||||
before do
|
||||
described_class.new(status1).remember!
|
||||
described_class.new(status2).flag!
|
||||
end
|
||||
|
||||
it 'silences the account' do
|
||||
expect(sender.silenced?).to be true
|
||||
end
|
||||
|
||||
it 'creates a report about the account' do
|
||||
expect(sender.targeted_reports.unresolved.count).to eq 1
|
||||
end
|
||||
|
||||
it 'attaches both matching statuses to the report' do
|
||||
expect(sender.targeted_reports.first.status_ids).to include(status1.id, status2.id)
|
||||
end
|
||||
end
|
||||
end
|
@ -25,15 +25,6 @@ describe StatusFinder do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a stream entry url' do
|
||||
let(:stream_entry) { Fabricate(:stream_entry) }
|
||||
let(:url) { account_stream_entry_url(stream_entry.account, stream_entry) }
|
||||
|
||||
it 'finds the stream entry' do
|
||||
expect(subject.status).to eq(stream_entry.status)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a remote url even if id exists on local' do
|
||||
let(:status) { Fabricate(:status) }
|
||||
let(:url) { "https://example.com/users/test/statuses/#{status.id}" }
|
||||
|
@ -119,46 +119,4 @@ RSpec.describe TagManager do
|
||||
expect(TagManager.instance.same_acct?('username', 'incorrect@Cb6E6126.nGrOk.Io')).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#url_for' do
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
|
||||
subject { TagManager.instance.url_for(target) }
|
||||
|
||||
context 'activity object' do
|
||||
let(:target) { Fabricate(:status, account: alice, reblog: Fabricate(:status)).stream_entry }
|
||||
|
||||
it 'returns the unique tag for status' do
|
||||
expect(target.object_type).to eq :activity
|
||||
is_expected.to eq "https://cb6e6126.ngrok.io/@alice/#{target.id}"
|
||||
end
|
||||
end
|
||||
|
||||
context 'comment object' do
|
||||
let(:target) { Fabricate(:status, account: alice, reply: true) }
|
||||
|
||||
it 'returns the unique tag for status' do
|
||||
expect(target.object_type).to eq :comment
|
||||
is_expected.to eq "https://cb6e6126.ngrok.io/@alice/#{target.id}"
|
||||
end
|
||||
end
|
||||
|
||||
context 'note object' do
|
||||
let(:target) { Fabricate(:status, account: alice, reply: false, thread: nil) }
|
||||
|
||||
it 'returns the unique tag for status' do
|
||||
expect(target.object_type).to eq :note
|
||||
is_expected.to eq "https://cb6e6126.ngrok.io/@alice/#{target.id}"
|
||||
end
|
||||
end
|
||||
|
||||
context 'person object' do
|
||||
let(:target) { alice }
|
||||
|
||||
it 'returns the URL for account' do
|
||||
expect(target.object_type).to eq :person
|
||||
is_expected.to eq 'https://cb6e6126.ngrok.io/@alice'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -18,6 +18,21 @@ class UserMailerPreview < ActionMailer::Preview
|
||||
UserMailer.password_change(User.first)
|
||||
end
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/two_factor_disabled
|
||||
def two_factor_disabled
|
||||
UserMailer.two_factor_disabled(User.first)
|
||||
end
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/two_factor_enabled
|
||||
def two_factor_enabled
|
||||
UserMailer.two_factor_enabled(User.first)
|
||||
end
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/two_factor_recovery_codes_changed
|
||||
def two_factor_recovery_codes_changed
|
||||
UserMailer.two_factor_recovery_codes_changed(User.first)
|
||||
end
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/reconfirmation_instructions
|
||||
def reconfirmation_instructions
|
||||
user = User.first
|
||||
@ -42,6 +57,6 @@ class UserMailerPreview < ActionMailer::Preview
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/warning
|
||||
def warning
|
||||
UserMailer.warning(User.first, AccountWarning.new(text: '', action: :silence))
|
||||
UserMailer.warning(User.first, AccountWarning.new(text: '', action: :silence), [Status.first.id])
|
||||
end
|
||||
end
|
||||
|
5
spec/models/account_alias_spec.rb
Normal file
5
spec/models/account_alias_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountAlias, type: :model do
|
||||
|
||||
end
|
5
spec/models/account_migration_spec.rb
Normal file
5
spec/models/account_migration_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountMigration, type: :model do
|
||||
|
||||
end
|
@ -450,7 +450,7 @@ RSpec.describe Account, type: :model do
|
||||
describe '.domains' do
|
||||
it 'returns domains' do
|
||||
Fabricate(:account, domain: 'domain')
|
||||
expect(Account.domains).to match_array(['domain'])
|
||||
expect(Account.remote.domains).to match_array(['domain'])
|
||||
end
|
||||
end
|
||||
|
||||
@ -583,26 +583,43 @@ RSpec.describe Account, type: :model do
|
||||
expect(account.valid?).to be true
|
||||
end
|
||||
|
||||
it 'is valid if we are creating an instance actor account with a period' do
|
||||
account = Fabricate.build(:account, id: -99, actor_type: 'Application', locked: true, username: 'example.com')
|
||||
expect(account.valid?).to be true
|
||||
end
|
||||
|
||||
it 'is valid if we are creating a possibly-conflicting instance actor account' do
|
||||
account_1 = Fabricate(:account, username: 'examplecom')
|
||||
account_2 = Fabricate.build(:account, id: -99, actor_type: 'Application', locked: true, username: 'example.com')
|
||||
expect(account_2.valid?).to be true
|
||||
end
|
||||
|
||||
it 'is invalid if the username doesn\'t only contains letters, numbers and underscores' do
|
||||
account = Fabricate.build(:account, username: 'the-doctor')
|
||||
account.valid?
|
||||
expect(account).to model_have_error_on_field(:username)
|
||||
end
|
||||
|
||||
it 'is invalid if the username contains a period' do
|
||||
account = Fabricate.build(:account, username: 'the.doctor')
|
||||
account.valid?
|
||||
expect(account).to model_have_error_on_field(:username)
|
||||
end
|
||||
|
||||
it 'is invalid if the username is longer then 30 characters' do
|
||||
account = Fabricate.build(:account, username: Faker::Lorem.characters(31))
|
||||
account = Fabricate.build(:account, username: Faker::Lorem.characters(number: 31))
|
||||
account.valid?
|
||||
expect(account).to model_have_error_on_field(:username)
|
||||
end
|
||||
|
||||
it 'is invalid if the display name is longer than 30 characters' do
|
||||
account = Fabricate.build(:account, display_name: Faker::Lorem.characters(31))
|
||||
account = Fabricate.build(:account, display_name: Faker::Lorem.characters(number: 31))
|
||||
account.valid?
|
||||
expect(account).to model_have_error_on_field(:display_name)
|
||||
end
|
||||
|
||||
it 'is invalid if the note is longer than 500 characters' do
|
||||
account = Fabricate.build(:account, note: Faker::Lorem.characters(501))
|
||||
account = Fabricate.build(:account, note: Faker::Lorem.characters(number: 501))
|
||||
account.valid?
|
||||
expect(account).to model_have_error_on_field(:note)
|
||||
end
|
||||
@ -636,19 +653,19 @@ RSpec.describe Account, type: :model do
|
||||
end
|
||||
|
||||
it 'is valid even if the username is longer then 30 characters' do
|
||||
account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(31))
|
||||
account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(number: 31))
|
||||
account.valid?
|
||||
expect(account).not_to model_have_error_on_field(:username)
|
||||
end
|
||||
|
||||
it 'is valid even if the display name is longer than 30 characters' do
|
||||
account = Fabricate.build(:account, domain: 'domain', display_name: Faker::Lorem.characters(31))
|
||||
account = Fabricate.build(:account, domain: 'domain', display_name: Faker::Lorem.characters(number: 31))
|
||||
account.valid?
|
||||
expect(account).not_to model_have_error_on_field(:display_name)
|
||||
end
|
||||
|
||||
it 'is valid even if the note is longer than 500 characters' do
|
||||
account = Fabricate.build(:account, domain: 'domain', note: Faker::Lorem.characters(501))
|
||||
account = Fabricate.build(:account, domain: 'domain', note: Faker::Lorem.characters(number: 501))
|
||||
account.valid?
|
||||
expect(account).not_to model_have_error_on_field(:note)
|
||||
end
|
||||
@ -665,7 +682,7 @@ RSpec.describe Account, type: :model do
|
||||
{ username: 'b', domain: 'b' },
|
||||
].map(&method(:Fabricate).curry(2).call(:account))
|
||||
|
||||
expect(Account.alphabetic).to eq matches
|
||||
expect(Account.where('id > 0').alphabetic).to eq matches
|
||||
end
|
||||
end
|
||||
|
||||
@ -732,7 +749,7 @@ RSpec.describe Account, type: :model do
|
||||
2.times { Fabricate(:account, domain: 'example.com') }
|
||||
Fabricate(:account, domain: 'example2.com')
|
||||
|
||||
results = Account.by_domain_accounts
|
||||
results = Account.where('id > 0').by_domain_accounts
|
||||
expect(results.length).to eq 2
|
||||
expect(results.first.domain).to eq 'example.com'
|
||||
expect(results.first.accounts_count).to eq 2
|
||||
@ -745,7 +762,7 @@ RSpec.describe Account, type: :model do
|
||||
it 'returns an array of accounts who do not have a domain' do
|
||||
account_1 = Fabricate(:account, domain: nil)
|
||||
account_2 = Fabricate(:account, domain: 'example.com')
|
||||
expect(Account.local).to match_array([account_1])
|
||||
expect(Account.where('id > 0').local).to match_array([account_1])
|
||||
end
|
||||
end
|
||||
|
||||
@ -756,14 +773,14 @@ RSpec.describe Account, type: :model do
|
||||
matches[index] = Fabricate(:account, domain: matches[index])
|
||||
end
|
||||
|
||||
expect(Account.partitioned).to match_array(matches)
|
||||
expect(Account.where('id > 0').partitioned).to match_array(matches)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'recent' do
|
||||
it 'returns a relation of accounts sorted by recent creation' do
|
||||
matches = 2.times.map { Fabricate(:account) }
|
||||
expect(Account.recent).to match_array(matches)
|
||||
expect(Account.where('id > 0').recent).to match_array(matches)
|
||||
end
|
||||
end
|
||||
|
||||
@ -787,7 +804,7 @@ RSpec.describe Account, type: :model do
|
||||
context 'when is local' do
|
||||
# Test disabled because test environment omits autogenerating keys for performance
|
||||
xit 'generates keys' do
|
||||
account = Account.create!(domain: nil, username: Faker::Internet.user_name(nil, ['_']))
|
||||
account = Account.create!(domain: nil, username: Faker::Internet.user_name(separators: ['_']))
|
||||
expect(account.keypair.private?).to eq true
|
||||
end
|
||||
end
|
||||
@ -795,12 +812,12 @@ RSpec.describe Account, type: :model do
|
||||
context 'when is remote' do
|
||||
it 'does not generate keys' do
|
||||
key = OpenSSL::PKey::RSA.new(1024).public_key
|
||||
account = Account.create!(domain: 'remote', username: Faker::Internet.user_name(nil, ['_']), public_key: key.to_pem)
|
||||
account = Account.create!(domain: 'remote', username: Faker::Internet.user_name(separators: ['_']), public_key: key.to_pem)
|
||||
expect(account.keypair.params).to eq key.params
|
||||
end
|
||||
|
||||
it 'normalizes domain' do
|
||||
account = Account.create!(domain: 'にゃん', username: Faker::Internet.user_name(nil, ['_']))
|
||||
account = Account.create!(domain: 'にゃん', username: Faker::Internet.user_name(separators: ['_']))
|
||||
expect(account.domain).to eq 'xn--r9j5b5b'
|
||||
end
|
||||
end
|
||||
|
@ -1,4 +1,57 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountStat, type: :model do
|
||||
describe '#increment_count!' do
|
||||
it 'increments the count' do
|
||||
account_stat = AccountStat.create(account: Fabricate(:account))
|
||||
expect(account_stat.followers_count).to eq 0
|
||||
account_stat.increment_count!(:followers_count)
|
||||
expect(account_stat.followers_count).to eq 1
|
||||
end
|
||||
|
||||
it 'increments the count in multi-threaded an environment' do
|
||||
account_stat = AccountStat.create(account: Fabricate(:account), statuses_count: 0)
|
||||
increment_by = 15
|
||||
wait_for_start = true
|
||||
|
||||
threads = Array.new(increment_by) do
|
||||
Thread.new do
|
||||
true while wait_for_start
|
||||
AccountStat.find(account_stat.id).increment_count!(:statuses_count)
|
||||
end
|
||||
end
|
||||
|
||||
wait_for_start = false
|
||||
threads.each(&:join)
|
||||
|
||||
expect(account_stat.reload.statuses_count).to eq increment_by
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decrement_count!' do
|
||||
it 'decrements the count' do
|
||||
account_stat = AccountStat.create(account: Fabricate(:account), followers_count: 15)
|
||||
expect(account_stat.followers_count).to eq 15
|
||||
account_stat.decrement_count!(:followers_count)
|
||||
expect(account_stat.followers_count).to eq 14
|
||||
end
|
||||
|
||||
it 'decrements the count in multi-threaded an environment' do
|
||||
account_stat = AccountStat.create(account: Fabricate(:account), statuses_count: 15)
|
||||
decrement_by = 10
|
||||
wait_for_start = true
|
||||
|
||||
threads = Array.new(decrement_by) do
|
||||
Thread.new do
|
||||
true while wait_for_start
|
||||
AccountStat.find(account_stat.id).decrement_count!(:statuses_count)
|
||||
end
|
||||
end
|
||||
|
||||
wait_for_start = false
|
||||
threads.each(&:join)
|
||||
|
||||
expect(account_stat.reload.statuses_count).to eq 5
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -58,8 +58,8 @@ RSpec.describe Admin::AccountAction, type: :model do
|
||||
end.to change { Admin::ActionLog.count }.by 1
|
||||
end
|
||||
|
||||
it 'calls queue_email!' do
|
||||
expect(account_action).to receive(:queue_email!)
|
||||
it 'calls process_email!' do
|
||||
expect(account_action).to receive(:process_email!)
|
||||
subject
|
||||
end
|
||||
|
||||
|
@ -1,63 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Streamable do
|
||||
class Parent
|
||||
def title; end
|
||||
|
||||
def target; end
|
||||
|
||||
def thread; end
|
||||
|
||||
def self.has_one(*); end
|
||||
|
||||
def self.after_create; end
|
||||
end
|
||||
|
||||
class Child < Parent
|
||||
include Streamable
|
||||
end
|
||||
|
||||
child = Child.new
|
||||
|
||||
describe '#title' do
|
||||
it 'calls Parent#title' do
|
||||
expect_any_instance_of(Parent).to receive(:title)
|
||||
child.title
|
||||
end
|
||||
end
|
||||
|
||||
describe '#content' do
|
||||
it 'calls #title' do
|
||||
expect_any_instance_of(Parent).to receive(:title)
|
||||
child.content
|
||||
end
|
||||
end
|
||||
|
||||
describe '#target' do
|
||||
it 'calls Parent#target' do
|
||||
expect_any_instance_of(Parent).to receive(:target)
|
||||
child.target
|
||||
end
|
||||
end
|
||||
|
||||
describe '#object_type' do
|
||||
it 'returns :activity' do
|
||||
expect(child.object_type).to eq :activity
|
||||
end
|
||||
end
|
||||
|
||||
describe '#thread' do
|
||||
it 'calls Parent#thread' do
|
||||
expect_any_instance_of(Parent).to receive(:thread)
|
||||
child.thread
|
||||
end
|
||||
end
|
||||
|
||||
describe '#hidden?' do
|
||||
it 'returns false' do
|
||||
expect(child.hidden?).to be false
|
||||
end
|
||||
end
|
||||
end
|
5
spec/models/custom_emoji_category_spec.rb
Normal file
5
spec/models/custom_emoji_category_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe CustomEmojiCategory, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
5
spec/models/domain_allow_spec.rb
Normal file
5
spec/models/domain_allow_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe DomainAllow, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
@ -41,12 +41,12 @@ describe Form::StatusBatch do
|
||||
|
||||
it 'call RemovalWorker' do
|
||||
form.save
|
||||
expect(RemovalWorker).to have_received(:perform_async).with(status.id)
|
||||
expect(RemovalWorker).to have_received(:perform_async).with(status.id, immediate: true)
|
||||
end
|
||||
|
||||
it 'do not call RemovalWorker' do
|
||||
form.save
|
||||
expect(RemovalWorker).not_to have_received(:perform_async).with(another_status.id)
|
||||
expect(RemovalWorker).not_to have_received(:perform_async).with(another_status.id, immediate: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
5
spec/models/marker_spec.rb
Normal file
5
spec/models/marker_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Marker, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
@ -1,5 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe PollVote, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
describe '#object_type' do
|
||||
let(:poll_vote) { Fabricate.build(:poll_vote) }
|
||||
|
||||
it 'returns :vote' do
|
||||
expect(poll_vote.object_type).to eq :vote
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -61,7 +61,7 @@ RSpec.describe RemoteFollow do
|
||||
subject { remote_follow.subscribe_address_for(account) }
|
||||
|
||||
it 'returns subscribe address' do
|
||||
is_expected.to eq 'https://quitter.no/main/ostatussub?profile=alice%40cb6e6126.ngrok.io'
|
||||
is_expected.to eq 'https://quitter.no/main/ostatussub?profile=https%3A%2F%2Fcb6e6126.ngrok.io%2Fusers%2Falice'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,143 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe RemoteProfile do
|
||||
let(:remote_profile) { RemoteProfile.new(body) }
|
||||
let(:body) do
|
||||
<<-XML
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<author>John</author>
|
||||
XML
|
||||
end
|
||||
|
||||
describe '.initialize' do
|
||||
it 'calls Nokogiri::XML.parse' do
|
||||
expect(Nokogiri::XML).to receive(:parse).with(body, nil, 'utf-8')
|
||||
RemoteProfile.new(body)
|
||||
end
|
||||
|
||||
it 'sets document' do
|
||||
remote_profile = RemoteProfile.new(body)
|
||||
expect(remote_profile).not_to be nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#root' do
|
||||
let(:document) { remote_profile.document }
|
||||
|
||||
it 'callse document.at_xpath' do
|
||||
expect(document).to receive(:at_xpath).with(
|
||||
'/atom:feed|/atom:entry',
|
||||
atom: OStatus::TagManager::XMLNS
|
||||
)
|
||||
|
||||
remote_profile.root
|
||||
end
|
||||
end
|
||||
|
||||
describe '#author' do
|
||||
let(:root) { remote_profile.root }
|
||||
|
||||
it 'calls root.at_xpath' do
|
||||
expect(root).to receive(:at_xpath).with(
|
||||
'./atom:author|./dfrn:owner',
|
||||
atom: OStatus::TagManager::XMLNS,
|
||||
dfrn: OStatus::TagManager::DFRN_XMLNS
|
||||
)
|
||||
|
||||
remote_profile.author
|
||||
end
|
||||
end
|
||||
|
||||
describe '#hub_link' do
|
||||
let(:root) { remote_profile.root }
|
||||
|
||||
it 'calls #link_href_from_xml' do
|
||||
expect(remote_profile).to receive(:link_href_from_xml).with(root, 'hub')
|
||||
remote_profile.hub_link
|
||||
end
|
||||
end
|
||||
|
||||
describe '#display_name' do
|
||||
let(:author) { remote_profile.author }
|
||||
|
||||
it 'calls author.at_xpath.content' do
|
||||
expect(author).to receive_message_chain(:at_xpath, :content).with(
|
||||
'./poco:displayName',
|
||||
poco: OStatus::TagManager::POCO_XMLNS
|
||||
).with(no_args)
|
||||
|
||||
remote_profile.display_name
|
||||
end
|
||||
end
|
||||
|
||||
describe '#note' do
|
||||
let(:author) { remote_profile.author }
|
||||
|
||||
it 'calls author.at_xpath.content' do
|
||||
expect(author).to receive_message_chain(:at_xpath, :content).with(
|
||||
'./atom:summary|./poco:note',
|
||||
atom: OStatus::TagManager::XMLNS,
|
||||
poco: OStatus::TagManager::POCO_XMLNS
|
||||
).with(no_args)
|
||||
|
||||
remote_profile.note
|
||||
end
|
||||
end
|
||||
|
||||
describe '#scope' do
|
||||
let(:author) { remote_profile.author }
|
||||
|
||||
it 'calls author.at_xpath.content' do
|
||||
expect(author).to receive_message_chain(:at_xpath, :content).with(
|
||||
'./mastodon:scope',
|
||||
mastodon: OStatus::TagManager::MTDN_XMLNS
|
||||
).with(no_args)
|
||||
|
||||
remote_profile.scope
|
||||
end
|
||||
end
|
||||
|
||||
describe '#avatar' do
|
||||
let(:author) { remote_profile.author }
|
||||
|
||||
it 'calls #link_href_from_xml' do
|
||||
expect(remote_profile).to receive(:link_href_from_xml).with(author, 'avatar')
|
||||
remote_profile.avatar
|
||||
end
|
||||
end
|
||||
|
||||
describe '#header' do
|
||||
let(:author) { remote_profile.author }
|
||||
|
||||
it 'calls #link_href_from_xml' do
|
||||
expect(remote_profile).to receive(:link_href_from_xml).with(author, 'header')
|
||||
remote_profile.header
|
||||
end
|
||||
end
|
||||
|
||||
describe '#locked?' do
|
||||
before do
|
||||
allow(remote_profile).to receive(:scope).and_return(scope)
|
||||
end
|
||||
|
||||
subject { remote_profile.locked? }
|
||||
|
||||
context 'scope is private' do
|
||||
let(:scope) { 'private' }
|
||||
|
||||
it 'returns true' do
|
||||
is_expected.to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'scope is not private' do
|
||||
let(:scope) { 'public' }
|
||||
|
||||
it 'returns false' do
|
||||
is_expected.to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -125,7 +125,7 @@ describe Report do
|
||||
end
|
||||
|
||||
it 'is invalid if comment is longer than 1000 characters' do
|
||||
report = Fabricate.build(:report, comment: Faker::Lorem.characters(1001))
|
||||
report = Fabricate.build(:report, comment: Faker::Lorem.characters(number: 1001))
|
||||
report.valid?
|
||||
expect(report).to model_have_error_on_field(:comment)
|
||||
end
|
||||
|
@ -339,56 +339,6 @@ RSpec.describe Status, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe '.as_direct_timeline' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:followed) { Fabricate(:account) }
|
||||
let(:not_followed) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
Fabricate(:follow, account: account, target_account: followed)
|
||||
|
||||
@self_public_status = Fabricate(:status, account: account, visibility: :public)
|
||||
@self_direct_status = Fabricate(:status, account: account, visibility: :direct)
|
||||
@followed_public_status = Fabricate(:status, account: followed, visibility: :public)
|
||||
@followed_direct_status = Fabricate(:status, account: followed, visibility: :direct)
|
||||
@not_followed_direct_status = Fabricate(:status, account: not_followed, visibility: :direct)
|
||||
|
||||
@results = Status.as_direct_timeline(account)
|
||||
end
|
||||
|
||||
it 'does not include public statuses from self' do
|
||||
expect(@results).to_not include(@self_public_status)
|
||||
end
|
||||
|
||||
it 'includes direct statuses from self' do
|
||||
expect(@results).to include(@self_direct_status)
|
||||
end
|
||||
|
||||
it 'does not include public statuses from followed' do
|
||||
expect(@results).to_not include(@followed_public_status)
|
||||
end
|
||||
|
||||
it 'does not include direct statuses not mentioning recipient from followed' do
|
||||
expect(@results).to_not include(@followed_direct_status)
|
||||
end
|
||||
|
||||
it 'does not include direct statuses not mentioning recipient from non-followed' do
|
||||
expect(@results).to_not include(@not_followed_direct_status)
|
||||
end
|
||||
|
||||
it 'includes direct statuses mentioning recipient from followed' do
|
||||
Fabricate(:mention, account: account, status: @followed_direct_status)
|
||||
results2 = Status.as_direct_timeline(account)
|
||||
expect(results2).to include(@followed_direct_status)
|
||||
end
|
||||
|
||||
it 'includes direct statuses mentioning recipient from non-followed' do
|
||||
Fabricate(:mention, account: account, status: @not_followed_direct_status)
|
||||
results2 = Status.as_direct_timeline(account)
|
||||
expect(results2).to include(@not_followed_direct_status)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.as_public_timeline' do
|
||||
it 'only includes statuses with public visibility' do
|
||||
public_status = Fabricate(:status, visibility: :public)
|
||||
|
@ -1,192 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe StreamEntry, type: :model do
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
let(:bob) { Fabricate(:account, username: 'bob') }
|
||||
let(:status) { Fabricate(:status, account: alice) }
|
||||
let(:reblog) { Fabricate(:status, account: bob, reblog: status) }
|
||||
let(:reply) { Fabricate(:status, account: bob, thread: status) }
|
||||
let(:stream_entry) { Fabricate(:stream_entry, activity: activity) }
|
||||
let(:activity) { reblog }
|
||||
|
||||
describe '#object_type' do
|
||||
before do
|
||||
allow(stream_entry).to receive(:orphaned?).and_return(orphaned)
|
||||
allow(stream_entry).to receive(:targeted?).and_return(targeted)
|
||||
end
|
||||
|
||||
subject { stream_entry.object_type }
|
||||
|
||||
context 'orphaned? is true' do
|
||||
let(:orphaned) { true }
|
||||
let(:targeted) { false }
|
||||
|
||||
it 'returns :activity' do
|
||||
is_expected.to be :activity
|
||||
end
|
||||
end
|
||||
|
||||
context 'targeted? is true' do
|
||||
let(:orphaned) { false }
|
||||
let(:targeted) { true }
|
||||
|
||||
it 'returns :activity' do
|
||||
is_expected.to be :activity
|
||||
end
|
||||
end
|
||||
|
||||
context 'orphaned? and targeted? are false' do
|
||||
let(:orphaned) { false }
|
||||
let(:targeted) { false }
|
||||
|
||||
context 'activity is reblog' do
|
||||
let(:activity) { reblog }
|
||||
|
||||
it 'returns :note' do
|
||||
is_expected.to be :note
|
||||
end
|
||||
end
|
||||
|
||||
context 'activity is reply' do
|
||||
let(:activity) { reply }
|
||||
|
||||
it 'returns :comment' do
|
||||
is_expected.to be :comment
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#verb' do
|
||||
before do
|
||||
allow(stream_entry).to receive(:orphaned?).and_return(orphaned)
|
||||
end
|
||||
|
||||
subject { stream_entry.verb }
|
||||
|
||||
context 'orphaned? is true' do
|
||||
let(:orphaned) { true }
|
||||
|
||||
it 'returns :delete' do
|
||||
is_expected.to be :delete
|
||||
end
|
||||
end
|
||||
|
||||
context 'orphaned? is false' do
|
||||
let(:orphaned) { false }
|
||||
|
||||
context 'activity is reblog' do
|
||||
let(:activity) { reblog }
|
||||
|
||||
it 'returns :share' do
|
||||
is_expected.to be :share
|
||||
end
|
||||
end
|
||||
|
||||
context 'activity is reply' do
|
||||
let(:activity) { reply }
|
||||
|
||||
it 'returns :post' do
|
||||
is_expected.to be :post
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#mentions' do
|
||||
before do
|
||||
allow(stream_entry).to receive(:orphaned?).and_return(orphaned)
|
||||
end
|
||||
|
||||
subject { stream_entry.mentions }
|
||||
|
||||
context 'orphaned? is true' do
|
||||
let(:orphaned) { true }
|
||||
|
||||
it 'returns []' do
|
||||
is_expected.to eq []
|
||||
end
|
||||
end
|
||||
|
||||
context 'orphaned? is false' do
|
||||
before do
|
||||
reblog.mentions << Fabricate(:mention, account: alice)
|
||||
reblog.mentions << Fabricate(:mention, account: bob)
|
||||
end
|
||||
|
||||
let(:orphaned) { false }
|
||||
|
||||
it 'returns [Account] includes alice and bob' do
|
||||
is_expected.to eq [alice, bob]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#targeted?' do
|
||||
it 'returns true for a reblog' do
|
||||
expect(reblog.stream_entry.targeted?).to be true
|
||||
end
|
||||
|
||||
it 'returns false otherwise' do
|
||||
expect(status.stream_entry.targeted?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#threaded?' do
|
||||
it 'returns true for a reply' do
|
||||
expect(reply.stream_entry.threaded?).to be true
|
||||
end
|
||||
|
||||
it 'returns false otherwise' do
|
||||
expect(status.stream_entry.threaded?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe 'delegated methods' do
|
||||
context 'with a nil status' do
|
||||
subject { described_class.new(status: nil) }
|
||||
|
||||
it 'returns nil for target' do
|
||||
expect(subject.target).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil for title' do
|
||||
expect(subject.title).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil for content' do
|
||||
expect(subject.content).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil for thread' do
|
||||
expect(subject.thread).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a real status' do
|
||||
let(:original) { Fabricate(:status, text: 'Test status') }
|
||||
let(:status) { Fabricate(:status, reblog: original, thread: original) }
|
||||
subject { described_class.new(status: status) }
|
||||
|
||||
it 'delegates target' do
|
||||
expect(status.target).not_to be_nil
|
||||
expect(subject.target).to eq(status.target)
|
||||
end
|
||||
|
||||
it 'delegates title' do
|
||||
expect(status.title).not_to be_nil
|
||||
expect(subject.title).to eq(status.title)
|
||||
end
|
||||
|
||||
it 'delegates content' do
|
||||
expect(status.content).not_to be_nil
|
||||
expect(subject.content).to eq(status.content)
|
||||
end
|
||||
|
||||
it 'delegates thread' do
|
||||
expect(status.thread).not_to be_nil
|
||||
expect(subject.thread).to eq(status.thread)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,67 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Subscription, type: :model do
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
|
||||
subject { Fabricate(:subscription, account: alice) }
|
||||
|
||||
describe '#expired?' do
|
||||
it 'return true when expires_at is past' do
|
||||
subject.expires_at = 2.days.ago
|
||||
expect(subject.expired?).to be true
|
||||
end
|
||||
|
||||
it 'return false when expires_at is future' do
|
||||
subject.expires_at = 2.days.from_now
|
||||
expect(subject.expired?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe 'lease_seconds' do
|
||||
it 'returns the time remaining until expiration' do
|
||||
datetime = 1.day.from_now
|
||||
subscription = Subscription.new(expires_at: datetime)
|
||||
travel_to(datetime - 12.hours) do
|
||||
expect(subscription.lease_seconds).to eq(12.hours)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'lease_seconds=' do
|
||||
it 'sets expires_at to min expiration when small value is provided' do
|
||||
subscription = Subscription.new
|
||||
datetime = 1.day.from_now
|
||||
too_low = Subscription::MIN_EXPIRATION - 1000
|
||||
travel_to(datetime) do
|
||||
subscription.lease_seconds = too_low
|
||||
end
|
||||
|
||||
expected = datetime + Subscription::MIN_EXPIRATION.seconds
|
||||
expect(subscription.expires_at).to be_within(1.0).of(expected)
|
||||
end
|
||||
|
||||
it 'sets expires_at to value when valid value is provided' do
|
||||
subscription = Subscription.new
|
||||
datetime = 1.day.from_now
|
||||
valid = Subscription::MIN_EXPIRATION + 1000
|
||||
travel_to(datetime) do
|
||||
subscription.lease_seconds = valid
|
||||
end
|
||||
|
||||
expected = datetime + valid.seconds
|
||||
expect(subscription.expires_at).to be_within(1.0).of(expected)
|
||||
end
|
||||
|
||||
it 'sets expires_at to max expiration when large value is provided' do
|
||||
subscription = Subscription.new
|
||||
datetime = 1.day.from_now
|
||||
too_high = Subscription::MAX_EXPIRATION + 1000
|
||||
travel_to(datetime) do
|
||||
subscription.lease_seconds = too_high
|
||||
end
|
||||
|
||||
expected = datetime + Subscription::MAX_EXPIRATION.seconds
|
||||
expect(subscription.expires_at).to be_within(1.0).of(expected)
|
||||
end
|
||||
end
|
||||
end
|
@ -62,6 +62,10 @@ RSpec.describe Tag, type: :model do
|
||||
expect(subject.match('hello #one·two·three').to_s).to eq ' #one·two·three'
|
||||
end
|
||||
|
||||
it 'matches ZWNJ' do
|
||||
expect(subject.match('just add #نرمافزار and').to_s).to eq ' #نرمافزار'
|
||||
end
|
||||
|
||||
it 'does not match middle dots at the start' do
|
||||
expect(subject.match('hello #·one·two·three')).to be_nil
|
||||
end
|
||||
@ -82,6 +86,40 @@ RSpec.describe Tag, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe '.find_normalized' do
|
||||
it 'returns tag for a multibyte case-insensitive name' do
|
||||
upcase_string = 'abcABCabcABCやゆよ'
|
||||
downcase_string = 'abcabcabcabcやゆよ';
|
||||
|
||||
tag = Fabricate(:tag, name: downcase_string)
|
||||
expect(Tag.find_normalized(upcase_string)).to eq tag
|
||||
end
|
||||
end
|
||||
|
||||
describe '.matching_name' do
|
||||
it 'returns tags for multibyte case-insensitive names' do
|
||||
upcase_string = 'abcABCabcABCやゆよ'
|
||||
downcase_string = 'abcabcabcabcやゆよ';
|
||||
|
||||
tag = Fabricate(:tag, name: downcase_string)
|
||||
expect(Tag.matching_name(upcase_string)).to eq [tag]
|
||||
end
|
||||
end
|
||||
|
||||
describe '.find_or_create_by_names' do
|
||||
it 'runs a passed block once per tag regardless of duplicates' do
|
||||
upcase_string = 'abcABCabcABCやゆよ'
|
||||
downcase_string = 'abcabcabcabcやゆよ';
|
||||
count = 0
|
||||
|
||||
Tag.find_or_create_by_names([upcase_string, downcase_string]) do |tag|
|
||||
count += 1
|
||||
end
|
||||
|
||||
expect(count).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
describe '.search_for' do
|
||||
it 'finds tag records with matching names' do
|
||||
tag = Fabricate(:tag, name: "match")
|
||||
@ -102,8 +140,8 @@ RSpec.describe Tag, type: :model do
|
||||
end
|
||||
|
||||
it 'finds the exact matching tag as the first item' do
|
||||
similar_tag = Fabricate(:tag, name: "matchlater")
|
||||
tag = Fabricate(:tag, name: "match")
|
||||
similar_tag = Fabricate(:tag, name: "matchlater", reviewed_at: Time.now.utc)
|
||||
tag = Fabricate(:tag, name: "match", reviewed_at: Time.now.utc)
|
||||
|
||||
results = Tag.search_for("match")
|
||||
|
||||
|
68
spec/models/trending_tags_spec.rb
Normal file
68
spec/models/trending_tags_spec.rb
Normal file
@ -0,0 +1,68 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe TrendingTags do
|
||||
describe '.record_use!' do
|
||||
pending
|
||||
end
|
||||
|
||||
describe '.update!' do
|
||||
let!(:at_time) { Time.now.utc }
|
||||
let!(:tag1) { Fabricate(:tag, name: 'Catstodon') }
|
||||
let!(:tag2) { Fabricate(:tag, name: 'DogsOfMastodon') }
|
||||
let!(:tag3) { Fabricate(:tag, name: 'OCs') }
|
||||
|
||||
before do
|
||||
allow(Redis.current).to receive(:pfcount) do |key|
|
||||
case key
|
||||
when "activity:tags:#{tag1.id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts"
|
||||
2
|
||||
when "activity:tags:#{tag1.id}:#{at_time.beginning_of_day.to_i}:accounts"
|
||||
16
|
||||
when "activity:tags:#{tag2.id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts"
|
||||
0
|
||||
when "activity:tags:#{tag2.id}:#{at_time.beginning_of_day.to_i}:accounts"
|
||||
4
|
||||
when "activity:tags:#{tag3.id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts"
|
||||
13
|
||||
end
|
||||
end
|
||||
|
||||
Redis.current.zadd('trending_tags', 0.9, tag3.id)
|
||||
Redis.current.sadd("trending_tags:used:#{at_time.beginning_of_day.to_i}", [tag1.id, tag2.id])
|
||||
|
||||
tag3.update(max_score: 0.9, max_score_at: (at_time - 1.day).beginning_of_day + 12.hours)
|
||||
|
||||
described_class.update!(at_time)
|
||||
end
|
||||
|
||||
it 'calculates and re-calculates scores' do
|
||||
expect(described_class.get(10, filtered: false)).to eq [tag1, tag3]
|
||||
end
|
||||
|
||||
it 'omits hashtags below threshold' do
|
||||
expect(described_class.get(10, filtered: false)).to_not include(tag2)
|
||||
end
|
||||
|
||||
it 'decays scores' do
|
||||
expect(Redis.current.zscore('trending_tags', tag3.id)).to be < 0.9
|
||||
end
|
||||
end
|
||||
|
||||
describe '.trending?' do
|
||||
let(:tag) { Fabricate(:tag) }
|
||||
|
||||
before do
|
||||
10.times { |i| Redis.current.zadd('trending_tags', i + 1, Fabricate(:tag).id) }
|
||||
end
|
||||
|
||||
it 'returns true if the hashtag is within limit' do
|
||||
Redis.current.zadd('trending_tags', 11, tag.id)
|
||||
expect(described_class.trending?(tag)).to be true
|
||||
end
|
||||
|
||||
it 'returns false if the hashtag is outside the limit' do
|
||||
Redis.current.zadd('trending_tags', 0, tag.id)
|
||||
expect(described_class.trending?(tag)).to be false
|
||||
end
|
||||
end
|
||||
end
|
@ -506,7 +506,7 @@ RSpec.describe User, type: :model do
|
||||
context 'when user is not confirmed' do
|
||||
let(:confirmed_at) { nil }
|
||||
|
||||
it { is_expected.to be false }
|
||||
it { is_expected.to be true }
|
||||
end
|
||||
end
|
||||
|
||||
@ -522,7 +522,7 @@ RSpec.describe User, type: :model do
|
||||
context 'when user is not confirmed' do
|
||||
let(:confirmed_at) { nil }
|
||||
|
||||
it { is_expected.to be false }
|
||||
it { is_expected.to be true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,24 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require 'pundit/rspec'
|
||||
|
||||
RSpec.describe SubscriptionPolicy do
|
||||
let(:subject) { described_class }
|
||||
let(:admin) { Fabricate(:user, admin: true).account }
|
||||
let(:john) { Fabricate(:user).account }
|
||||
|
||||
permissions :index? do
|
||||
context 'admin?' do
|
||||
it 'permits' do
|
||||
expect(subject).to permit(admin, Subscription)
|
||||
end
|
||||
end
|
||||
|
||||
context '!admin?' do
|
||||
it 'denies' do
|
||||
expect(subject).to_not permit(john, Subscription)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -8,7 +8,7 @@ RSpec.describe TagPolicy do
|
||||
let(:admin) { Fabricate(:user, admin: true).account }
|
||||
let(:john) { Fabricate(:user).account }
|
||||
|
||||
permissions :index?, :hide?, :unhide? do
|
||||
permissions :index?, :show?, :update? do
|
||||
context 'staff?' do
|
||||
it 'permits' do
|
||||
expect(subject).to permit(admin, Tag)
|
||||
|
@ -12,7 +12,7 @@ require 'capybara/rspec'
|
||||
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
|
||||
|
||||
ActiveRecord::Migration.maintain_test_schema!
|
||||
WebMock.disable_net_connect!
|
||||
WebMock.disable_net_connect!(allow: Chewy.settings[:host])
|
||||
Redis.current = Redis::Namespace.new("mastodon_test#{ENV['TEST_ENV_NUMBER']}", redis: Redis.current)
|
||||
Sidekiq::Testing.inline!
|
||||
Sidekiq::Logging.logger = nil
|
||||
|
@ -11,16 +11,16 @@ describe 'Link headers' do
|
||||
end
|
||||
|
||||
it 'contains webfinger url in link header' do
|
||||
link_header = link_header_with_type('application/xrd+xml')
|
||||
link_header = link_header_with_type('application/jrd+json')
|
||||
|
||||
expect(link_header.href).to match 'http://www.example.com/.well-known/webfinger?resource=acct%3Atest%40cb6e6126.ngrok.io'
|
||||
expect(link_header.attr_pairs.first).to eq %w(rel lrdd)
|
||||
end
|
||||
|
||||
it 'contains atom url in link header' do
|
||||
link_header = link_header_with_type('application/atom+xml')
|
||||
it 'contains activitypub url in link header' do
|
||||
link_header = link_header_with_type('application/activity+json')
|
||||
|
||||
expect(link_header.href).to eq 'http://www.example.com/users/test.atom'
|
||||
expect(link_header.href).to eq 'https://cb6e6126.ngrok.io/users/test'
|
||||
expect(link_header.attr_pairs.first).to eq %w(rel alternate)
|
||||
end
|
||||
|
||||
|
@ -12,23 +12,6 @@ describe 'The webfinger route' do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'asking for xml format' do
|
||||
it 'returns an xml response for xml format' do
|
||||
get webfinger_url(resource: alice.to_webfinger_s, format: :xml)
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.content_type).to eq 'application/xrd+xml'
|
||||
end
|
||||
|
||||
it 'returns an xml response for xml accept header' do
|
||||
headers = { 'HTTP_ACCEPT' => 'application/xrd+xml' }
|
||||
get webfinger_url(resource: alice.to_webfinger_s), headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.content_type).to eq 'application/xrd+xml'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'asking for json format' do
|
||||
it 'returns a json response for json format' do
|
||||
get webfinger_url(resource: alice.to_webfinger_s, format: :json)
|
||||
|
@ -1,126 +1,56 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe AccountSearchService, type: :service do
|
||||
describe '.call' do
|
||||
describe 'with a query to ignore' do
|
||||
describe '#call' do
|
||||
context 'with a query to ignore' do
|
||||
it 'returns empty array for missing query' do
|
||||
results = subject.call('', nil, limit: 10)
|
||||
|
||||
expect(results).to eq []
|
||||
end
|
||||
it 'returns empty array for hashtag query' do
|
||||
results = subject.call('#tag', nil, limit: 10)
|
||||
|
||||
expect(results).to eq []
|
||||
end
|
||||
it 'returns empty array for limit zero' do
|
||||
Fabricate(:account, username: 'match')
|
||||
|
||||
results = subject.call('match', nil, limit: 0)
|
||||
|
||||
expect(results).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
describe 'searching for a simple term that is not an exact match' do
|
||||
context 'searching for a simple term that is not an exact match' do
|
||||
it 'does not return a nil entry in the array for the exact match' do
|
||||
match = Fabricate(:account, username: 'matchingusername')
|
||||
|
||||
account = Fabricate(:account, username: 'matchingusername')
|
||||
results = subject.call('match', nil, limit: 5)
|
||||
expect(results).to eq [match]
|
||||
|
||||
expect(results).to eq [account]
|
||||
end
|
||||
end
|
||||
|
||||
describe 'searching local and remote users' do
|
||||
describe "when only '@'" do
|
||||
before do
|
||||
allow(Account).to receive(:find_local)
|
||||
allow(Account).to receive(:search_for)
|
||||
subject.call('@', nil, limit: 10)
|
||||
end
|
||||
|
||||
it 'uses find_local with empty query to look for local accounts' do
|
||||
expect(Account).to have_received(:find_local).with('')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when no domain' do
|
||||
before do
|
||||
allow(Account).to receive(:find_local)
|
||||
allow(Account).to receive(:search_for)
|
||||
subject.call('one', nil, limit: 10)
|
||||
end
|
||||
|
||||
it 'uses find_local to look for local accounts' do
|
||||
expect(Account).to have_received(:find_local).with('one')
|
||||
end
|
||||
|
||||
it 'uses search_for to find matches' do
|
||||
expect(Account).to have_received(:search_for).with('one', 10, 0)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when there is a domain' do
|
||||
before do
|
||||
allow(Account).to receive(:find_remote)
|
||||
end
|
||||
|
||||
it 'uses find_remote to look for remote accounts' do
|
||||
subject.call('two@example.com', nil, limit: 10)
|
||||
expect(Account).to have_received(:find_remote).with('two', 'example.com')
|
||||
end
|
||||
|
||||
describe 'and there is no account provided' do
|
||||
it 'uses search_for to find matches' do
|
||||
allow(Account).to receive(:search_for)
|
||||
subject.call('two@example.com', nil, limit: 10, resolve: false)
|
||||
|
||||
expect(Account).to have_received(:search_for).with('two example.com', 10, 0)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'and there is an account provided' do
|
||||
it 'uses advanced_search_for to find matches' do
|
||||
account = Fabricate(:account)
|
||||
allow(Account).to receive(:advanced_search_for)
|
||||
subject.call('two@example.com', account, limit: 10, resolve: false)
|
||||
|
||||
expect(Account).to have_received(:advanced_search_for).with('two example.com', account, 10, nil, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with an exact match' do
|
||||
it 'returns exact match first, and does not return duplicates' do
|
||||
partial = Fabricate(:account, username: 'exactness')
|
||||
exact = Fabricate(:account, username: 'exact')
|
||||
|
||||
results = subject.call('exact', nil, limit: 10)
|
||||
expect(results.size).to eq 2
|
||||
expect(results).to eq [exact, partial]
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when there is a local domain' do
|
||||
context 'when there is a local domain' do
|
||||
around do |example|
|
||||
before = Rails.configuration.x.local_domain
|
||||
|
||||
example.run
|
||||
|
||||
Rails.configuration.x.local_domain = before
|
||||
end
|
||||
|
||||
it 'returns exact match first' do
|
||||
remote = Fabricate(:account, username: 'a', domain: 'remote', display_name: 'e')
|
||||
remote_too = Fabricate(:account, username: 'b', domain: 'remote', display_name: 'e')
|
||||
exact = Fabricate(:account, username: 'e')
|
||||
exact = Fabricate(:account, username: 'e')
|
||||
|
||||
Rails.configuration.x.local_domain = 'example.com'
|
||||
|
||||
results = subject.call('e@example.com', nil, limit: 2)
|
||||
|
||||
expect(results.size).to eq 2
|
||||
expect(results).to eq([exact, remote]).or eq([exact, remote_too])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when there is a domain but no exact match' do
|
||||
context 'when there is a domain but no exact match' do
|
||||
it 'follows the remote account when resolve is true' do
|
||||
service = double(call: nil)
|
||||
allow(ResolveAccountService).to receive(:new).and_return(service)
|
||||
@ -138,23 +68,21 @@ describe AccountSearchService, type: :service do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'should not include suspended accounts' do
|
||||
it 'returns the fuzzy match first, and does not return suspended exacts' do
|
||||
partial = Fabricate(:account, username: 'exactness')
|
||||
exact = Fabricate(:account, username: 'exact', suspended: true)
|
||||
it 'returns the fuzzy match first, and does not return suspended exacts' do
|
||||
partial = Fabricate(:account, username: 'exactness')
|
||||
exact = Fabricate(:account, username: 'exact', suspended: true)
|
||||
results = subject.call('exact', nil, limit: 10)
|
||||
|
||||
results = subject.call('exact', nil, limit: 10)
|
||||
expect(results.size).to eq 1
|
||||
expect(results).to eq [partial]
|
||||
end
|
||||
expect(results.size).to eq 1
|
||||
expect(results).to eq [partial]
|
||||
end
|
||||
|
||||
it "does not return suspended remote accounts" do
|
||||
remote = Fabricate(:account, username: 'a', domain: 'remote', display_name: 'e', suspended: true)
|
||||
it "does not return suspended remote accounts" do
|
||||
remote = Fabricate(:account, username: 'a', domain: 'remote', display_name: 'e', suspended: true)
|
||||
results = subject.call('a@example.com', nil, limit: 2)
|
||||
|
||||
results = subject.call('a@example.com', nil, limit: 2)
|
||||
expect(results.size).to eq 0
|
||||
expect(results).to eq []
|
||||
end
|
||||
expect(results.size).to eq 0
|
||||
expect(results).to eq []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -71,6 +71,39 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Audio object' do
|
||||
let(:object) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: "https://#{valid_domain}/@foo/1234",
|
||||
type: 'Audio',
|
||||
name: 'Nyan Cat 10 hours remix',
|
||||
attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
url: [
|
||||
{
|
||||
type: 'Link',
|
||||
mimeType: 'application/x-bittorrent',
|
||||
href: "https://#{valid_domain}/12345.torrent",
|
||||
},
|
||||
|
||||
{
|
||||
type: 'Link',
|
||||
mimeType: 'text/html',
|
||||
href: "https://#{valid_domain}/watch?v=12345",
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.url).to eq "https://#{valid_domain}/watch?v=12345"
|
||||
expect(strip_tags(status.text)).to eq "Nyan Cat 10 hours remix https://#{valid_domain}/watch?v=12345"
|
||||
end
|
||||
end
|
||||
|
||||
context 'with wrong id' do
|
||||
let(:note) do
|
||||
{
|
||||
|
@ -38,6 +38,15 @@ RSpec.describe AppSignUpService, type: :service do
|
||||
user = User.find_by(id: access_token.resource_owner_id)
|
||||
expect(user).to_not be_nil
|
||||
expect(user.account).to_not be_nil
|
||||
expect(user.invite_request).to be_nil
|
||||
end
|
||||
|
||||
it 'creates an account with invite request text' do
|
||||
access_token = subject.call(app, good_params.merge(reason: 'Foo bar'))
|
||||
expect(access_token).to_not be_nil
|
||||
user = User.find_by(id: access_token.resource_owner_id)
|
||||
expect(user).to_not be_nil
|
||||
expect(user.invite_request&.text).to eq 'Foo bar'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -38,13 +38,6 @@ RSpec.describe AuthorizeFollowService, type: :service do
|
||||
it 'creates follow relation' do
|
||||
expect(bob.following?(sender)).to be true
|
||||
end
|
||||
|
||||
it 'sends a follow request authorization salmon slap' do
|
||||
expect(a_request(:post, "http://salmon.example.com/").with { |req|
|
||||
xml = OStatus2::Salmon.new.unpack(req.body)
|
||||
xml.match(OStatus::TagManager::VERBS[:authorize])
|
||||
}).to have_been_made.once
|
||||
end
|
||||
end
|
||||
|
||||
describe 'remote ActivityPub' do
|
||||
|
@ -14,11 +14,8 @@ RSpec.describe BatchedRemoveStatusService, type: :service do
|
||||
before do
|
||||
allow(Redis.current).to receive_messages(publish: nil)
|
||||
|
||||
stub_request(:post, 'http://example.com/push').to_return(status: 200, body: '', headers: {})
|
||||
stub_request(:post, 'http://example.com/salmon').to_return(status: 200, body: '', headers: {})
|
||||
stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
|
||||
|
||||
Fabricate(:subscription, account: alice, callback_url: 'http://example.com/push', confirmed: true, expires_at: 30.days.from_now)
|
||||
jeff.user.update(current_sign_in_at: Time.zone.now)
|
||||
jeff.follow!(alice)
|
||||
hank.follow!(alice)
|
||||
@ -49,19 +46,6 @@ RSpec.describe BatchedRemoveStatusService, type: :service do
|
||||
expect(Redis.current).to have_received(:publish).with('timeline:public', any_args).at_least(:once)
|
||||
end
|
||||
|
||||
it 'sends PuSH update to PuSH subscribers' do
|
||||
expect(a_request(:post, 'http://example.com/push').with { |req|
|
||||
matches = req.body.match(OStatus::TagManager::VERBS[:delete])
|
||||
}).to have_been_made.at_least_once
|
||||
end
|
||||
|
||||
it 'sends Salmon slap to previously mentioned users' do
|
||||
expect(a_request(:post, "http://example.com/salmon").with { |req|
|
||||
xml = OStatus2::Salmon.new.unpack(req.body)
|
||||
xml.match(OStatus::TagManager::VERBS[:delete])
|
||||
}).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'sends delete activity to followers' do
|
||||
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.at_least_once
|
||||
end
|
||||
|
@ -28,13 +28,6 @@ RSpec.describe BlockService, type: :service do
|
||||
it 'creates a blocking relation' do
|
||||
expect(sender.blocking?(bob)).to be true
|
||||
end
|
||||
|
||||
it 'sends a block salmon slap' do
|
||||
expect(a_request(:post, "http://salmon.example.com/").with { |req|
|
||||
xml = OStatus2::Salmon.new.unpack(req.body)
|
||||
xml.match(OStatus::TagManager::VERBS[:block])
|
||||
}).to have_been_made.once
|
||||
end
|
||||
end
|
||||
|
||||
describe 'remote ActivityPub' do
|
||||
|
@ -22,9 +22,10 @@ RSpec.describe BootstrapTimelineService, type: :service do
|
||||
context 'when setting is set' do
|
||||
let!(:alice) { Fabricate(:account, username: 'alice') }
|
||||
let!(:bob) { Fabricate(:account, username: 'bob') }
|
||||
let!(:eve) { Fabricate(:account, username: 'eve', suspended: true) }
|
||||
|
||||
before do
|
||||
Setting.bootstrap_timeline_accounts = 'alice, bob'
|
||||
Setting.bootstrap_timeline_accounts = 'alice, @bob, eve, unknown'
|
||||
subject.call(source_account)
|
||||
end
|
||||
|
||||
@ -32,6 +33,10 @@ RSpec.describe BootstrapTimelineService, type: :service do
|
||||
expect(source_account.following?(alice)).to be true
|
||||
expect(source_account.following?(bob)).to be true
|
||||
end
|
||||
|
||||
it 'does not follow suspended account' do
|
||||
expect(source_account.following?(eve)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -30,13 +30,6 @@ RSpec.describe FavouriteService, type: :service do
|
||||
it 'creates a favourite' do
|
||||
expect(status.favourites.first).to_not be_nil
|
||||
end
|
||||
|
||||
it 'sends a salmon slap' do
|
||||
expect(a_request(:post, "http://salmon.example.com/").with { |req|
|
||||
xml = OStatus2::Salmon.new.unpack(req.body)
|
||||
xml.match(OStatus::TagManager::VERBS[:favorite])
|
||||
}).to have_been_made.once
|
||||
end
|
||||
end
|
||||
|
||||
describe 'remote ActivityPub' do
|
||||
|
@ -4,20 +4,13 @@ RSpec.describe FetchLinkCardService, type: :service do
|
||||
subject { FetchLinkCardService.new }
|
||||
|
||||
before do
|
||||
stub_request(:head, 'http://example.xn--fiqs8s/').to_return(status: 200, headers: { 'Content-Type' => 'text/html' })
|
||||
stub_request(:get, 'http://example.xn--fiqs8s/').to_return(request_fixture('idn.txt'))
|
||||
stub_request(:head, 'http://example.com/sjis').to_return(status: 200, headers: { 'Content-Type' => 'text/html' })
|
||||
stub_request(:get, 'http://example.com/sjis').to_return(request_fixture('sjis.txt'))
|
||||
stub_request(:head, 'http://example.com/sjis_with_wrong_charset').to_return(status: 200, headers: { 'Content-Type' => 'text/html' })
|
||||
stub_request(:get, 'http://example.com/sjis_with_wrong_charset').to_return(request_fixture('sjis_with_wrong_charset.txt'))
|
||||
stub_request(:head, 'http://example.com/koi8-r').to_return(status: 200, headers: { 'Content-Type' => 'text/html' })
|
||||
stub_request(:get, 'http://example.com/koi8-r').to_return(request_fixture('koi8-r.txt'))
|
||||
stub_request(:head, 'http://example.com/日本語').to_return(status: 200, headers: { 'Content-Type' => 'text/html' })
|
||||
stub_request(:get, 'http://example.com/日本語').to_return(request_fixture('sjis.txt'))
|
||||
stub_request(:head, 'https://github.com/qbi/WannaCry').to_return(status: 404)
|
||||
stub_request(:head, 'http://example.com/test-').to_return(status: 200, headers: { 'Content-Type' => 'text/html' })
|
||||
stub_request(:get, 'https://github.com/qbi/WannaCry').to_return(status: 404)
|
||||
stub_request(:get, 'http://example.com/test-').to_return(request_fixture('idn.txt'))
|
||||
stub_request(:head, 'http://example.com/windows-1251').to_return(status: 200, headers: { 'Content-Type' => 'text/html' })
|
||||
stub_request(:get, 'http://example.com/windows-1251').to_return(request_fixture('windows-1251.txt'))
|
||||
|
||||
subject.call(status)
|
||||
@ -90,11 +83,11 @@ RSpec.describe FetchLinkCardService, type: :service do
|
||||
let(:status) { Fabricate(:status, account: Fabricate(:account, domain: 'example.com'), text: 'Habt ihr ein paar gute Links zu #<span class="tag"><a href="https://quitter.se/tag/wannacry" target="_blank" rel="tag noopener" title="https://quitter.se/tag/wannacry">Wannacry</a></span> herumfliegen? Ich will mal unter <br> <a href="https://github.com/qbi/WannaCry" target="_blank" rel="noopener" title="https://github.com/qbi/WannaCry">https://github.com/qbi/WannaCry</a> was sammeln. !<a href="http://sn.jonkman.ca/group/416/id" target="_blank" rel="noopener" title="http://sn.jonkman.ca/group/416/id">security</a> ') }
|
||||
|
||||
it 'parses out URLs' do
|
||||
expect(a_request(:head, 'https://github.com/qbi/WannaCry')).to have_been_made.at_least_once
|
||||
expect(a_request(:get, 'https://github.com/qbi/WannaCry')).to have_been_made.at_least_once
|
||||
end
|
||||
|
||||
it 'ignores URLs to hashtags' do
|
||||
expect(a_request(:head, 'https://quitter.se/tag/wannacry')).to_not have_been_made
|
||||
expect(a_request(:get, 'https://quitter.se/tag/wannacry')).to_not have_been_made
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -4,6 +4,7 @@ RSpec.describe FetchRemoteAccountService, type: :service do
|
||||
let(:url) { 'https://example.com/alice' }
|
||||
let(:prefetched_body) { nil }
|
||||
let(:protocol) { :ostatus }
|
||||
|
||||
subject { FetchRemoteAccountService.new.call(url, prefetched_body, protocol) }
|
||||
|
||||
let(:actor) do
|
||||
@ -36,36 +37,6 @@ RSpec.describe FetchRemoteAccountService, type: :service do
|
||||
include_examples 'return Account'
|
||||
end
|
||||
|
||||
context 'protocol is :ostatus' do
|
||||
let(:prefetched_body) { xml }
|
||||
let(:protocol) { :ostatus }
|
||||
|
||||
before do
|
||||
stub_request(:get, "https://kickass.zone/.well-known/webfinger?resource=acct:localhost@kickass.zone").to_return(request_fixture('webfinger-hacker3.txt'))
|
||||
stub_request(:get, "https://kickass.zone/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
|
||||
end
|
||||
|
||||
include_examples 'return Account'
|
||||
|
||||
it 'does not update account information if XML comes from an unverified domain' do
|
||||
feed_xml = <<-XML.squish
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:georss="http://www.georss.org/georss" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:media="http://purl.org/syndication/atommedia" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:statusnet="http://status.net/schema/api/1/">
|
||||
<author>
|
||||
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
|
||||
<uri>http://kickass.zone/users/localhost</uri>
|
||||
<name>localhost</name>
|
||||
<poco:preferredUsername>localhost</poco:preferredUsername>
|
||||
<poco:displayName>Villain!!!</poco:displayName>
|
||||
</author>
|
||||
</feed>
|
||||
XML
|
||||
|
||||
returned_account = described_class.new.call('https://real-fake-domains.com/alice', feed_xml, :ostatus)
|
||||
expect(returned_account.display_name).to_not eq 'Villain!!!'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when prefetched_body is nil' do
|
||||
context 'protocol is :activitypub' do
|
||||
before do
|
||||
@ -75,15 +46,5 @@ RSpec.describe FetchRemoteAccountService, type: :service do
|
||||
|
||||
include_examples 'return Account'
|
||||
end
|
||||
|
||||
context 'protocol is :ostatus' do
|
||||
before do
|
||||
stub_request(:get, url).to_return(status: 200, body: xml, headers: { 'Content-Type' => 'application/atom+xml' })
|
||||
stub_request(:get, "https://kickass.zone/.well-known/webfinger?resource=acct:localhost@kickass.zone").to_return(request_fixture('webfinger-hacker3.txt'))
|
||||
stub_request(:get, "https://kickass.zone/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
|
||||
end
|
||||
|
||||
include_examples 'return Account'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user