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

This commit is contained in:
KMY 2025-02-10 09:09:12 +09:00
commit f3f93ba0c6
108 changed files with 1041 additions and 727 deletions

View file

@ -1,125 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::ReportsController do
render_views
let(:user) { Fabricate(:admin_user) }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
it 'returns http success with no filters' do
specified = Fabricate(:report, action_taken_at: nil, comment: 'First report')
other = Fabricate(:report, action_taken_at: Time.now.utc, comment: 'Second report')
get :index
expect(response).to have_http_status(200)
expect(response.body)
.to include(specified.comment)
.and not_include(other.comment)
end
it 'returns http success with resolved filter' do
specified = Fabricate(:report, action_taken_at: Time.now.utc, comment: 'First report')
other = Fabricate(:report, action_taken_at: nil, comment: 'Second report')
get :index, params: { resolved: '1' }
expect(response).to have_http_status(200)
expect(response.body)
.to include(specified.comment)
.and not_include(other.comment)
end
end
describe 'GET #show' do
it 'renders report' do
report = Fabricate(:report, comment: 'A big problem')
get :show, params: { id: report }
expect(response).to have_http_status(200)
expect(response.body)
.to include(report.comment)
end
describe 'account moderation notes' do
let(:report) { Fabricate(:report) }
it 'includes moderation notes' do
note1 = Fabricate(:report_note, report: report)
note2 = Fabricate(:report_note, report: report)
get :show, params: { id: report }
expect(response).to have_http_status(200)
report_notes = assigns(:report_notes).to_a
expect(report_notes.size).to be 2
expect(report_notes).to eq [note1, note2]
end
end
end
describe 'POST #resolve' do
it 'resolves the report' do
report = Fabricate(:report)
put :resolve, params: { id: report }
expect(response).to redirect_to(admin_reports_path)
report.reload
expect(report.action_taken_by_account).to eq user.account
expect(report.action_taken?).to be true
expect(last_action_log.target).to eq(report)
end
end
describe 'POST #reopen' do
it 'reopens the report' do
report = Fabricate(:report, action_taken_at: 3.days.ago)
put :reopen, params: { id: report }
expect(response).to redirect_to(admin_report_path(report))
report.reload
expect(report.action_taken_by_account).to be_nil
expect(report.action_taken?).to be false
expect(last_action_log.target).to eq(report)
end
end
describe 'POST #assign_to_self' do
it 'reopens the report' do
report = Fabricate(:report)
put :assign_to_self, params: { id: report }
expect(response).to redirect_to(admin_report_path(report))
report.reload
expect(report.assigned_account).to eq user.account
expect(last_action_log.target).to eq(report)
end
end
describe 'POST #unassign' do
it 'reopens the report' do
report = Fabricate(:report, assigned_account_id: Account.last.id)
put :unassign, params: { id: report }
expect(response).to redirect_to(admin_report_path(report))
report.reload
expect(report.assigned_account).to be_nil
expect(last_action_log.target).to eq(report)
end
end
private
def last_action_log
Admin::ActionLog.last
end
end

View file

@ -1,102 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Auth::PasswordsController do
include Devise::Test::ControllerHelpers
describe 'GET #new' do
it 'returns http success' do
request.env['devise.mapping'] = Devise.mappings[:user]
get :new
expect(response).to have_http_status(200)
end
end
describe 'GET #edit' do
let(:user) { Fabricate(:user) }
before do
request.env['devise.mapping'] = Devise.mappings[:user]
end
context 'with valid reset_password_token' do
it 'returns http success' do
token = user.send_reset_password_instructions
get :edit, params: { reset_password_token: token }
expect(response).to have_http_status(200)
end
end
context 'with invalid reset_password_token' do
it 'redirects to #new' do
get :edit, params: { reset_password_token: 'some_invalid_value' }
expect(response).to redirect_to subject.new_password_path(subject.send(:resource_name))
end
end
end
describe 'POST #update' do
let(:user) { Fabricate(:user) }
let(:password) { 'reset0password' }
before do
request.env['devise.mapping'] = Devise.mappings[:user]
end
context 'with valid reset_password_token' do
let!(:session_activation) { Fabricate(:session_activation, user: user) }
let!(:access_token) { Fabricate(:access_token, resource_owner_id: user.id) }
let!(:web_push_subscription) { Fabricate(:web_push_subscription, access_token: access_token) }
before do
token = user.send_reset_password_instructions
post :update, params: { user: { password: password, password_confirmation: password, reset_password_token: token } }
end
it 'resets the password' do
expect(response)
.to redirect_to '/auth/sign_in'
# Change password
expect(User.find(user.id))
.to be_present
.and be_valid_password(password)
# Deactivate session
expect(user.session_activations.count)
.to eq 0
expect { session_activation.reload }
.to raise_error(ActiveRecord::RecordNotFound)
# Revoke tokens
expect(Doorkeeper::AccessToken.active_for(user).count)
.to eq 0
# Remove push subs
expect(Web::PushSubscription.where(user: user).or(Web::PushSubscription.where(access_token: access_token)).count)
.to eq 0
expect { web_push_subscription.reload }
.to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'with invalid reset_password_token' do
before do
post :update, params: { user: { password: password, password_confirmation: password, reset_password_token: 'some_invalid_value' } }
end
it 'renders reset password and retains password' do
expect(response)
.to render_template(:new)
expect(User.find(user.id))
.to be_present
.and be_external_or_valid_password(user.password)
end
end
end
end

View file

@ -40,15 +40,11 @@ RSpec.describe Api::RateLimitHeaders do
end
end
it 'applies rate limiting limit header' do
it 'provides rate limit information in headers' do
expect(response.headers['X-RateLimit-Limit']).to eq '100'
end
it 'applies rate limiting remaining header' do
expect(response.headers['X-RateLimit-Remaining']).to eq '80'
end
it 'applies rate limiting reset header' do
expect(response.headers['X-RateLimit-Reset']).to eq (start_time + 10.seconds).iso8601(6)
end
end

View file

@ -23,11 +23,9 @@ RSpec.describe Filters::StatusesController do
get :index, params: { filter_id: filter }
end
it 'returns http success' do
it 'returns http success and private cache control headers' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end

View file

@ -14,11 +14,9 @@ RSpec.describe RelationshipsController do
get :show, params: { page: 2, relationship: 'followed_by' }
end
it 'returns http success' do
it 'returns http success and private cache control headers' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end

View file

@ -3,7 +3,7 @@
if defined?(Flatware)
Flatware.configure do |config|
config.after_fork do |test_env_number|
unless ENV.fetch('DISABLE_SIMPLECOV', nil) == 'true'
if ENV.fetch('COVERAGE', false)
require 'simplecov'
SimpleCov.at_fork.call(test_env_number) # Combines parallel coverage results
end

View file

@ -233,6 +233,28 @@ RSpec.describe FeedManager do
end
end
context 'with list feed' do
let(:list) { Fabricate(:list, account: bob) }
before do
bob.follow!(alice)
list.list_accounts.create!(account: alice)
end
it "returns false for followee's status" do
status = Fabricate(:status, text: 'Hello world', account: alice)
expect(subject.filter?(:list, status, list)).to be false
end
it 'returns false for reblog by followee' do
status = Fabricate(:status, text: 'Hello world', account: jeff)
reblog = Fabricate(:status, reblog: status, account: alice)
expect(subject.filter?(:list, reblog, list)).to be false
end
end
context 'with mentions feed' do
it 'returns true for status that mentions blocked account' do
bob.block!(jeff)

View file

@ -2,7 +2,7 @@
ENV['RAILS_ENV'] ||= 'test'
unless ENV['DISABLE_SIMPLECOV'] == 'true'
if ENV.fetch('COVERAGE', false)
require 'simplecov'
SimpleCov.start 'rails' do

View file

@ -0,0 +1,34 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Auth Passwords' do
describe 'GET /auth/password/edit' do
context 'with invalid reset_password_token' do
it 'redirects to #new' do
get edit_user_password_path, params: { reset_password_token: 'some_invalid_value' }
expect(response)
.to redirect_to new_user_password_path
end
end
end
describe 'PUT /auth/password' do
let(:user) { Fabricate(:user) }
let(:password) { 'reset0password' }
context 'with invalid reset_password_token' do
it 'renders reset password and retains password' do
put user_password_path, params: { user: { password: password, password_confirmation: password, reset_password_token: 'some_invalid_value' } }
expect(response.body)
.to include(I18n.t('auth.set_new_password'))
expect(User.find(user.id))
.to be_present
.and be_external_or_valid_password(user.password)
end
end
end
end

View file

@ -116,19 +116,13 @@ RSpec.describe 'The /.well-known/webfinger endpoint' do
perform_request!
end
it 'returns http success' do
it 'returns http success with expect headers and media type' do
expect(response).to have_http_status(200)
end
it 'sets only a Vary Origin header' do
expect(response.headers['Vary']).to eq('Origin')
end
it 'returns application/jrd+json' do
expect(response.media_type).to eq 'application/jrd+json'
end
it 'returns links for the internal account' do
expect(response.parsed_body)
.to include(
subject: 'acct:mastodon.internal@cb6e6126.ngrok.io',

View file

@ -0,0 +1,112 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Admin Reports' do
let(:user) { Fabricate(:admin_user) }
before { sign_in(user) }
describe 'Viewing existing reports' do
let!(:unresolved_report) { Fabricate(:report, action_taken_at: nil, comment: 'First report') }
let!(:resolved_report) { Fabricate(:report, action_taken_at: Time.now.utc, comment: 'Second report') }
let!(:report_note) { Fabricate :report_note, report: resolved_report, content: 'Note about resolved report' }
it 'Shows basic report details' do
visit admin_reports_path
expect(page)
.to have_content(unresolved_report.comment)
.and have_no_content(resolved_report.comment)
click_on I18n.t('admin.reports.resolved')
expect(page)
.to have_content(resolved_report.comment)
.and have_no_content(unresolved_report.comment)
click_on resolved_report.comment
expect(page)
.to have_title(I18n.t('admin.reports.report', id: resolved_report.id))
.and have_content(resolved_report.comment)
.and have_content(report_note.content)
end
end
describe 'Resolving reports' do
let!(:report) { Fabricate :report }
it 'resolves an open report' do
visit admin_report_path(report)
within '.content__heading__actions' do
click_on I18n.t('admin.reports.mark_as_resolved')
end
expect(page)
.to have_title(I18n.t('admin.reports.title'))
.and have_content(I18n.t('admin.reports.resolved_msg'))
report.reload
expect(report.action_taken_by_account)
.to eq user.account
expect(report)
.to be_action_taken
expect(last_action_log.target)
.to eq(report)
end
end
describe 'Reopening reports' do
let!(:report) { Fabricate :report, action_taken_at: 3.days.ago }
it 'reopens a resolved report' do
visit admin_report_path(report)
within '.content__heading__actions' do
click_on I18n.t('admin.reports.mark_as_unresolved')
end
expect(page)
.to have_title(I18n.t('admin.reports.report', id: report.id))
report.reload
expect(report.action_taken_by_account)
.to be_nil
expect(report)
.to_not be_action_taken
expect(last_action_log.target)
.to eq(report)
end
end
describe 'Assigning reports' do
let!(:report) { Fabricate :report }
it 'assigns report to user and then unassigns' do
visit admin_report_path(report)
click_on I18n.t('admin.reports.assign_to_self')
expect(page)
.to have_title(I18n.t('admin.reports.report', id: report.id))
report.reload
expect(report.assigned_account)
.to eq user.account
expect(last_action_log.target)
.to eq(report)
click_on I18n.t('admin.reports.unassign')
expect(page)
.to have_title(I18n.t('admin.reports.report', id: report.id))
report.reload
expect(report.assigned_account)
.to be_nil
expect(last_action_log.target)
.to eq(report)
end
end
private
def last_action_log
Admin::ActionLog.last
end
end

View file

@ -0,0 +1,83 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Auth Passwords' do
let(:user) { Fabricate :user }
let!(:session_activation) { Fabricate(:session_activation, user: user) }
let!(:access_token) { Fabricate(:access_token, resource_owner_id: user.id) }
let!(:web_push_subscription) { Fabricate(:web_push_subscription, access_token: access_token) }
describe 'Resetting a password', :inline_jobs do
let(:new_password) { 'New.Pass.123' }
before { allow(Devise).to receive(:pam_authentication).and_return(false) } # Avoid the "seamless external" path
it 'initiates reset, sends link, resets password from form, clears data' do
visit new_user_password_path
expect(page)
.to have_title(I18n.t('auth.reset_password'))
submit_email_reset
expect(page)
.to have_title(I18n.t('auth.set_new_password'))
set_new_password
expect(page)
.to have_title(I18n.t('auth.login'))
# Change password
expect(User.find(user.id))
.to be_present
.and be_valid_password(new_password)
# Deactivate session
expect(user_session_count)
.to eq(0)
expect { session_activation.reload }
.to raise_error(ActiveRecord::RecordNotFound)
# Revoke tokens
expect(user_token_count)
.to eq(0)
# Remove push subs
expect(push_subs_count)
.to eq(0)
expect { web_push_subscription.reload }
.to raise_error(ActiveRecord::RecordNotFound)
end
def submit_email_reset
fill_in 'user_email', with: user.email
click_on I18n.t('auth.reset_password')
open_last_email
visit_in_email(I18n.t('devise.mailer.reset_password_instructions.action'))
end
def set_new_password
fill_in 'user_password', with: new_password
fill_in 'user_password_confirmation', with: new_password
click_on I18n.t('auth.set_new_password')
end
def user_session_count
user
.session_activations
.count
end
def user_token_count
Doorkeeper::AccessToken
.active_for(user)
.count
end
def push_subs_count
Web::PushSubscription
.where(user: user)
.or(Web::PushSubscription.where(access_token: access_token))
.count
end
end
end

View file

@ -38,6 +38,9 @@ RSpec.describe 'Settings applications page' do
expect(page)
.to have_content(I18n.t('doorkeeper.applications.index.title'))
.and have_content('My new app')
.and have_content('read')
.and have_content('write')
.and have_content('follow')
end
it 'does not save with invalid form values' do
@ -73,10 +76,12 @@ RSpec.describe 'Settings applications page' do
fill_in form_app_name_label,
with: 'My new app name with a new value'
check 'push', id: :doorkeeper_application_scopes_push
submit_form
expect(page)
.to have_content('My new app name with a new value')
.and have_checked_field('push', id: :doorkeeper_application_scopes_push)
end
it 'does not update with wrong values' do

View file

@ -33,15 +33,11 @@ RSpec.describe PollExpirationNotifyWorker do
end
context 'when poll is local' do
it 'notifies voters' do
it 'notifies voters, owner, and local voters' do
expect(ActivityPub::DistributePollUpdateWorker).to have_enqueued_sidekiq_job(poll.status.id)
end
it 'notifies owner' do
expect(LocalNotificationWorker).to have_enqueued_sidekiq_job(poll.account.id, poll.id, 'Poll', 'poll')
end
it 'notifies local voters' do
expect(LocalNotificationWorker).to have_enqueued_sidekiq_job(poll_vote.account.id, poll.id, 'Poll', 'poll')
end
end
@ -49,15 +45,11 @@ RSpec.describe PollExpirationNotifyWorker do
context 'when poll is remote' do
let(:remote?) { true }
it 'does not notify remote voters' do
it 'does not notify remote voters or owner, does notify local voters' do
expect(ActivityPub::DistributePollUpdateWorker).to_not have_enqueued_sidekiq_job(poll.status.id)
end
it 'does not notify owner' do
expect(LocalNotificationWorker).to_not have_enqueued_sidekiq_job(poll.account.id, poll.id, 'Poll', 'poll')
end
it 'notifies local voters' do
expect(LocalNotificationWorker).to have_enqueued_sidekiq_job(poll_vote.account.id, poll.id, 'Poll', 'poll')
end
end

View file

@ -13,11 +13,9 @@ RSpec.describe PublishScheduledStatusWorker do
end
context 'when the account is not disabled' do
it 'creates a status' do
it 'creates a status and removes scheduled record' do
expect(scheduled_status.account.statuses.first.text).to eq 'Hello world, future!'
end
it 'removes the scheduled status' do
expect(ScheduledStatus.find_by(id: scheduled_status.id)).to be_nil
end
end
@ -25,11 +23,9 @@ RSpec.describe PublishScheduledStatusWorker do
context 'when the account is disabled' do
let(:scheduled_status) { Fabricate(:scheduled_status, account: Fabricate(:account, user: Fabricate(:user, disabled: true))) }
it 'does not create a status' do
it 'does not create a status and removes scheduled record' do
expect(Status.count).to eq 0
end
it 'removes the scheduled status' do
expect(ScheduledStatus.find_by(id: scheduled_status.id)).to be_nil
end
end

View file

@ -18,14 +18,11 @@ RSpec.describe UnfollowFollowWorker do
let(:show_reblogs) { true }
describe 'perform' do
it 'unfollows source account and follows target account' do
it 'unfollows source account and follows target account and preserves show_reblogs' do
subject.perform(local_follower.id, source_account.id, target_account.id)
expect(local_follower.following?(source_account)).to be false
expect(local_follower.following?(target_account)).to be true
end
it 'preserves show_reblogs' do
subject.perform(local_follower.id, source_account.id, target_account.id)
expect(Follow.find_by(account: local_follower, target_account: target_account).show_reblogs?).to be show_reblogs
end
end
@ -35,14 +32,11 @@ RSpec.describe UnfollowFollowWorker do
let(:show_reblogs) { false }
describe 'perform' do
it 'unfollows source account and follows target account' do
it 'unfollows source account and follows target account and preserves show_reblogs' do
subject.perform(local_follower.id, source_account.id, target_account.id)
expect(local_follower.following?(source_account)).to be false
expect(local_follower.following?(target_account)).to be true
end
it 'preserves show_reblogs' do
subject.perform(local_follower.id, source_account.id, target_account.id)
expect(Follow.find_by(account: local_follower, target_account: target_account).show_reblogs?).to be show_reblogs
end
end