Merge remote-tracking branch 'parent/main' into upstream-20250210

This commit is contained in:
KMY 2025-02-17 08:57:56 +09:00
commit 935bea989d
69 changed files with 1646 additions and 1363 deletions

View file

@ -59,16 +59,15 @@ RSpec.describe Admin::AccountsController do
let(:account) { Fabricate(:account) }
it 'includes moderation notes' do
note1 = Fabricate(:account_moderation_note, target_account: account)
note2 = Fabricate(:account_moderation_note, target_account: account)
note1 = Fabricate(:account_moderation_note, target_account: account, content: 'Note 1 remarks')
note2 = Fabricate(:account_moderation_note, target_account: account, content: 'Note 2 remarks')
get :show, params: { id: account.id }
expect(response).to have_http_status(200)
moderation_notes = assigns(:moderation_notes).to_a
expect(moderation_notes.size).to be 2
expect(moderation_notes).to eq [note1, note2]
expect(response.body)
.to include(note1.content)
.and include(note2.content)
end
end

View file

@ -3,40 +3,54 @@
require 'rails_helper'
RSpec.describe Admin::BaseController do
render_views
controller do
def success
authorize :dashboard, :index?
render 'admin/reports/show'
render html: '<p>success</p>', layout: true
end
end
it 'requires administrator or moderator' do
routes.draw { get 'success' => 'admin/base#success' }
sign_in(Fabricate(:user))
get :success
before { routes.draw { get 'success' => 'admin/base#success' } }
expect(response).to have_http_status(403)
context 'when signed in as regular user' do
before { sign_in Fabricate(:user) }
it 'responds with unauthorized' do
get :success
expect(response).to have_http_status(403)
end
end
it 'returns private cache control headers' do
routes.draw { get 'success' => 'admin/base#success' }
sign_in(Fabricate(:moderator_user))
get :success
context 'when signed in as moderator' do
before { sign_in Fabricate(:moderator_user) }
expect(response.headers['Cache-Control']).to include('private, no-store')
it 'returns success with private headers and admin layout' do
get :success
expect(response)
.to have_http_status(200)
expect(response.headers['Cache-Control'])
.to include('private, no-store')
expect(response.parsed_body)
.to have_css('body.admin')
end
end
it 'renders admin layout as a moderator' do
routes.draw { get 'success' => 'admin/base#success' }
sign_in(Fabricate(:moderator_user))
get :success
expect(response).to render_template layout: 'admin'
end
context 'when signed in as admin' do
before { sign_in Fabricate(:admin_user) }
it 'renders admin layout as an admin' do
routes.draw { get 'success' => 'admin/base#success' }
sign_in(Fabricate(:admin_user))
get :success
expect(response).to render_template layout: 'admin'
it 'returns success with private headers and admin layout' do
get :success
expect(response)
.to have_http_status(200)
expect(response.headers['Cache-Control'])
.to include('private, no-store')
expect(response.parsed_body)
.to have_css('body.admin')
end
end
end

View file

@ -49,23 +49,11 @@ RSpec.describe Admin::InstancesController do
expect(response).to have_http_status(200)
instance = assigns(:instance)
expect(instance).to_not be_new_record
expect(response.body)
.to include(I18n.t('admin.instances.totals_time_period_hint_html'))
.and include(I18n.t('accounts.nothing_here'))
expect(Admin::ActionLogFilter).to have_received(:new).with(target_domain: account_popular_main.domain)
action_logs = assigns(:action_logs).to_a
expect(action_logs.size).to eq 0
end
context 'with an unknown domain' do
it 'returns http success' do
get :show, params: { id: 'unknown.example' }
expect(response).to have_http_status(200)
instance = assigns(:instance)
expect(instance).to be_new_record
end
end
end

View file

@ -262,8 +262,8 @@ RSpec.describe Auth::SessionsController do
end
it 'renders two factor authentication page' do
expect(controller).to render_template('two_factor')
expect(controller).to render_template(partial: '_otp_authentication_form')
expect(response.body)
.to include(I18n.t('simple_form.hints.sessions.otp'))
end
end
@ -278,8 +278,8 @@ RSpec.describe Auth::SessionsController do
end
it 'renders two factor authentication page' do
expect(controller).to render_template('two_factor')
expect(controller).to render_template(partial: '_otp_authentication_form')
expect(response.body)
.to include(I18n.t('simple_form.hints.sessions.otp'))
end
end
@ -289,8 +289,8 @@ RSpec.describe Auth::SessionsController do
end
it 'renders two factor authentication page' do
expect(controller).to render_template('two_factor')
expect(controller).to render_template(partial: '_otp_authentication_form')
expect(response.body)
.to include(I18n.t('simple_form.hints.sessions.otp'))
end
end
@ -417,8 +417,8 @@ RSpec.describe Auth::SessionsController do
end
it 'renders webauthn authentication page' do
expect(controller).to render_template('two_factor')
expect(controller).to render_template(partial: '_webauthn_form')
expect(response.body)
.to include(I18n.t('simple_form.title.sessions.webauthn'))
end
end
@ -428,8 +428,8 @@ RSpec.describe Auth::SessionsController do
end
it 'renders webauthn authentication page' do
expect(controller).to render_template('two_factor')
expect(controller).to render_template(partial: '_webauthn_form')
expect(response.body)
.to include(I18n.t('simple_form.title.sessions.webauthn'))
end
end

View file

@ -3,6 +3,8 @@
require 'rails_helper'
RSpec.describe ChallengableConcern do
render_views
controller(ApplicationController) do
include ChallengableConcern # rubocop:disable RSpec/DescribedClass
@ -85,29 +87,35 @@ RSpec.describe ChallengableConcern do
before { get :foo }
it 'renders challenge' do
expect(response).to render_template('auth/challenges/new', layout: :auth)
expect(response.parsed_body)
.to have_title(I18n.t('challenge.prompt'))
end
# See Auth::ChallengesControllerSpec
end
context 'with POST requests' do
before { post :bar }
it 'renders challenge' do
expect(response).to render_template('auth/challenges/new', layout: :auth)
expect(response.parsed_body)
.to have_title(I18n.t('challenge.prompt'))
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
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', layout: :auth)
expect(session[:challenge_passed_at]).to be_nil
expect(response.parsed_body)
.to have_title(I18n.t('challenge.prompt'))
expect(session[:challenge_passed_at])
.to be_nil
end
end
end

View file

@ -1,68 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Settings::FeaturedTagsController do
render_views
context 'when user is not signed in' do
subject { post :create }
it { is_expected.to redirect_to new_user_session_path }
end
context 'when user is signed in' do
let(:user) { Fabricate(:user, password: '12345678') }
before { sign_in user, scope: :user }
describe 'POST #create' do
subject { post :create, params: { featured_tag: params } }
context 'when parameter is valid' do
let(:params) { { name: 'test' } }
it 'creates featured tag' do
expect { subject }.to change { user.account.featured_tags.count }.by(1)
end
end
context 'when parameter is invalid' do
let(:params) { { name: 'test, #foo !bleh' } }
it 'renders new' do
expect(subject).to render_template :index
end
end
end
describe 'GET to #index' do
let(:tag) { Fabricate(:tag) }
before do
status = Fabricate :status, account: user.account
status.tags << tag
end
it 'responds with success' do
get :index
expect(response).to have_http_status(200)
expect(response.body).to include(
settings_featured_tags_path(featured_tag: { name: tag.name })
)
end
end
describe 'DELETE to #destroy' do
let(:featured_tag) { Fabricate(:featured_tag, account: user.account) }
it 'removes the featured tag' do
delete :destroy, params: { id: featured_tag.id }
expect(response).to redirect_to(settings_featured_tags_path)
expect { featured_tag.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end

View file

@ -1,67 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Settings::Migration::RedirectsController do
render_views
let!(:user) { Fabricate(:user, password: 'testtest') }
before do
sign_in user, scope: :user
end
describe 'GET #new' do
before do
get :new
end
it 'returns http success with private cache control headers', :aggregate_failures do
expect(response).to have_http_status(200)
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'POST #create' do
context 'with valid params' do
before { stub_resolver }
it 'redirects to the settings migration path' do
post :create, params: { form_redirect: { acct: 'new@host.com', current_password: 'testtest' } }
expect(response).to redirect_to(settings_migration_path)
end
end
context 'with non valid params' do
it 'returns success and renders the new page' do
post :create, params: { form_redirect: { acct: '' } }
expect(response).to have_http_status(200)
expect(response).to render_template(:new)
end
end
end
describe 'DELETE #destroy' do
let(:account) { Fabricate(:account) }
before do
user.account.update(moved_to_account_id: account.id)
end
it 'resets the account and sends an update' do
delete :destroy
expect(response).to redirect_to(settings_migration_path)
expect(user.account.reload.moved_to_account).to be_nil
end
end
private
def stub_resolver
resolver = instance_double(ResolveAccountService, call: Fabricate(:account))
allow(ResolveAccountService).to receive(:new).and_return(resolver)
end
end

View file

@ -1,74 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Settings::PrivacyController do
render_views
let!(:user) { Fabricate(:user) }
let(:account) { user.account }
before do
sign_in user, scope: :user
end
describe 'GET #show' do
before do
get :show
end
it 'returns http success with private cache control headers', :aggregate_failures do
expect(response)
.to have_http_status(200)
.and have_attributes(
headers: include(
'Cache-Control' => 'private, no-store'
)
)
end
end
describe 'PUT #update' do
context 'when update succeeds' do
before do
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async)
end
it 'updates the user profile' do
put :update, params: { account: { discoverable: '1', settings: { indexable: '1' } } }
expect(account.reload.discoverable)
.to be(true)
expect(response)
.to redirect_to(settings_privacy_path)
expect(ActivityPub::UpdateDistributionWorker)
.to have_received(:perform_async).with(account.id)
end
end
context 'when update fails' do
before do
allow(UpdateAccountService).to receive(:new).and_return(failing_update_service)
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async)
end
it 'updates the user profile' do
put :update, params: { account: { discoverable: '1', settings: { indexable: '1' } } }
expect(response)
.to render_template(:show)
expect(ActivityPub::UpdateDistributionWorker)
.to_not have_received(:perform_async)
end
private
def failing_update_service
instance_double(UpdateAccountService, call: false)
end
end
end
end

View file

@ -1,30 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Settings::TwoFactorAuthentication::RecoveryCodesController do
render_views
describe 'POST #create' do
it 'updates the codes and shows them on a view when signed in' do
user = Fabricate(:user)
otp_backup_codes = user.generate_otp_backup_codes!
allow(user).to receive(:generate_otp_backup_codes!).and_return(otp_backup_codes)
allow(controller).to receive(:current_user).and_return(user)
sign_in user, scope: :user
post :create, session: { challenge_passed_at: Time.now.utc }
expect(flash[:notice]).to eq 'Recovery codes successfully regenerated'
expect(response).to have_http_status(200)
expect(response).to render_template(:index)
expect(response.body)
.to include(*otp_backup_codes)
end
it 'redirects when not signed in' do
post :create
expect(response).to redirect_to '/auth/sign_in'
end
end
end

View file

@ -1,79 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Settings::TwoFactorAuthenticationMethodsController do
render_views
context 'when not signed in' do
describe 'GET to #index' do
it 'redirects' do
get :index
expect(response).to redirect_to '/auth/sign_in'
end
end
end
context 'when signed in' do
let(:user) { Fabricate(:user) }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
describe 'when user has enabled otp' do
before do
user.update(otp_required_for_login: true)
get :index
end
it 'returns http success with private cache control headers', :aggregate_failures do
expect(response).to have_http_status(200)
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'when user has not enabled otp' do
before do
user.update(otp_required_for_login: false)
get :index
end
it 'redirects to enable otp' do
expect(response).to redirect_to(settings_otp_authentication_path)
end
end
end
describe 'POST to #disable' do
before do
user.update(otp_required_for_login: true)
end
context 'when user has not passed challenge' do
it 'renders challenge page' do
post :disable
expect(response).to have_http_status(200)
expect(response).to render_template('auth/challenges/new')
end
end
context 'when user has passed challenge' do
before do
mailer = instance_double(ApplicationMailer::MessageDelivery, deliver_later!: true)
allow(UserMailer).to receive(:two_factor_disabled).with(user).and_return(mailer)
end
it 'redirects to settings page' do
post :disable, session: { challenge_passed_at: 10.minutes.ago }
expect(UserMailer).to have_received(:two_factor_disabled).with(user)
expect(response).to redirect_to(settings_otp_authentication_path)
end
end
end
end
end

View file

@ -9,93 +9,6 @@ RSpec.describe StatusesController do
let(:account) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: account) }
context 'when status is public' do
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'with HTML' do
let(:format) { 'html' }
it 'renders status successfully', :aggregate_failures do
expect(response)
.to have_http_status(200)
.and render_template(:show)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('public'),
'Link' => include('activity+json')
)
expect(response.body).to include status.text
end
end
context 'with JSON' do
let(:format) { 'json' }
it 'renders ActivityPub Note object successfully', :aggregate_failures do
expect(response)
.to have_http_status(200)
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
expect(response.headers).to include(
'Content-Type' => include('application/activity+json'),
'Link' => include('activity+json')
)
expect(response.parsed_body)
.to include(content: include(status.text))
end
end
end
context 'when status is private' do
let(:status) { Fabricate(:status, account: account, visibility: :private) }
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'with JSON' do
let(:format) { 'json' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'with HTML' do
let(:format) { 'html' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
context 'when status is direct' do
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'with JSON' do
let(:format) { 'json' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'with HTML' do
let(:format) { 'html' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
context 'when signed-in' do
let(:user) { Fabricate(:user) }
@ -275,206 +188,5 @@ RSpec.describe StatusesController do
end
end
end
context 'with signature' do
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
before do
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
end
context 'when account blocks account' do
before do
account.block!(remote_account)
get :show, params: { account_username: status.account.username, id: status.id }
end
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when account domain blocks account' do
before do
account.block_domain!(remote_account.domain)
get :show, params: { account_username: status.account.username, id: status.id }
end
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is public' do
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'with HTML' do
let(:format) { 'html' }
it 'renders status successfully', :aggregate_failures do
expect(response)
.to have_http_status(200)
.and render_template(:show)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('private'),
'Link' => include('activity+json')
)
expect(response.body).to include status.text
end
end
context 'with JSON' do
let(:format) { 'json' }
it 'renders ActivityPub Note object successfully', :aggregate_failures do
expect(response)
.to have_http_status(200)
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
expect(response.headers).to include(
'Content-Type' => include('application/activity+json'),
'Link' => include('activity+json')
)
expect(response.parsed_body)
.to include(content: include(status.text))
end
end
end
context 'when status is private' do
let(:status) { Fabricate(:status, account: account, visibility: :private) }
context 'when user is authorized to see it' do
before do
remote_account.follow!(account)
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'with HTML' do
let(:format) { 'html' }
it 'renders status successfully', :aggregate_failures do
expect(response)
.to have_http_status(200)
.and render_template(:show)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('private'),
'Link' => include('activity+json')
)
expect(response.body).to include status.text
end
end
context 'with JSON' do
let(:format) { 'json' }
it 'renders ActivityPub Note object successfully' do
expect(response)
.to have_http_status(200)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('private'),
'Content-Type' => include('application/activity+json'),
'Link' => include('activity+json')
)
expect(response.parsed_body)
.to include(content: include(status.text))
end
end
end
context 'when user is not authorized to see it' do
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'with JSON' do
let(:format) { 'json' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'with HTML' do
let(:format) { 'html' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
end
context 'when status is direct' do
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
context 'when user is authorized to see it' do
before do
Fabricate(:mention, account: remote_account, status: status)
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'with HTML' do
let(:format) { 'html' }
it 'renders status successfully', :aggregate_failures do
expect(response)
.to have_http_status(200)
.and render_template(:show)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('private'),
'Link' => include('activity+json')
)
expect(response.body).to include status.text
end
end
context 'with JSON' do
let(:format) { 'json' }
it 'renders ActivityPub Note object', :aggregate_failures do
expect(response)
.to have_http_status(200)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('private'),
'Content-Type' => include('application/activity+json'),
'Link' => include('activity+json')
)
expect(response.parsed_body)
.to include(content: include(status.text))
end
end
end
context 'when user is not authorized to see it' do
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'with JSON' do
let(:format) { 'json' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'with HTML' do
let(:format) { 'html' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
end
end
end
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

View file

@ -48,10 +48,16 @@ RSpec.describe ActivityPub::Activity::Create do
content: '@bob lorem ipsum',
published: 1.hour.ago.utc.iso8601,
updated: 1.hour.ago.utc.iso8601,
tag: {
type: 'Mention',
href: ActivityPub::TagManager.instance.uri_for(follower),
},
tag: [
{
type: 'Mention',
href: ActivityPub::TagManager.instance.uri_for(follower),
},
{
type: 'Mention',
href: ActivityPub::TagManager.instance.uri_for(follower),
},
],
}
end

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Admin Instances' do
describe 'GET /admin/instances/:id' do
context 'with an unknown domain' do
before { sign_in Fabricate(:admin_user) }
it 'returns http success' do
get admin_instance_path(id: 'unknown.example')
expect(response)
.to have_http_status(200)
end
end
end
end

View file

@ -2,15 +2,23 @@
require 'rails_helper'
RSpec.describe 'Settings Aliases' do
RSpec.describe 'Settings Featured Tags' do
describe 'POST /settings/featured_tags' do
before { sign_in Fabricate(:user) }
context 'when signed in' do
before { sign_in Fabricate(:user) }
it 'gracefully handles invalid nested params' do
post settings_featured_tags_path(featured_tag: 'invalid')
it 'gracefully handles invalid nested params' do
post settings_featured_tags_path(featured_tag: 'invalid')
expect(response)
.to have_http_status(400)
expect(response)
.to have_http_status(400)
end
end
context 'when not signed in' do
subject { post settings_featured_tags_path }
it { is_expected.to redirect_to new_user_session_path }
end
end
end

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Settings TwoFactorAuthentication RecoveryCodes' do
describe 'POST /settings/two_factor_authentication/recovery_codes' do
context 'when signed out' do
it 'redirects to sign in page' do
post settings_two_factor_authentication_recovery_codes_path
expect(response)
.to redirect_to(new_user_session_path)
end
end
end
end

View file

@ -0,0 +1,35 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Settings TwoFactorAuthenticationMethods' do
context 'when not signed in' do
describe 'GET to /settings/two_factor_authentication_methods' do
it 'redirects to sign in page' do
get settings_two_factor_authentication_methods_path
expect(response)
.to redirect_to(new_user_session_path)
end
end
end
context 'when signed in' do
let(:user) { Fabricate(:user) }
before { sign_in user }
describe 'GET to /settings/two_factor_authentication_methods' do
describe 'when user has not enabled otp' do
before { user.update(otp_required_for_login: false) }
it 'redirects to enable otp' do
get settings_two_factor_authentication_methods_path
expect(response)
.to redirect_to(settings_otp_authentication_path)
end
end
end
end
end

View file

@ -45,6 +45,72 @@ RSpec.describe 'Statuses' do
.to redirect_to(original_status.url)
end
end
context 'when status visibility is public' do
subject { get short_account_status_path(account_username: account.username, id: status.id, format: format) }
let(:status) { Fabricate(:status, account: account, visibility: :public) }
context 'with HTML' do
let(:format) { 'html' }
it 'renders status successfully', :aggregate_failures do
subject
expect(response)
.to have_http_status(200)
.and render_template(:show)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('public'),
'Link' => include('activity+json')
)
expect(response.body)
.to include(status.text)
end
end
context 'with JSON' do
let(:format) { 'json' }
it 'renders ActivityPub Note object successfully', :aggregate_failures do
subject
expect(response)
.to have_http_status(200)
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
expect(response.headers).to include(
'Content-Type' => include('application/activity+json'),
'Link' => include('activity+json')
)
expect(response.parsed_body)
.to include(content: include(status.text))
end
end
end
context 'when status visibility is private' do
let(:status) { Fabricate(:status, account: account, visibility: :private) }
it 'returns http not found' do
get short_account_status_path(account_username: account.username, id: status.id)
expect(response)
.to have_http_status(404)
end
end
context 'when status visibility is direct' do
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
it 'returns http not found' do
get short_account_status_path(account_username: account.username, id: status.id)
expect(response)
.to have_http_status(404)
end
end
end
context 'when signed in' do
@ -63,5 +129,185 @@ RSpec.describe 'Statuses' do
end
end
end
context 'with "HTTP Signature" access signed by a remote account' do
subject do
get short_account_status_path(account_username: status.account.username, id: status.id, format: format),
headers: nil,
sign_with: remote_account
end
let(:format) { 'html' }
let(:remote_account) { Fabricate(:account, domain: 'host.example') }
context 'when account blocks the remote account' do
before { account.block!(remote_account) }
it 'returns http not found' do
subject
expect(response)
.to have_http_status(404)
end
end
context 'when account domain blocks the domain of the remote account' do
before { account.block_domain!(remote_account.domain) }
it 'returns http not found' do
subject
expect(response)
.to have_http_status(404)
end
end
context 'when status has public visibility' do
context 'with HTML' do
let(:format) { 'html' }
it 'renders status successfully', :aggregate_failures do
subject
expect(response)
.to have_http_status(200)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('private'),
'Link' => include('activity+json')
)
expect(response.body)
.to include(status.text)
end
end
context 'with JSON' do
let(:format) { 'json' }
it 'renders ActivityPub Note object successfully', :aggregate_failures do
subject
expect(response)
.to have_http_status(200)
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
expect(response.headers).to include(
'Content-Type' => include('application/activity+json'),
'Link' => include('activity+json')
)
expect(response.parsed_body)
.to include(content: include(status.text))
end
end
end
context 'when status has private visibility' do
let(:status) { Fabricate(:status, account: account, visibility: :private) }
context 'when user is authorized to see it' do
before { remote_account.follow!(account) }
context 'with HTML' do
let(:format) { 'html' }
it 'renders status successfully', :aggregate_failures do
subject
expect(response)
.to have_http_status(200)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('private'),
'Link' => include('activity+json')
)
expect(response.body)
.to include(status.text)
end
end
context 'with JSON' do
let(:format) { 'json' }
it 'renders ActivityPub Note object successfully' do
subject
expect(response)
.to have_http_status(200)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('private'),
'Content-Type' => include('application/activity+json'),
'Link' => include('activity+json')
)
expect(response.parsed_body)
.to include(content: include(status.text))
end
end
end
context 'when user is not authorized to see it' do
it 'returns http not found' do
subject
expect(response)
.to have_http_status(404)
end
end
end
context 'when status is direct' do
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
context 'when user is authorized to see it' do
before { Fabricate(:mention, account: remote_account, status: status) }
context 'with HTML' do
let(:format) { 'html' }
it 'renders status successfully', :aggregate_failures do
subject
expect(response)
.to have_http_status(200)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('private'),
'Link' => include('activity+json')
)
expect(response.body)
.to include(status.text)
end
end
context 'with JSON' do
let(:format) { 'json' }
it 'renders ActivityPub Note object', :aggregate_failures do
subject
expect(response)
.to have_http_status(200)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('private'),
'Content-Type' => include('application/activity+json'),
'Link' => include('activity+json')
)
expect(response.parsed_body)
.to include(content: include(status.text))
end
end
end
context 'when user is not authorized to see it' do
it 'returns http not found' do
subject
expect(response)
.to have_http_status(404)
end
end
end
end
end
end

View file

@ -12,6 +12,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
[
{ type: 'Hashtag', name: 'hoge' },
{ type: 'Mention', href: ActivityPub::TagManager.instance.uri_for(alice) },
{ type: 'Mention', href: ActivityPub::TagManager.instance.uri_for(alice) },
{ type: 'Mention', href: bogus_mention },
]
end

View file

@ -5,6 +5,7 @@ require 'rails_helper'
RSpec.describe 'finding software updates through the admin interface' do
before do
Fabricate(:software_update, version: '99.99.99', type: 'major', urgent: true, release_notes: 'https://github.com/mastodon/mastodon/releases/v99')
Fabricate(:software_update, version: '3.5.0', type: 'major', urgent: true, release_notes: 'https://github.com/mastodon/mastodon/releases/v3.5.0')
sign_in Fabricate(:owner_user), scope: :user
end
@ -16,6 +17,7 @@ RSpec.describe 'finding software updates through the admin interface' do
expect(page).to have_title(I18n.t('admin.software_updates.title'))
expect(page).to have_content('99.99.99')
.and have_no_content('3.5.0')
click_on I18n.t('admin.software_updates.release_notes')
expect(page).to have_current_path('https://github.com/mastodon/mastodon/releases/v99', url: true)

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Settings Featured Tags' do
let(:user) { Fabricate(:user) }
before { sign_in(user) }
describe 'Managing tags' do
let(:tag) { Fabricate(:tag) }
let(:status) { Fabricate :status, account: user.account }
before { status.tags << tag }
it 'Views, adds, and removes featured tags' do
visit settings_featured_tags_path
# Link to existing tag used on a status
expect(page.body)
.to include(
settings_featured_tags_path(featured_tag: { name: tag.name })
)
# Invalid entry
fill_in 'featured_tag_name', with: 'test, #foo !bleh'
expect { click_on I18n.t('featured_tags.add_new') }
.to_not change(user.account.featured_tags, :count)
# Valid entry
fill_in 'featured_tag_name', with: '#friends'
expect { click_on I18n.t('featured_tags.add_new') }
.to change(user.account.featured_tags, :count).by(1)
# Delete the created entry
expect { click_on I18n.t('filters.index.delete') }
.to change(user.account.featured_tags, :count).by(-1)
expect(page)
.to have_title(I18n.t('settings.featured_tags'))
end
end
end

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Settings Migration Redirects' do
let!(:user) { Fabricate(:user, password: 'testtest') }
before { sign_in(user) }
describe 'Managing redirects' do
before { stub_resolver }
it 'creates and destroys redirects' do
visit new_settings_migration_redirect_path
expect(page)
.to have_title(I18n.t('settings.migrate'))
# Empty form invalid submission
expect { click_on I18n.t('migrations.set_redirect') }
.to_not(change { user.account.moved_to_account_id }.from(nil))
# Valid form submission
fill_in 'form_redirect_acct', with: 'new@example.host'
fill_in 'form_redirect_current_password', with: 'testtest'
expect { click_on I18n.t('migrations.set_redirect') }
.to(change { user.reload.account.moved_to_account_id }.from(nil))
# Delete the account move
expect { click_on I18n.t('migrations.cancel') }
.to(change { user.reload.account.moved_to_account_id }.to(nil))
expect(page)
.to have_content(I18n.t('migrations.cancelled_msg'))
end
private
def stub_resolver
resolver = instance_double(ResolveAccountService, call: Fabricate(:account))
allow(ResolveAccountService).to receive(:new).and_return(resolver)
end
end
end

View file

@ -0,0 +1,67 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Settings Privacy' do
let!(:user) { Fabricate(:user) }
before { sign_in(user) }
describe 'Managing privacy settings' do
before { user.account.update(discoverable: false) }
context 'with a successful update' do
before { allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async) }
it 'updates user profile information' do
# View settings page
visit settings_privacy_path
expect(page)
.to have_content(I18n.t('privacy.title'))
.and have_private_cache_control
# Fill out form and submit
check 'account_discoverable'
check 'account_indexable'
expect { click_on submit_button }
.to change { user.account.reload.discoverable }.to(true)
expect(page)
.to have_content(I18n.t('privacy.title'))
.and have_content(I18n.t('generic.changes_saved_msg'))
expect(ActivityPub::UpdateDistributionWorker)
.to have_received(:perform_async).with(user.account.id)
end
end
context 'with a failed update' do
before do
allow(UpdateAccountService).to receive(:new).and_return(failing_update_service)
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async)
end
it 'updates user profile information' do
# View settings page
visit settings_privacy_path
expect(page)
.to have_content(I18n.t('privacy.title'))
.and have_private_cache_control
# Fill out form and submit
check 'account_discoverable'
check 'account_indexable'
expect { click_on submit_button }
.to_not(change { user.account.reload.discoverable })
expect(page)
.to have_content(I18n.t('privacy.title'))
expect(ActivityPub::UpdateDistributionWorker)
.to_not have_received(:perform_async)
end
private
def failing_update_service
instance_double(UpdateAccountService, call: false)
end
end
end
end

View file

@ -0,0 +1,37 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Settings TwoFactorAuthentication RecoveryCodes' do
describe 'Generating recovery codes' do
let(:user) { Fabricate :user, otp_required_for_login: true }
let(:backup_code) { +'147e7284c95bd260b91ed17820860019' }
before do
stub_code_generator
sign_in(user)
end
it 'updates the codes and includes them in the view' do
# Attempt to generate codes
visit settings_two_factor_authentication_methods_path
click_on I18n.t('two_factor_authentication.generate_recovery_codes')
# Fill in challenge password
fill_in 'form_challenge_current_password', with: user.password
expect { click_on I18n.t('challenge.confirm') }
.to(change { user.reload.otp_backup_codes })
expect(page)
.to have_content(I18n.t('two_factor_authentication.recovery_codes_regenerated'))
.and have_title(I18n.t('settings.two_factor_authentication'))
.and have_css('ol.recovery-codes')
.and have_content(backup_code)
end
def stub_code_generator
allow(SecureRandom).to receive(:hex).and_return(backup_code)
end
end
end

View file

@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Settings TwoFactorAuthenticationMethods' do
context 'when signed in' do
let(:user) { Fabricate(:user) }
before { sign_in user }
describe 'Managing 2FA methods' do
before { user.update(otp_required_for_login: true) }
it 'disables 2FA with challenge confirmation', :inline_jobs do
visit settings_two_factor_authentication_methods_path
expect(page)
.to have_content(I18n.t('settings.two_factor_authentication'))
.and have_private_cache_control
# Attempt to disable
click_on I18n.t('two_factor_authentication.disable')
expect(page)
.to have_title(I18n.t('challenge.prompt'))
# Fill in challenge form
fill_in 'form_challenge_current_password', with: user.password
emails = capture_emails do
expect { click_on I18n.t('challenge.confirm') }
.to change { user.reload.otp_required_for_login }.to(false)
end
expect(page)
.to have_content(I18n.t('two_factor_authentication.disabled_success'))
expect(emails.first)
.to be_present
.and(deliver_to(user.email))
.and(have_subject(I18n.t('devise.mailer.two_factor_disabled.subject')))
end
end
end
end