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