Merge commit 'c357a7f8d697ede4df4be74456b0497118c9d049' into glitch-soc/merge-upstream

This commit is contained in:
Claire
2025-06-30 17:52:22 +02:00
154 changed files with 1168 additions and 826 deletions

View File

@@ -0,0 +1,6 @@
# frozen_string_literal: true
Fabricator(:fasp_follow_recommendation, from: 'Fasp::FollowRecommendation') do
requesting_account { Fabricate.build(:account) }
recommended_account { Fabricate.build(:account, domain: 'fedi.example.com') }
end

View File

@@ -41,3 +41,15 @@ Fabricator(:follow_recommendation_fasp, from: :fasp_provider) do
def fasp.update_remote_capabilities = true
end
end
Fabricator(:account_search_fasp, from: :fasp_provider) do
confirmed true
capabilities [
{ id: 'account_search', version: '0.1', enabled: true },
]
after_build do |fasp|
# Prevent fabrication from attempting an HTTP call to the provider
def fasp.update_remote_capabilities = true
end
end

View File

@@ -3,10 +3,10 @@
require 'rails_helper'
RSpec.describe Mastodon::EmailConfigurationHelper do
describe '#smtp_settings' do
describe '#convert_smtp_settings' do
subject { described_class }
let(:converted_settings) { subject.smtp_settings(configuration) }
let(:converted_settings) { subject.convert_smtp_settings(configuration) }
let(:base_configuration) do
{
address: 'localhost',

View File

@@ -103,4 +103,9 @@ class UserMailerPreview < ActionMailer::Preview
def terms_of_service_changed
UserMailer.terms_of_service_changed(User.first, TermsOfService.live.first)
end
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/announcement_published
def announcement_published
UserMailer.announcement_published(User.first, Announcement.last)
end
end

View File

@@ -14,6 +14,43 @@ RSpec.describe UserMailer do
end
end
shared_examples 'optional bulk mailer settings' do
context 'when no optional bulk mailer settings are present' do
it 'does not include delivery method options' do
expect(mail.message.delivery_method.settings).to be_empty
end
end
context 'when optional bulk mailer settings are present' do
let(:smtp_settings) do
{
address: 'localhost',
port: 25,
authentication: 'none',
}
end
before do
Rails.configuration.x.email ||= ActiveSupport::OrderedOptions.new
Rails.configuration.x.email.update({ bulk_mail: { smtp_settings: } })
end
after do
Rails.configuration.x.email = nil
end
it 'uses the bulk mailer settings' do
expect(mail.message.delivery_method.settings).to eq({
address: 'localhost',
port: 25,
authentication: nil,
enable_starttls: nil,
enable_starttls_auto: true,
})
end
end
end
let(:receiver) { Fabricate(:user) }
describe '#confirmation_instructions' do
@@ -316,6 +353,8 @@ RSpec.describe UserMailer do
.and(have_subject(I18n.t('user_mailer.terms_of_service_changed.subject')))
.and(have_body_text(I18n.t('user_mailer.terms_of_service_changed.changelog')))
end
it_behaves_like 'optional bulk mailer settings'
end
describe '#announcement_published' do
@@ -328,5 +367,7 @@ RSpec.describe UserMailer do
.and(have_subject(I18n.t('user_mailer.announcement_published.subject')))
.and(have_body_text(I18n.t('user_mailer.announcement_published.description', domain: local_domain_uri.host)))
end
it_behaves_like 'optional bulk mailer settings'
end
end

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe AccountSuggestions::FaspSource do
describe '#get', feature: :fasp do
subject { described_class.new }
let(:bob) { Fabricate(:account) }
let(:alice) { Fabricate(:account, domain: 'fedi.example.com') }
let(:eve) { Fabricate(:account, domain: 'fedi.example.com') }
before do
[alice, eve].each do |recommended_account|
Fasp::FollowRecommendation.create!(requesting_account: bob, recommended_account:)
end
end
it 'returns recommendations obtained by FASP' do
expect(subject.get(bob)).to contain_exactly([alice.id, :fasp], [eve.id, :fasp])
end
end
end

View File

@@ -3,6 +3,87 @@
require 'rails_helper'
RSpec.describe TermsOfService do
describe 'Validations' do
subject { Fabricate.build :terms_of_service }
it { is_expected.to validate_presence_of(:text) }
it { is_expected.to validate_uniqueness_of(:effective_date) }
it { is_expected.to allow_values(2.days.from_now).for(:effective_date) }
it { is_expected.to_not allow_values(2.days.ago).for(:effective_date) }
context 'with an existing published effective TOS' do
before do
travel_to 5.days.ago do
Fabricate :terms_of_service, published_at: 2.days.ago, effective_date: 1.day.from_now
end
end
it { is_expected.to allow_values(1.day.ago).for(:effective_date) }
end
context 'when published' do
subject { Fabricate.build :terms_of_service, published_at: Time.zone.today }
it { is_expected.to validate_presence_of(:changelog) }
it { is_expected.to validate_presence_of(:effective_date) }
end
end
describe 'Scopes' do
describe '.published' do
let!(:unpublished) { Fabricate :terms_of_service, published_at: nil }
let!(:published_older_effective) { travel_to(3.days.ago) { Fabricate :terms_of_service, published_at: 5.days.ago, effective_date: Time.zone.today } }
let!(:published_newer_effective) { travel_to(2.days.ago) { Fabricate :terms_of_service, published_at: 5.days.ago, effective_date: Time.zone.today } }
it 'returns published records in correct order' do
expect(described_class.published)
.to eq([published_newer_effective, published_older_effective])
.and not_include(unpublished)
end
end
describe '.live' do
let!(:not_effective) { Fabricate :terms_of_service }
let!(:effective_past) { travel_to(3.days.ago) { Fabricate :terms_of_service, effective_date: Time.zone.today } }
let!(:effective_future) { Fabricate :terms_of_service, effective_date: 3.days.from_now }
before { not_effective.update_attribute(:effective_date, nil) }
it 'returns records without effective or with past effective' do
expect(described_class.live)
.to include(not_effective)
.and include(effective_past)
.and not_include(effective_future)
end
end
describe '.upcoming' do
let!(:unpublished) { Fabricate :terms_of_service, published_at: nil }
let!(:effective_past) { travel_to(3.days.ago) { Fabricate :terms_of_service, effective_date: Time.zone.today } }
let!(:effective_future_near) { Fabricate :terms_of_service, effective_date: 3.days.from_now }
let!(:effective_future_far) { Fabricate :terms_of_service, effective_date: 5.days.from_now }
it 'returns published records with future effective date in order of soonest first' do
expect(described_class.upcoming)
.to eq([effective_future_near, effective_future_far])
.and not_include(unpublished)
.and not_include(effective_past)
end
end
describe '.draft' do
let!(:published) { Fabricate :terms_of_service, published_at: 2.days.ago }
let!(:unpublished) { Fabricate :terms_of_service, published_at: nil }
it 'returns not published records' do
expect(described_class.draft)
.to include(unpublished)
.and not_include(published)
end
end
end
describe '#scope_for_notification' do
subject { terms_of_service.scope_for_notification }

View File

@@ -118,6 +118,15 @@ RSpec.describe 'Search API' do
.to start_with('application/json')
end
end
context 'when `account_search` FASP is enabled', feature: :fasp do
it 'enqueues a retrieval job and adds a header to inform the client' do
get '/api/v2/search', headers: headers, params: params
expect(Fasp::AccountSearchWorker).to have_enqueued_sidekiq_job
expect(response.headers['Mastodon-Async-Refresh']).to be_present
end
end
end
end

View File

@@ -0,0 +1,57 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Public files' do
include RoutingHelper
context 'when requesting service worker file' do
it 'returns the file with the expected headers' do
get '/sw.js'
expect(response)
.to have_http_status(200)
expect(response.headers['Cache-Control'])
.to eq "public, max-age=#{Mastodon::Middleware::PublicFileServer::SERVICE_WORKER_TTL}, must-revalidate"
expect(response.headers['X-Content-Type-Options'])
.to eq 'nosniff'
end
end
context 'when requesting paperclip attachments', :attachment_processing do
let(:attachment) { Fabricate(:media_attachment, type: :image) }
it 'returns the file with the expected headers' do
get attachment.file.url(:original)
expect(response)
.to have_http_status(200)
expect(response.headers['Cache-Control'])
.to eq "public, max-age=#{Mastodon::Middleware::PublicFileServer::CACHE_TTL}, immutable"
expect(response.headers['Content-Security-Policy'])
.to eq "default-src 'none'; form-action 'none'"
expect(response.headers['X-Content-Type-Options'])
.to eq 'nosniff'
end
end
context 'when requesting other static files' do
it 'returns the file with the expected headers' do
get '/sounds/boop.ogg'
expect(response)
.to have_http_status(200)
expect(response.headers['Cache-Control'])
.to eq "public, max-age=#{Mastodon::Middleware::PublicFileServer::CACHE_TTL}, must-revalidate"
expect(response.headers['X-Content-Type-Options'])
.to eq 'nosniff'
end
end
end

View File

@@ -66,7 +66,7 @@ RSpec.describe SearchService do
allow(AccountSearchService).to receive(:new).and_return(service)
results = subject.call(query, nil, 10)
expect(service).to have_received(:call).with(query, nil, limit: 10, offset: 0, resolve: false, start_with_hashtag: false, use_searchable_text: true, following: false)
expect(service).to have_received(:call).with(query, nil, limit: 10, offset: 0, resolve: false, start_with_hashtag: false, use_searchable_text: true, following: false, query_fasp: nil)
expect(results).to eq empty_results.merge(accounts: [account])
end
end

View File

@@ -0,0 +1,50 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Fasp::AccountSearchWorker, feature: :fasp do
include ProviderRequestHelper
let(:provider) { Fabricate(:account_search_fasp) }
let(:account) { Fabricate(:account) }
let(:fetch_service) { instance_double(ActivityPub::FetchRemoteActorService, call: true) }
let!(:stubbed_request) do
path = '/account_search/v0/search?term=cats&limit=10'
stub_provider_request(provider,
method: :get,
path:,
response_body: [
'https://fedi.example.com/accounts/2',
'https://fedi.example.com/accounts/9',
])
end
before do
allow(ActivityPub::FetchRemoteActorService).to receive(:new).and_return(fetch_service)
end
it 'requests search results and fetches received account uris' do
described_class.new.perform('cats')
expect(stubbed_request).to have_been_made
expect(fetch_service).to have_received(:call).with('https://fedi.example.com/accounts/2')
expect(fetch_service).to have_received(:call).with('https://fedi.example.com/accounts/9')
end
it 'marks a running async refresh as finished' do
async_refresh = AsyncRefresh.create("fasp:account_search:#{Digest::MD5.base64digest('cats')}", count_results: true)
described_class.new.perform('cats')
expect(async_refresh.reload).to be_finished
end
it 'tracks the number of fetched accounts in the async refresh' do
async_refresh = AsyncRefresh.create("fasp:account_search:#{Digest::MD5.base64digest('cats')}", count_results: true)
described_class.new.perform('cats')
expect(async_refresh.reload.result_count).to eq 2
end
end

View File

@@ -7,7 +7,7 @@ RSpec.describe Fasp::FollowRecommendationWorker, feature: :fasp do
let(:provider) { Fabricate(:follow_recommendation_fasp) }
let(:account) { Fabricate(:account) }
let(:fetch_service) { instance_double(ActivityPub::FetchRemoteActorService, call: true) }
let(:fetch_service) { instance_double(ActivityPub::FetchRemoteActorService) }
let!(:stubbed_request) do
account_uri = ActivityPub::TagManager.instance.uri_for(account)
@@ -23,6 +23,8 @@ RSpec.describe Fasp::FollowRecommendationWorker, feature: :fasp do
before do
allow(ActivityPub::FetchRemoteActorService).to receive(:new).and_return(fetch_service)
allow(fetch_service).to receive(:call).and_invoke(->(_) { Fabricate(:account, domain: 'fedi.example.com') })
end
it "sends the requesting account's uri to provider and fetches received account uris" do
@@ -48,4 +50,10 @@ RSpec.describe Fasp::FollowRecommendationWorker, feature: :fasp do
expect(async_refresh.reload.result_count).to eq 2
end
it 'persists the results' do
expect do
described_class.new.perform(account.id)
end.to change(Fasp::FollowRecommendation, :count).by(2)
end
end

View File

@@ -0,0 +1,17 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Scheduler::Fasp::FollowRecommendationCleanupScheduler do
let(:worker) { described_class.new }
describe '#perform', feature: :fasp do
before do
Fabricate(:fasp_follow_recommendation, created_at: 2.days.ago)
end
it 'deletes outdated recommendations' do
expect { worker.perform }.to change(Fasp::FollowRecommendation, :count).by(-1)
end
end
end