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

This commit is contained in:
KMY 2024-06-18 07:43:33 +09:00
commit aa2cdc898a
271 changed files with 1839 additions and 1397 deletions

View file

@ -23,8 +23,11 @@ describe Settings::Preferences::AppearanceController do
end
describe 'PUT #update' do
subject { put :update, params: { user: { settings_attributes: { theme: 'contrast' } } } }
it 'redirects correctly' do
put :update, params: { user: { setting_theme: 'contrast' } }
expect { subject }
.to change { user.reload.settings.theme }.to('contrast')
expect(response).to redirect_to(settings_preferences_appearance_path)
end

View file

@ -6,50 +6,50 @@ RSpec.describe Admin::AccountModerationNotesHelper do
include AccountsHelper
describe '#admin_account_link_to' do
subject { helper.admin_account_link_to(account) }
context 'when Account is nil' do
let(:account) { nil }
it 'returns nil' do
expect(helper.admin_account_link_to(account)).to be_nil
expect(subject).to be_nil
end
end
context 'with account' do
let(:account) { Fabricate(:account) }
it 'calls #link_to' do
allow(helper).to receive(:link_to)
helper.admin_account_link_to(account)
expect(helper).to have_received(:link_to).with(
admin_account_path(account.id),
class: name_tag_classes(account),
title: account.acct
)
it 'returns a labeled avatar link to the account' do
expect(parsed_html.a[:href]).to eq admin_account_path(account.id)
expect(parsed_html.a[:class]).to eq 'name-tag'
expect(parsed_html.a.span.text).to eq account.acct
end
end
end
describe '#admin_account_inline_link_to' do
subject { helper.admin_account_inline_link_to(account) }
context 'when Account is nil' do
let(:account) { nil }
it 'returns nil' do
expect(helper.admin_account_inline_link_to(account)).to be_nil
expect(subject).to be_nil
end
end
context 'with account' do
let(:account) { Fabricate(:account) }
it 'calls #link_to' do
result = helper.admin_account_inline_link_to(account)
expect(result).to match(name_tag_classes(account, true))
expect(result).to match(account.acct)
expect(result).to match(admin_account_path(account.id))
it 'returns an inline link to the account' do
expect(parsed_html.a[:href]).to eq admin_account_path(account.id)
expect(parsed_html.a[:class]).to eq 'inline-name-tag'
expect(parsed_html.a.span.text).to eq account.acct
end
end
end
def parsed_html
Nokogiri::Slop(subject)
end
end

View file

@ -54,7 +54,7 @@ RSpec.describe ActivityPub::Activity::Flag do
}.with_indifferent_access, sender)
end
let(:long_comment) { Faker::Lorem.characters(number: 6000) }
let(:long_comment) { 'a' * described_class::COMMENT_SIZE_LIMIT * 2 }
before do
subject.perform
@ -63,10 +63,12 @@ RSpec.describe ActivityPub::Activity::Flag do
it 'creates a report but with a truncated comment' do
report = Report.find_by(account: sender, target_account: flagged)
expect(report).to_not be_nil
expect(report.comment.length).to eq 5000
expect(report.comment).to eq long_comment[0...5000]
expect(report.status_ids).to eq [status.id]
expect(report)
.to be_present
.and have_attributes(status_ids: [status.id])
expect(report.comment)
.to have_attributes(length: described_class::COMMENT_SIZE_LIMIT)
.and eq(long_comment[0...described_class::COMMENT_SIZE_LIMIT])
end
end

View file

@ -325,7 +325,7 @@ RSpec.describe ActivityPub::Activity::Like do
Fabricate(:custom_emoji, domain: nil, shortcode: 'tinking', license: 'Everyone but Ohagi')
end
it 'create emoji reaction' do # rubocop:disable RSpec/MultipleExpectations
it 'create emoji reaction' do
expect(subject.count).to eq 1
expect(subject.first.name).to eq 'tinking'
expect(subject.first.account).to eq sender

View file

@ -243,13 +243,13 @@ RSpec.describe FeedManager do
expect(described_class.instance.filter?(:mentions, reply, bob)).to be true
end
it 'returns true for status by silenced account who recipient is not following' do
it 'returns false for status by limited account who recipient is not following' do
status = Fabricate(:status, text: 'Hello world', account: alice)
alice.silence!
expect(described_class.instance.filter?(:mentions, status, bob)).to be true
expect(described_class.instance.filter?(:mentions, status, bob)).to be false
end
it 'returns false for status by followed silenced account' do
it 'returns false for status by followed limited account' do
status = Fabricate(:status, text: 'Hello world', account: alice)
alice.silence!
bob.follow!(alice)

View file

@ -3,22 +3,30 @@
require 'rails_helper'
RSpec.describe AccountDomainBlock do
let(:account) { Fabricate(:account) }
it 'removes blocking cache after creation' do
account = Fabricate(:account)
Rails.cache.write("exclude_domains_for:#{account.id}", 'a.domain.already.blocked')
described_class.create!(account: account, domain: 'a.domain.blocked.later')
expect(Rails.cache.exist?("exclude_domains_for:#{account.id}")).to be false
expect { block_domain_for_account('a.domain.blocked.later') }
.to change { account_has_exclude_domains_cache? }.to(false)
end
it 'removes blocking cache after destruction' do
account = Fabricate(:account)
block = described_class.create!(account: account, domain: 'domain')
block = block_domain_for_account('domain')
Rails.cache.write("exclude_domains_for:#{account.id}", 'domain')
block.destroy!
expect { block.destroy! }
.to change { account_has_exclude_domains_cache? }.to(false)
end
expect(Rails.cache.exist?("exclude_domains_for:#{account.id}")).to be false
private
def block_domain_for_account(domain)
Fabricate(:account_domain_block, account: account, domain: domain)
end
def account_has_exclude_domains_cache?
Rails.cache.exist?("exclude_domains_for:#{account.id}")
end
end

View file

@ -991,20 +991,20 @@ RSpec.describe Account do
expect(account).to model_have_error_on_field(:username)
end
it 'is invalid if the username is longer than 30 characters' do
account = Fabricate.build(:account, username: Faker::Lorem.characters(number: 31))
it 'is invalid if the username is longer than the character limit' do
account = Fabricate.build(:account, username: username_over_limit)
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(number: 31))
it 'is invalid if the display name is longer than the character limit' do
account = Fabricate.build(:account, display_name: username_over_limit)
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(number: 501))
it 'is invalid if the note is longer than the character limit' do
account = Fabricate.build(:account, note: account_note_over_limit)
account.valid?
expect(account).to model_have_error_on_field(:note)
end
@ -1037,24 +1037,32 @@ RSpec.describe Account do
expect(account).to model_have_error_on_field(:username)
end
it 'is valid even if the username is longer than 30 characters' do
account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(number: 31))
it 'is valid even if the username is longer than the character limit' do
account = Fabricate.build(:account, domain: 'domain', username: username_over_limit)
account.valid?
expect(account).to_not 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(number: 31))
it 'is valid even if the display name is longer than the character limit' do
account = Fabricate.build(:account, domain: 'domain', display_name: username_over_limit)
account.valid?
expect(account).to_not 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(number: 501))
it 'is valid even if the note is longer than the character limit' do
account = Fabricate.build(:account, domain: 'domain', note: account_note_over_limit)
account.valid?
expect(account).to_not model_have_error_on_field(:note)
end
end
def username_over_limit
'a' * described_class::USERNAME_LENGTH_LIMIT * 2
end
def account_note_over_limit
'a' * described_class::NOTE_LENGTH_LIMIT * 2
end
end
describe 'scopes' do

View file

@ -3,6 +3,19 @@
require 'rails_helper'
describe Appeal do
describe 'Validations' do
it 'validates text length is under limit' do
appeal = Fabricate.build(
:appeal,
strike: Fabricate(:account_warning),
text: 'a' * described_class::TEXT_LENGTH_LIMIT * 2
)
expect(appeal).to_not be_valid
expect(appeal).to model_have_error_on_field(:text)
end
end
describe 'scopes' do
describe 'approved' do
let(:approved_appeal) { Fabricate(:appeal, approved_at: 10.days.ago) }

View file

@ -36,16 +36,15 @@ RSpec.describe Follow do
end
end
describe 'recent' do
it 'sorts so that more recent follows comes earlier' do
follow0 = described_class.create!(account: alice, target_account: bob)
follow1 = described_class.create!(account: bob, target_account: alice)
describe '.recent' do
let!(:follow_earlier) { Fabricate(:follow) }
let!(:follow_later) { Fabricate(:follow) }
a = described_class.recent.to_a
it 'sorts with most recent follows first' do
results = described_class.recent
expect(a.size).to eq 2
expect(a[0]).to eq follow1
expect(a[1]).to eq follow0
expect(results.size).to eq 2
expect(results).to eq [follow_later, follow_earlier]
end
end

View file

@ -8,11 +8,6 @@ RSpec.describe Import do
let(:data) { attachment_fixture('imports.txt') }
describe 'validations' do
it 'has a valid parameters' do
import = described_class.create(account: account, type: type, data: data)
expect(import).to be_valid
end
it 'is invalid without an type' do
import = described_class.create(account: account, data: data)
expect(import).to model_have_error_on_field(:type)

View file

@ -31,14 +31,6 @@ describe Poll do
end
describe 'validations' do
context 'when valid' do
let(:poll) { Fabricate.build(:poll) }
it 'is valid with valid attributes' do
expect(poll).to be_valid
end
end
context 'when not valid' do
let(:poll) { Fabricate.build(:poll, expires_at: nil) }

View file

@ -123,14 +123,14 @@ describe Report do
describe 'validations' do
let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') }
it 'is invalid if comment is longer than 1000 characters only if reporter is local' do
report = Fabricate.build(:report, comment: Faker::Lorem.characters(number: 1001))
it 'is invalid if comment is longer than character limit and reporter is local' do
report = Fabricate.build(:report, comment: comment_over_limit)
expect(report.valid?).to be false
expect(report).to model_have_error_on_field(:comment)
end
it 'is valid if comment is longer than 1000 characters and reporter is not local' do
report = Fabricate.build(:report, account: remote_account, comment: Faker::Lorem.characters(number: 1001))
it 'is valid if comment is longer than character limit and reporter is not local' do
report = Fabricate.build(:report, account: remote_account, comment: comment_over_limit)
expect(report.valid?).to be true
end
@ -146,5 +146,9 @@ describe Report do
expect(report.valid?).to be false
expect(report).to model_have_error_on_field(:rule_ids)
end
def comment_over_limit
'a' * described_class::COMMENT_SIZE_LIMIT * 2
end
end
end

View file

@ -0,0 +1,50 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ScheduledStatus do
let(:account) { Fabricate(:account) }
describe 'validations' do
context 'when scheduled_at is less than minimum offset' do
subject { Fabricate.build(:scheduled_status, scheduled_at: 4.minutes.from_now, account: account) }
it 'is not valid', :aggregate_failures do
expect(subject).to_not be_valid
expect(subject.errors[:scheduled_at]).to include(I18n.t('scheduled_statuses.too_soon'))
end
end
context 'when account has reached total limit' do
subject { Fabricate.build(:scheduled_status, account: account) }
before do
allow(account.scheduled_statuses).to receive(:count).and_return(described_class::TOTAL_LIMIT)
end
it 'is not valid', :aggregate_failures do
expect(subject).to_not be_valid
expect(subject.errors[:base]).to include(I18n.t('scheduled_statuses.over_total_limit', limit: ScheduledStatus::TOTAL_LIMIT))
end
end
context 'when account has reached daily limit' do
subject { Fabricate.build(:scheduled_status, account: account, scheduled_at: base_time + 10.minutes) }
let(:base_time) { Time.current.change(hour: 12) }
before do
stub_const('ScheduledStatus::DAILY_LIMIT', 3)
travel_to base_time do
Fabricate.times(ScheduledStatus::DAILY_LIMIT, :scheduled_status, account: account, scheduled_at: base_time + 1.hour)
end
end
it 'is not valid', :aggregate_failures do
expect(subject).to_not be_valid
expect(subject.errors[:base]).to include(I18n.t('scheduled_statuses.over_daily_limit', limit: ScheduledStatus::DAILY_LIMIT))
end
end
end
end

View file

@ -71,8 +71,8 @@ RSpec.describe WebauthnCredential do
expect(webauthn_credential).to model_have_error_on_field(:sign_count)
end
it 'is invalid if sign_count is greater 2**63 - 1' do
webauthn_credential = Fabricate.build(:webauthn_credential, sign_count: 2**63)
it 'is invalid if sign_count is greater than the limit' do
webauthn_credential = Fabricate.build(:webauthn_credential, sign_count: (described_class::SIGN_COUNT_LIMIT * 2))
webauthn_credential.valid?

View file

@ -113,7 +113,7 @@ RSpec.describe 'Domain Allows' do
end
context 'with invalid domain name' do
let(:params) { 'foo bar' }
let(:params) { { domain: 'foo bar' } }
it 'returns http unprocessable entity' do
subject

View file

@ -282,6 +282,32 @@ describe '/api/v1/statuses' do
expect(response).to have_http_status(404)
end
end
context 'when scheduling a status' do
let(:params) { { status: 'Hello world', scheduled_at: 10.minutes.from_now } }
let(:account) { user.account }
it 'returns HTTP 200' do
subject
expect(response).to have_http_status(200)
end
it 'creates a scheduled status' do
expect { subject }.to change { account.scheduled_statuses.count }.from(0).to(1)
end
context 'when the scheduling time is less than 5 minutes' do
let(:params) { { status: 'Hello world', scheduled_at: 4.minutes.from_now } }
it 'does not create a scheduled status', :aggregate_failures do
subject
expect(response).to have_http_status(422)
expect(account.scheduled_statuses).to be_empty
end
end
end
end
describe 'DELETE /api/v1/statuses/:id' do

View file

@ -58,6 +58,7 @@ RSpec.describe 'Notifications' do
expect(response).to have_http_status(200)
expect(body_json_types.uniq).to eq ['mention']
expect(body_as_json[0][:page_min_id]).to_not be_nil
end
end

View file

@ -57,9 +57,11 @@ RSpec.describe BackupService do
end
def expect_outbox_export
json = export_json(:outbox)
body = export_json_raw(:outbox)
json = Oj.load(body)
aggregate_failures do
expect(body.scan('@context').count).to eq 1
expect(json['@context']).to_not be_nil
expect(json['type']).to eq 'OrderedCollection'
expect(json['totalItems']).to eq 4
@ -89,8 +91,12 @@ RSpec.describe BackupService do
end
end
def export_json_raw(type)
read_zip_file(backup, "#{type}.json")
end
def export_json(type)
Oj.load(read_zip_file(backup, "#{type}.json"))
Oj.load(export_json_raw(type))
end
def include_create_item(status)

View file

@ -129,6 +129,35 @@ RSpec.describe NotifyService do
end
end
describe NotifyService::DismissCondition do
subject { described_class.new(notification) }
let(:activity) { Fabricate(:mention, status: Fabricate(:status)) }
let(:notification) { Fabricate(:notification, type: :mention, activity: activity, from_account: activity.status.account, account: activity.account) }
describe '#dismiss?' do
context 'when sender is silenced' do
before do
notification.from_account.silence!
end
it 'returns false' do
expect(subject.dismiss?).to be false
end
end
context 'when recipient has blocked sender' do
before do
notification.account.block!(notification.from_account)
end
it 'returns true' do
expect(subject.dismiss?).to be true
end
end
end
end
describe NotifyService::FilterCondition do
subject { described_class.new(notification) }

View file

@ -61,6 +61,16 @@ RSpec.describe PostStatusService do
status2 = subject.call(account, text: 'test', idempotency: 'meepmeep', scheduled_at: future)
expect(status2.id).to eq status1.id
end
context 'when scheduled_at is less than min offset' do
let(:invalid_scheduled_time) { 4.minutes.from_now }
it 'raises invalid record error' do
expect do
subject.call(account, text: 'Hi future!', scheduled_at: invalid_scheduled_time)
end.to raise_error(ActiveRecord::RecordInvalid)
end
end
end
it 'creates response to the original status of boost' do

View file

@ -50,30 +50,16 @@ RSpec.describe RemoveStatusService, :sidekiq_inline do
it 'sends Delete activity to followers' do
subject.call(status)
expect(a_request(:post, hank.shared_inbox_url).with(
body: hash_including({
'type' => 'Delete',
'object' => {
'type' => 'Tombstone',
'id' => ActivityPub::TagManager.instance.uri_for(status),
'atomUri' => OStatus::TagManager.instance.uri_for(status),
},
})
)).to have_been_made.once
expect(delete_delivery(hank, status))
.to have_been_made.once
end
it 'sends Delete activity to rebloggers' do
subject.call(status)
expect(a_request(:post, bill.shared_inbox_url).with(
body: hash_including({
'type' => 'Delete',
'object' => {
'type' => 'Tombstone',
'id' => ActivityPub::TagManager.instance.uri_for(status),
'atomUri' => OStatus::TagManager.instance.uri_for(status),
},
})
)).to have_been_made.once
expect(delete_delivery(bill, status))
.to have_been_made.once
end
it 'remove status from notifications' do
@ -81,6 +67,22 @@ RSpec.describe RemoveStatusService, :sidekiq_inline do
Notification.where(activity_type: 'Favourite', from_account: jeff, account: alice).count
}.from(1).to(0)
end
def delete_delivery(target, status)
a_request(:post, target.shared_inbox_url)
.with(body: delete_activity_for(status))
end
def delete_activity_for(status)
hash_including(
'type' => 'Delete',
'object' => {
'type' => 'Tombstone',
'id' => ActivityPub::TagManager.instance.uri_for(status),
'atomUri' => OStatus::TagManager.instance.uri_for(status),
}
)
end
end
context 'when removed status is null-searchability' do
@ -141,6 +143,7 @@ RSpec.describe RemoveStatusService, :sidekiq_inline do
it 'do not send Delete activity to followers', :sidekiq_inline do
subject.call(status)
expect(a_request(:post, hank.inbox_url)).to_not have_been_made
expect(a_request(:post, hank.shared_inbox_url)).to_not have_been_made
end
@ -152,15 +155,9 @@ RSpec.describe RemoveStatusService, :sidekiq_inline do
it 'sends Undo activity to followers' do
subject.call(status)
expect(a_request(:post, hank.shared_inbox_url).with(
body: hash_including({
'type' => 'Undo',
'object' => hash_including({
'type' => 'Announce',
'object' => ActivityPub::TagManager.instance.uri_for(original_status),
}),
})
)).to have_been_made.once
expect(undo_delivery(hank, original_status))
.to have_been_made.once
end
end
@ -170,15 +167,9 @@ RSpec.describe RemoveStatusService, :sidekiq_inline do
it 'sends Undo activity to followers' do
subject.call(status)
expect(a_request(:post, hank.shared_inbox_url).with(
body: hash_including({
'type' => 'Undo',
'object' => hash_including({
'type' => 'Announce',
'object' => ActivityPub::TagManager.instance.uri_for(original_status),
}),
})
)).to have_been_made.once
expect(undo_delivery(hank, original_status))
.to have_been_made.once
end
end
@ -188,15 +179,24 @@ RSpec.describe RemoveStatusService, :sidekiq_inline do
it 'sends Undo activity to followers' do
subject.call(status)
expect(a_request(:post, bill.shared_inbox_url).with(
body: hash_including({
'type' => 'Undo',
'object' => hash_including({
'type' => 'Announce',
'object' => ActivityPub::TagManager.instance.uri_for(original_status),
}),
})
)).to have_been_made.once
expect(undo_delivery(bill, original_status))
.to have_been_made.once
end
end
def undo_delivery(target, status)
a_request(:post, target.shared_inbox_url)
.with(body: undo_activity_for(status))
end
def undo_activity_for(status)
hash_including(
'type' => 'Undo',
'object' => hash_including(
'type' => 'Announce',
'object' => ActivityPub::TagManager.instance.uri_for(status)
)
)
end
end