Merge remote-tracking branch 'upstream/main'
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Dalite 2024-10-27 13:11:29 +01:00
commit 2f5d989bab
16 changed files with 1357 additions and 1462 deletions

View file

@ -111,8 +111,8 @@ group :opentelemetry do
gem 'opentelemetry-instrumentation-http_client', '~> 0.22.3', require: false gem 'opentelemetry-instrumentation-http_client', '~> 0.22.3', require: false
gem 'opentelemetry-instrumentation-net_http', '~> 0.22.4', require: false gem 'opentelemetry-instrumentation-net_http', '~> 0.22.4', require: false
gem 'opentelemetry-instrumentation-pg', '~> 0.29.0', require: false gem 'opentelemetry-instrumentation-pg', '~> 0.29.0', require: false
gem 'opentelemetry-instrumentation-rack', '~> 0.24.1', require: false gem 'opentelemetry-instrumentation-rack', '~> 0.25.0', require: false
gem 'opentelemetry-instrumentation-rails', '~> 0.31.0', require: false gem 'opentelemetry-instrumentation-rails', '~> 0.32.0', require: false
gem 'opentelemetry-instrumentation-redis', '~> 0.25.3', require: false gem 'opentelemetry-instrumentation-redis', '~> 0.25.3', require: false
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.25.2', require: false gem 'opentelemetry-instrumentation-sidekiq', '~> 0.25.2', require: false
gem 'opentelemetry-sdk', '~> 1.4', require: false gem 'opentelemetry-sdk', '~> 1.4', require: false

View file

@ -504,7 +504,7 @@ GEM
opentelemetry-semantic_conventions opentelemetry-semantic_conventions
opentelemetry-helpers-sql-obfuscation (0.2.0) opentelemetry-helpers-sql-obfuscation (0.2.0)
opentelemetry-common (~> 0.21) opentelemetry-common (~> 0.21)
opentelemetry-instrumentation-action_mailer (0.1.0) opentelemetry-instrumentation-action_mailer (0.2.0)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-active_support (~> 0.1) opentelemetry-instrumentation-active_support (~> 0.1)
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
@ -516,13 +516,13 @@ GEM
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-active_support (~> 0.1) opentelemetry-instrumentation-active_support (~> 0.1)
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-active_job (0.7.7) opentelemetry-instrumentation-active_job (0.7.8)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-active_model_serializers (0.20.2) opentelemetry-instrumentation-active_model_serializers (0.20.2)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-active_record (0.7.3) opentelemetry-instrumentation-active_record (0.8.0)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-active_support (0.6.0) opentelemetry-instrumentation-active_support (0.6.0)
@ -554,16 +554,16 @@ GEM
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-helpers-sql-obfuscation opentelemetry-helpers-sql-obfuscation
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rack (0.24.6) opentelemetry-instrumentation-rack (0.25.0)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rails (0.31.2) opentelemetry-instrumentation-rails (0.32.0)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-action_mailer (~> 0.1.0) opentelemetry-instrumentation-action_mailer (~> 0.2.0)
opentelemetry-instrumentation-action_pack (~> 0.9.0) opentelemetry-instrumentation-action_pack (~> 0.9.0)
opentelemetry-instrumentation-action_view (~> 0.7.0) opentelemetry-instrumentation-action_view (~> 0.7.0)
opentelemetry-instrumentation-active_job (~> 0.7.0) opentelemetry-instrumentation-active_job (~> 0.7.0)
opentelemetry-instrumentation-active_record (~> 0.7.0) opentelemetry-instrumentation-active_record (~> 0.8.0)
opentelemetry-instrumentation-active_support (~> 0.6.0) opentelemetry-instrumentation-active_support (~> 0.6.0)
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-redis (0.25.7) opentelemetry-instrumentation-redis (0.25.7)
@ -591,7 +591,7 @@ GEM
parslet (2.0.0) parslet (2.0.0)
pastel (0.8.0) pastel (0.8.0)
tty-color (~> 0.5) tty-color (~> 0.5)
pg (1.5.8) pg (1.5.9)
pghero (3.6.1) pghero (3.6.1)
activerecord (>= 6.1) activerecord (>= 6.1)
premailer (1.27.0) premailer (1.27.0)
@ -992,8 +992,8 @@ DEPENDENCIES
opentelemetry-instrumentation-http_client (~> 0.22.3) opentelemetry-instrumentation-http_client (~> 0.22.3)
opentelemetry-instrumentation-net_http (~> 0.22.4) opentelemetry-instrumentation-net_http (~> 0.22.4)
opentelemetry-instrumentation-pg (~> 0.29.0) opentelemetry-instrumentation-pg (~> 0.29.0)
opentelemetry-instrumentation-rack (~> 0.24.1) opentelemetry-instrumentation-rack (~> 0.25.0)
opentelemetry-instrumentation-rails (~> 0.31.0) opentelemetry-instrumentation-rails (~> 0.32.0)
opentelemetry-instrumentation-redis (~> 0.25.3) opentelemetry-instrumentation-redis (~> 0.25.3)
opentelemetry-instrumentation-sidekiq (~> 0.25.2) opentelemetry-instrumentation-sidekiq (~> 0.25.2)
opentelemetry-sdk (~> 1.4) opentelemetry-sdk (~> 1.4)

View file

@ -36,9 +36,14 @@ class IpBlock < ApplicationRecord
class << self class << self
def blocked?(remote_ip) def blocked?(remote_ip)
blocked_ips_map = Rails.cache.fetch(CACHE_KEY) { FastIpMap.new(IpBlock.where(severity: :no_access).pluck(:ip)) }
blocked_ips_map.include?(remote_ip) blocked_ips_map.include?(remote_ip)
end end
private
def blocked_ips_map
Rails.cache.fetch(CACHE_KEY) { FastIpMap.new(severity_no_access.pluck(:ip)) }
end
end end
private private

View file

@ -3,6 +3,8 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe FeedManager do RSpec.describe FeedManager do
subject { described_class.instance }
before do |example| before do |example|
unless example.metadata[:skip_stub] unless example.metadata[:skip_stub]
stub_const 'FeedManager::MAX_ITEMS', 10 stub_const 'FeedManager::MAX_ITEMS', 10
@ -32,26 +34,26 @@ RSpec.describe FeedManager do
it 'returns false for followee\'s status' do it 'returns false for followee\'s status' do
status = Fabricate(:status, text: 'Hello world', account: alice) status = Fabricate(:status, text: 'Hello world', account: alice)
bob.follow!(alice) bob.follow!(alice)
expect(described_class.instance.filter?(:home, status, bob)).to be false expect(subject.filter?(:home, status, bob)).to be false
end end
it 'returns false for reblog by followee' do it 'returns false for reblog by followee' do
status = Fabricate(:status, text: 'Hello world', account: jeff) status = Fabricate(:status, text: 'Hello world', account: jeff)
reblog = Fabricate(:status, reblog: status, account: alice) reblog = Fabricate(:status, reblog: status, account: alice)
bob.follow!(alice) bob.follow!(alice)
expect(described_class.instance.filter?(:home, reblog, bob)).to be false expect(subject.filter?(:home, reblog, bob)).to be false
end end
it 'returns true for post from account who blocked me' do it 'returns true for post from account who blocked me' do
status = Fabricate(:status, text: 'Hello, World', account: alice) status = Fabricate(:status, text: 'Hello, World', account: alice)
alice.block!(bob) alice.block!(bob)
expect(described_class.instance.filter?(:home, status, bob)).to be true expect(subject.filter?(:home, status, bob)).to be true
end end
it 'returns true for post from blocked account' do it 'returns true for post from blocked account' do
status = Fabricate(:status, text: 'Hello, World', account: alice) status = Fabricate(:status, text: 'Hello, World', account: alice)
bob.block!(alice) bob.block!(alice)
expect(described_class.instance.filter?(:home, status, bob)).to be true expect(subject.filter?(:home, status, bob)).to be true
end end
it 'returns true for reblog by followee of blocked account' do it 'returns true for reblog by followee of blocked account' do
@ -59,7 +61,7 @@ RSpec.describe FeedManager do
reblog = Fabricate(:status, reblog: status, account: alice) reblog = Fabricate(:status, reblog: status, account: alice)
bob.follow!(alice) bob.follow!(alice)
bob.block!(jeff) bob.block!(jeff)
expect(described_class.instance.filter?(:home, reblog, bob)).to be true expect(subject.filter?(:home, reblog, bob)).to be true
end end
it 'returns true for reblog by followee of muted account' do it 'returns true for reblog by followee of muted account' do
@ -67,7 +69,7 @@ RSpec.describe FeedManager do
reblog = Fabricate(:status, reblog: status, account: alice) reblog = Fabricate(:status, reblog: status, account: alice)
bob.follow!(alice) bob.follow!(alice)
bob.mute!(jeff) bob.mute!(jeff)
expect(described_class.instance.filter?(:home, reblog, bob)).to be true expect(subject.filter?(:home, reblog, bob)).to be true
end end
it 'returns true for reblog by followee of someone who is blocking recipient' do it 'returns true for reblog by followee of someone who is blocking recipient' do
@ -75,14 +77,14 @@ RSpec.describe FeedManager do
reblog = Fabricate(:status, reblog: status, account: alice) reblog = Fabricate(:status, reblog: status, account: alice)
bob.follow!(alice) bob.follow!(alice)
jeff.block!(bob) jeff.block!(bob)
expect(described_class.instance.filter?(:home, reblog, bob)).to be true expect(subject.filter?(:home, reblog, bob)).to be true
end end
it 'returns true for reblog from account with reblogs disabled' do it 'returns true for reblog from account with reblogs disabled' do
status = Fabricate(:status, text: 'Hello world', account: jeff) status = Fabricate(:status, text: 'Hello world', account: jeff)
reblog = Fabricate(:status, reblog: status, account: alice) reblog = Fabricate(:status, reblog: status, account: alice)
bob.follow!(alice, reblogs: false) bob.follow!(alice, reblogs: false)
expect(described_class.instance.filter?(:home, reblog, bob)).to be true expect(subject.filter?(:home, reblog, bob)).to be true
end end
it 'returns false for reply by followee to another followee' do it 'returns false for reply by followee to another followee' do
@ -90,49 +92,49 @@ RSpec.describe FeedManager do
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice) reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
bob.follow!(alice) bob.follow!(alice)
bob.follow!(jeff) bob.follow!(jeff)
expect(described_class.instance.filter?(:home, reply, bob)).to be false expect(subject.filter?(:home, reply, bob)).to be false
end end
it 'returns false for reply by followee to recipient' do it 'returns false for reply by followee to recipient' do
status = Fabricate(:status, text: 'Hello world', account: bob) status = Fabricate(:status, text: 'Hello world', account: bob)
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice) reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
bob.follow!(alice) bob.follow!(alice)
expect(described_class.instance.filter?(:home, reply, bob)).to be false expect(subject.filter?(:home, reply, bob)).to be false
end end
it 'returns false for reply by followee to self' do it 'returns false for reply by followee to self' do
status = Fabricate(:status, text: 'Hello world', account: alice) status = Fabricate(:status, text: 'Hello world', account: alice)
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice) reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
bob.follow!(alice) bob.follow!(alice)
expect(described_class.instance.filter?(:home, reply, bob)).to be false expect(subject.filter?(:home, reply, bob)).to be false
end end
it 'returns true for reply by followee to non-followed account' do it 'returns true for reply by followee to non-followed account' do
status = Fabricate(:status, text: 'Hello world', account: jeff) status = Fabricate(:status, text: 'Hello world', account: jeff)
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice) reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
bob.follow!(alice) bob.follow!(alice)
expect(described_class.instance.filter?(:home, reply, bob)).to be true expect(subject.filter?(:home, reply, bob)).to be true
end end
it 'returns true for the second reply by followee to a non-federated status' do it 'returns true for the second reply by followee to a non-federated status' do
reply = Fabricate(:status, text: 'Reply 1', reply: true, account: alice) reply = Fabricate(:status, text: 'Reply 1', reply: true, account: alice)
second_reply = Fabricate(:status, text: 'Reply 2', thread: reply, account: alice) second_reply = Fabricate(:status, text: 'Reply 2', thread: reply, account: alice)
bob.follow!(alice) bob.follow!(alice)
expect(described_class.instance.filter?(:home, second_reply, bob)).to be true expect(subject.filter?(:home, second_reply, bob)).to be true
end end
it 'returns false for status by followee mentioning another account' do it 'returns false for status by followee mentioning another account' do
bob.follow!(alice) bob.follow!(alice)
jeff.follow!(alice) jeff.follow!(alice)
status = PostStatusService.new.call(alice, text: 'Hey @jeff') status = PostStatusService.new.call(alice, text: 'Hey @jeff')
expect(described_class.instance.filter?(:home, status, bob)).to be false expect(subject.filter?(:home, status, bob)).to be false
end end
it 'returns true for status by followee mentioning blocked account' do it 'returns true for status by followee mentioning blocked account' do
bob.block!(jeff) bob.block!(jeff)
bob.follow!(alice) bob.follow!(alice)
status = PostStatusService.new.call(alice, text: 'Hey @jeff') status = PostStatusService.new.call(alice, text: 'Hey @jeff')
expect(described_class.instance.filter?(:home, status, bob)).to be true expect(subject.filter?(:home, status, bob)).to be true
end end
it 'returns true for reblog of a personally blocked domain' do it 'returns true for reblog of a personally blocked domain' do
@ -140,19 +142,19 @@ RSpec.describe FeedManager do
alice.follow!(jeff) alice.follow!(jeff)
status = Fabricate(:status, text: 'Hello world', account: bob) status = Fabricate(:status, text: 'Hello world', account: bob)
reblog = Fabricate(:status, reblog: status, account: jeff) reblog = Fabricate(:status, reblog: status, account: jeff)
expect(described_class.instance.filter?(:home, reblog, alice)).to be true expect(subject.filter?(:home, reblog, alice)).to be true
end end
it 'returns true for German post when follow is set to English only' do it 'returns true for German post when follow is set to English only' do
alice.follow!(bob, languages: %w(en)) alice.follow!(bob, languages: %w(en))
status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de') status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de')
expect(described_class.instance.filter?(:home, status, alice)).to be true expect(subject.filter?(:home, status, alice)).to be true
end end
it 'returns false for German post when follow is set to German' do it 'returns false for German post when follow is set to German' do
alice.follow!(bob, languages: %w(de)) alice.follow!(bob, languages: %w(de))
status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de') status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de')
expect(described_class.instance.filter?(:home, status, alice)).to be false expect(subject.filter?(:home, status, alice)).to be false
end end
it 'returns true for post from followee on exclusive list' do it 'returns true for post from followee on exclusive list' do
@ -161,7 +163,7 @@ RSpec.describe FeedManager do
list.accounts << bob list.accounts << bob
allow(List).to receive(:where).and_return(list) allow(List).to receive(:where).and_return(list)
status = Fabricate(:status, text: 'I post a lot', account: bob) status = Fabricate(:status, text: 'I post a lot', account: bob)
expect(described_class.instance.filter?(:home, status, alice)).to be true expect(subject.filter?(:home, status, alice)).to be true
end end
it 'returns true for reblog from followee on exclusive list' do it 'returns true for reblog from followee on exclusive list' do
@ -171,7 +173,7 @@ RSpec.describe FeedManager do
allow(List).to receive(:where).and_return(list) allow(List).to receive(:where).and_return(list)
status = Fabricate(:status, text: 'I post a lot', account: bob) status = Fabricate(:status, text: 'I post a lot', account: bob)
reblog = Fabricate(:status, reblog: status, account: jeff) reblog = Fabricate(:status, reblog: status, account: jeff)
expect(described_class.instance.filter?(:home, reblog, alice)).to be true expect(subject.filter?(:home, reblog, alice)).to be true
end end
it 'returns false for post from followee on non-exclusive list' do it 'returns false for post from followee on non-exclusive list' do
@ -179,7 +181,7 @@ RSpec.describe FeedManager do
alice.follow!(bob) alice.follow!(bob)
list.accounts << bob list.accounts << bob
status = Fabricate(:status, text: 'I post a lot', account: bob) status = Fabricate(:status, text: 'I post a lot', account: bob)
expect(described_class.instance.filter?(:home, status, alice)).to be false expect(subject.filter?(:home, status, alice)).to be false
end end
it 'returns false for reblog from followee on non-exclusive list' do it 'returns false for reblog from followee on non-exclusive list' do
@ -188,7 +190,7 @@ RSpec.describe FeedManager do
list.accounts << jeff list.accounts << jeff
status = Fabricate(:status, text: 'I post a lot', account: bob) status = Fabricate(:status, text: 'I post a lot', account: bob)
reblog = Fabricate(:status, reblog: status, account: jeff) reblog = Fabricate(:status, reblog: status, account: jeff)
expect(described_class.instance.filter?(:home, reblog, alice)).to be false expect(subject.filter?(:home, reblog, alice)).to be false
end end
end end
@ -196,27 +198,27 @@ RSpec.describe FeedManager do
it 'returns true for status that mentions blocked account' do it 'returns true for status that mentions blocked account' do
bob.block!(jeff) bob.block!(jeff)
status = PostStatusService.new.call(alice, text: 'Hey @jeff') status = PostStatusService.new.call(alice, text: 'Hey @jeff')
expect(described_class.instance.filter?(:mentions, status, bob)).to be true expect(subject.filter?(:mentions, status, bob)).to be true
end end
it 'returns true for status that replies to a blocked account' do it 'returns true for status that replies to a blocked account' do
status = Fabricate(:status, text: 'Hello world', account: jeff) status = Fabricate(:status, text: 'Hello world', account: jeff)
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice) reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
bob.block!(jeff) bob.block!(jeff)
expect(described_class.instance.filter?(:mentions, reply, bob)).to be true expect(subject.filter?(:mentions, reply, bob)).to be true
end end
it 'returns false for status by limited 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) status = Fabricate(:status, text: 'Hello world', account: alice)
alice.silence! alice.silence!
expect(described_class.instance.filter?(:mentions, status, bob)).to be false expect(subject.filter?(:mentions, status, bob)).to be false
end end
it 'returns false for status by followed limited account' do it 'returns false for status by followed limited account' do
status = Fabricate(:status, text: 'Hello world', account: alice) status = Fabricate(:status, text: 'Hello world', account: alice)
alice.silence! alice.silence!
bob.follow!(alice) bob.follow!(alice)
expect(described_class.instance.filter?(:mentions, status, bob)).to be false expect(subject.filter?(:mentions, status, bob)).to be false
end end
end end
end end
@ -228,7 +230,7 @@ RSpec.describe FeedManager do
members = Array.new(described_class::MAX_ITEMS) { |count| [count, count] } members = Array.new(described_class::MAX_ITEMS) { |count| [count, count] }
redis.zadd("feed:home:#{account.id}", members) redis.zadd("feed:home:#{account.id}", members)
described_class.instance.push_to_home(account, status) subject.push_to_home(account, status)
expect(redis.zcard("feed:home:#{account.id}")).to eq described_class::MAX_ITEMS expect(redis.zcard("feed:home:#{account.id}")).to eq described_class::MAX_ITEMS
end end
@ -239,7 +241,7 @@ RSpec.describe FeedManager do
reblogged = Fabricate(:status) reblogged = Fabricate(:status)
reblog = Fabricate(:status, reblog: reblogged) reblog = Fabricate(:status, reblog: reblogged)
expect(described_class.instance.push_to_home(account, reblog)).to be true expect(subject.push_to_home(account, reblog)).to be true
end end
it 'does not save a new reblog of a recent status' do it 'does not save a new reblog of a recent status' do
@ -247,9 +249,9 @@ RSpec.describe FeedManager do
reblogged = Fabricate(:status) reblogged = Fabricate(:status)
reblog = Fabricate(:status, reblog: reblogged) reblog = Fabricate(:status, reblog: reblogged)
described_class.instance.push_to_home(account, reblogged) subject.push_to_home(account, reblogged)
expect(described_class.instance.push_to_home(account, reblog)).to be false expect(subject.push_to_home(account, reblog)).to be false
end end
it 'saves a new reblog of an old status' do it 'saves a new reblog of an old status' do
@ -257,14 +259,14 @@ RSpec.describe FeedManager do
reblogged = Fabricate(:status) reblogged = Fabricate(:status)
reblog = Fabricate(:status, reblog: reblogged) reblog = Fabricate(:status, reblog: reblogged)
described_class.instance.push_to_home(account, reblogged) subject.push_to_home(account, reblogged)
# Fill the feed with intervening statuses # Fill the feed with intervening statuses
described_class::REBLOG_FALLOFF.times do described_class::REBLOG_FALLOFF.times do
described_class.instance.push_to_home(account, Fabricate(:status)) subject.push_to_home(account, Fabricate(:status))
end end
expect(described_class.instance.push_to_home(account, reblog)).to be true expect(subject.push_to_home(account, reblog)).to be true
end end
it 'does not save a new reblog of a recently-reblogged status' do it 'does not save a new reblog of a recently-reblogged status' do
@ -273,10 +275,10 @@ RSpec.describe FeedManager do
reblogs = Array.new(2) { Fabricate(:status, reblog: reblogged) } reblogs = Array.new(2) { Fabricate(:status, reblog: reblogged) }
# The first reblog will be accepted # The first reblog will be accepted
described_class.instance.push_to_home(account, reblogs.first) subject.push_to_home(account, reblogs.first)
# The second reblog should be ignored # The second reblog should be ignored
expect(described_class.instance.push_to_home(account, reblogs.last)).to be false expect(subject.push_to_home(account, reblogs.last)).to be false
end end
it 'saves a new reblog of a recently-reblogged status when previous reblog has been deleted' do it 'saves a new reblog of a recently-reblogged status when previous reblog has been deleted' do
@ -285,15 +287,15 @@ RSpec.describe FeedManager do
old_reblog = Fabricate(:status, reblog: reblogged) old_reblog = Fabricate(:status, reblog: reblogged)
# The first reblog should be accepted # The first reblog should be accepted
expect(described_class.instance.push_to_home(account, old_reblog)).to be true expect(subject.push_to_home(account, old_reblog)).to be true
# The first reblog should be successfully removed # The first reblog should be successfully removed
expect(described_class.instance.unpush_from_home(account, old_reblog)).to be true expect(subject.unpush_from_home(account, old_reblog)).to be true
reblog = Fabricate(:status, reblog: reblogged) reblog = Fabricate(:status, reblog: reblogged)
# The second reblog should be accepted # The second reblog should be accepted
expect(described_class.instance.push_to_home(account, reblog)).to be true expect(subject.push_to_home(account, reblog)).to be true
end end
it 'does not save a new reblog of a multiply-reblogged-then-unreblogged status' do it 'does not save a new reblog of a multiply-reblogged-then-unreblogged status' do
@ -302,14 +304,14 @@ RSpec.describe FeedManager do
reblogs = Array.new(3) { Fabricate(:status, reblog: reblogged) } reblogs = Array.new(3) { Fabricate(:status, reblog: reblogged) }
# Accept the reblogs # Accept the reblogs
described_class.instance.push_to_home(account, reblogs[0]) subject.push_to_home(account, reblogs[0])
described_class.instance.push_to_home(account, reblogs[1]) subject.push_to_home(account, reblogs[1])
# Unreblog the first one # Unreblog the first one
described_class.instance.unpush_from_home(account, reblogs[0]) subject.unpush_from_home(account, reblogs[0])
# The last reblog should still be ignored # The last reblog should still be ignored
expect(described_class.instance.push_to_home(account, reblogs.last)).to be false expect(subject.push_to_home(account, reblogs.last)).to be false
end end
it 'saves a new reblog of a long-ago-reblogged status' do it 'saves a new reblog of a long-ago-reblogged status' do
@ -318,15 +320,15 @@ RSpec.describe FeedManager do
reblogs = Array.new(2) { Fabricate(:status, reblog: reblogged) } reblogs = Array.new(2) { Fabricate(:status, reblog: reblogged) }
# The first reblog will be accepted # The first reblog will be accepted
described_class.instance.push_to_home(account, reblogs.first) subject.push_to_home(account, reblogs.first)
# Fill the feed with intervening statuses # Fill the feed with intervening statuses
described_class::REBLOG_FALLOFF.times do described_class::REBLOG_FALLOFF.times do
described_class.instance.push_to_home(account, Fabricate(:status)) subject.push_to_home(account, Fabricate(:status))
end end
# The second reblog should also be accepted # The second reblog should also be accepted
expect(described_class.instance.push_to_home(account, reblogs.last)).to be true expect(subject.push_to_home(account, reblogs.last)).to be true
end end
end end
@ -334,9 +336,9 @@ RSpec.describe FeedManager do
account = Fabricate(:account) account = Fabricate(:account)
reblog = Fabricate(:status) reblog = Fabricate(:status)
status = Fabricate(:status, reblog: reblog) status = Fabricate(:status, reblog: reblog)
described_class.instance.push_to_home(account, status) subject.push_to_home(account, status)
expect(described_class.instance.push_to_home(account, reblog)).to be false expect(subject.push_to_home(account, reblog)).to be false
end end
end end
@ -359,9 +361,9 @@ RSpec.describe FeedManager do
it "does not push when the given status's reblog is already inserted" do it "does not push when the given status's reblog is already inserted" do
reblog = Fabricate(:status) reblog = Fabricate(:status)
status = Fabricate(:status, reblog: reblog) status = Fabricate(:status, reblog: reblog)
described_class.instance.push_to_list(list, status) subject.push_to_list(list, status)
expect(described_class.instance.push_to_list(list, reblog)).to be false expect(subject.push_to_list(list, reblog)).to be false
end end
context 'when replies policy is set to no replies' do context 'when replies policy is set to no replies' do
@ -371,19 +373,19 @@ RSpec.describe FeedManager do
it 'pushes statuses that are not replies' do it 'pushes statuses that are not replies' do
status = Fabricate(:status, text: 'Hello world', account: bob) status = Fabricate(:status, text: 'Hello world', account: bob)
expect(described_class.instance.push_to_list(list, status)).to be true expect(subject.push_to_list(list, status)).to be true
end end
it 'pushes statuses that are replies to list owner' do it 'pushes statuses that are replies to list owner' do
status = Fabricate(:status, text: 'Hello world', account: owner) status = Fabricate(:status, text: 'Hello world', account: owner)
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob) reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
expect(described_class.instance.push_to_list(list, reply)).to be true expect(subject.push_to_list(list, reply)).to be true
end end
it 'does not push replies to another member of the list' do it 'does not push replies to another member of the list' do
status = Fabricate(:status, text: 'Hello world', account: alice) status = Fabricate(:status, text: 'Hello world', account: alice)
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob) reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
expect(described_class.instance.push_to_list(list, reply)).to be false expect(subject.push_to_list(list, reply)).to be false
end end
end end
@ -394,25 +396,25 @@ RSpec.describe FeedManager do
it 'pushes statuses that are not replies' do it 'pushes statuses that are not replies' do
status = Fabricate(:status, text: 'Hello world', account: bob) status = Fabricate(:status, text: 'Hello world', account: bob)
expect(described_class.instance.push_to_list(list, status)).to be true expect(subject.push_to_list(list, status)).to be true
end end
it 'pushes statuses that are replies to list owner' do it 'pushes statuses that are replies to list owner' do
status = Fabricate(:status, text: 'Hello world', account: owner) status = Fabricate(:status, text: 'Hello world', account: owner)
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob) reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
expect(described_class.instance.push_to_list(list, reply)).to be true expect(subject.push_to_list(list, reply)).to be true
end end
it 'pushes replies to another member of the list' do it 'pushes replies to another member of the list' do
status = Fabricate(:status, text: 'Hello world', account: alice) status = Fabricate(:status, text: 'Hello world', account: alice)
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob) reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
expect(described_class.instance.push_to_list(list, reply)).to be true expect(subject.push_to_list(list, reply)).to be true
end end
it 'does not push replies to someone not a member of the list' do it 'does not push replies to someone not a member of the list' do
status = Fabricate(:status, text: 'Hello world', account: eve) status = Fabricate(:status, text: 'Hello world', account: eve)
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob) reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
expect(described_class.instance.push_to_list(list, reply)).to be false expect(subject.push_to_list(list, reply)).to be false
end end
end end
@ -423,25 +425,25 @@ RSpec.describe FeedManager do
it 'pushes statuses that are not replies' do it 'pushes statuses that are not replies' do
status = Fabricate(:status, text: 'Hello world', account: bob) status = Fabricate(:status, text: 'Hello world', account: bob)
expect(described_class.instance.push_to_list(list, status)).to be true expect(subject.push_to_list(list, status)).to be true
end end
it 'pushes statuses that are replies to list owner' do it 'pushes statuses that are replies to list owner' do
status = Fabricate(:status, text: 'Hello world', account: owner) status = Fabricate(:status, text: 'Hello world', account: owner)
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob) reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
expect(described_class.instance.push_to_list(list, reply)).to be true expect(subject.push_to_list(list, reply)).to be true
end end
it 'pushes replies to another member of the list' do it 'pushes replies to another member of the list' do
status = Fabricate(:status, text: 'Hello world', account: alice) status = Fabricate(:status, text: 'Hello world', account: alice)
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob) reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
expect(described_class.instance.push_to_list(list, reply)).to be true expect(subject.push_to_list(list, reply)).to be true
end end
it 'pushes replies to someone not a member of the list' do it 'pushes replies to someone not a member of the list' do
status = Fabricate(:status, text: 'Hello world', account: eve) status = Fabricate(:status, text: 'Hello world', account: eve)
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob) reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
expect(described_class.instance.push_to_list(list, reply)).to be true expect(subject.push_to_list(list, reply)).to be true
end end
end end
end end
@ -451,9 +453,9 @@ RSpec.describe FeedManager do
account = Fabricate(:account, id: 0) account = Fabricate(:account, id: 0)
reblog = Fabricate(:status) reblog = Fabricate(:status)
status = Fabricate(:status, reblog: reblog) status = Fabricate(:status, reblog: reblog)
described_class.instance.push_to_home(account, status) subject.push_to_home(account, status)
described_class.instance.merge_into_home(account, reblog.account) subject.merge_into_home(account, reblog.account)
expect(redis.zscore('feed:home:0', reblog.id)).to be_nil expect(redis.zscore('feed:home:0', reblog.id)).to be_nil
end end
@ -466,14 +468,14 @@ RSpec.describe FeedManager do
reblogged = Fabricate(:status) reblogged = Fabricate(:status)
status = Fabricate(:status, reblog: reblogged) status = Fabricate(:status, reblog: reblogged)
described_class.instance.push_to_home(receiver, reblogged) subject.push_to_home(receiver, reblogged)
described_class::REBLOG_FALLOFF.times { described_class.instance.push_to_home(receiver, Fabricate(:status)) } described_class::REBLOG_FALLOFF.times { subject.push_to_home(receiver, Fabricate(:status)) }
described_class.instance.push_to_home(receiver, status) subject.push_to_home(receiver, status)
# The reblogging status should show up under normal conditions. # The reblogging status should show up under normal conditions.
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s) expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s)
described_class.instance.unpush_from_home(receiver, status) subject.unpush_from_home(receiver, status)
# Restore original status # Restore original status
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to_not include(status.id.to_s) expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to_not include(status.id.to_s)
@ -484,12 +486,12 @@ RSpec.describe FeedManager do
reblogged = Fabricate(:status) reblogged = Fabricate(:status)
status = Fabricate(:status, reblog: reblogged) status = Fabricate(:status, reblog: reblogged)
described_class.instance.push_to_home(receiver, status) subject.push_to_home(receiver, status)
# The reblogging status should show up under normal conditions. # The reblogging status should show up under normal conditions.
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [status.id.to_s] expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [status.id.to_s]
described_class.instance.unpush_from_home(receiver, status) subject.unpush_from_home(receiver, status)
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to be_empty expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to be_empty
end end
@ -499,14 +501,14 @@ RSpec.describe FeedManager do
reblogs = Array.new(3) { Fabricate(:status, reblog: reblogged) } reblogs = Array.new(3) { Fabricate(:status, reblog: reblogged) }
reblogs.each do |reblog| reblogs.each do |reblog|
described_class.instance.push_to_home(receiver, reblog) subject.push_to_home(receiver, reblog)
end end
# The reblogging status should show up under normal conditions. # The reblogging status should show up under normal conditions.
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [reblogs.first.id.to_s] expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [reblogs.first.id.to_s]
reblogs[0...-1].each do |reblog| reblogs[0...-1].each do |reblog|
described_class.instance.unpush_from_home(receiver, reblog) subject.unpush_from_home(receiver, reblog)
end end
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [reblogs.last.id.to_s] expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [reblogs.last.id.to_s]
@ -515,10 +517,10 @@ RSpec.describe FeedManager do
it 'sends push updates' do it 'sends push updates' do
status = Fabricate(:status) status = Fabricate(:status)
described_class.instance.push_to_home(receiver, status) subject.push_to_home(receiver, status)
allow(redis).to receive_messages(publish: nil) allow(redis).to receive_messages(publish: nil)
described_class.instance.unpush_from_home(receiver, status) subject.unpush_from_home(receiver, status)
deletion = Oj.dump(event: :delete, payload: status.id.to_s) deletion = Oj.dump(event: :delete, payload: status.id.to_s)
expect(redis).to have_received(:publish).with("timeline:#{receiver.id}", deletion) expect(redis).to have_received(:publish).with("timeline:#{receiver.id}", deletion)
@ -532,9 +534,9 @@ RSpec.describe FeedManager do
it 'leaves a tagged status' do it 'leaves a tagged status' do
status = Fabricate(:status) status = Fabricate(:status)
status.tags << tag status.tags << tag
described_class.instance.push_to_home(receiver, status) subject.push_to_home(receiver, status)
described_class.instance.unmerge_tag_from_home(tag, receiver) subject.unmerge_tag_from_home(tag, receiver)
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to_not include(status.id.to_s) expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to_not include(status.id.to_s)
end end
@ -545,9 +547,9 @@ RSpec.describe FeedManager do
status = Fabricate(:status, account: followee) status = Fabricate(:status, account: followee)
status.tags << tag status.tags << tag
described_class.instance.push_to_home(receiver, status) subject.push_to_home(receiver, status)
described_class.instance.unmerge_tag_from_home(tag, receiver) subject.unmerge_tag_from_home(tag, receiver)
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s) expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s)
end end
@ -555,9 +557,9 @@ RSpec.describe FeedManager do
it 'remains a tagged status written by receiver' do it 'remains a tagged status written by receiver' do
status = Fabricate(:status, account: receiver) status = Fabricate(:status, account: receiver)
status.tags << tag status.tags << tag
described_class.instance.push_to_home(receiver, status) subject.push_to_home(receiver, status)
described_class.instance.unmerge_tag_from_home(tag, receiver) subject.unmerge_tag_from_home(tag, receiver)
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s) expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s)
end end
@ -588,7 +590,7 @@ RSpec.describe FeedManager do
end end
it 'correctly cleans the home timeline' do it 'correctly cleans the home timeline' do
described_class.instance.clear_from_home(account, target_account) subject.clear_from_home(account, target_account)
expect(redis.zrange("feed:home:#{account.id}", 0, -1)).to eq [status_from_followed_account_first.id.to_s, status_from_followed_account_next.id.to_s] expect(redis.zrange("feed:home:#{account.id}", 0, -1)).to eq [status_from_followed_account_first.id.to_s, status_from_followed_account_next.id.to_s]
end end

View file

@ -9,8 +9,8 @@ RSpec.describe AccountMigration do
end end
end end
describe 'validations' do describe 'Validations' do
subject { described_class.new(account: source_account, acct: target_acct) } subject { Fabricate.build :account_migration, account: source_account }
let(:source_account) { Fabricate(:account) } let(:source_account) { Fabricate(:account) }
let(:target_acct) { target_account.acct } let(:target_acct) { target_account.acct }
@ -26,9 +26,7 @@ RSpec.describe AccountMigration do
allow(service_double).to receive(:call).with(target_acct, anything).and_return(target_account) allow(service_double).to receive(:call).with(target_acct, anything).and_return(target_account)
end end
it 'passes validations' do it { is_expected.to allow_value(target_account.acct).for(:acct) }
expect(subject).to be_valid
end
end end
context 'with unresolvable account' do context 'with unresolvable account' do
@ -40,17 +38,13 @@ RSpec.describe AccountMigration do
allow(service_double).to receive(:call).with(target_acct, anything).and_return(nil) allow(service_double).to receive(:call).with(target_acct, anything).and_return(nil)
end end
it 'has errors on acct field' do it { is_expected.to_not allow_value(target_acct).for(:acct) }
expect(subject).to model_have_error_on_field(:acct)
end
end end
context 'with a space in the domain part' do context 'with a space in the domain part' do
let(:target_acct) { 'target@remote. org' } let(:target_acct) { 'target@remote. org' }
it 'has errors on acct field' do it { is_expected.to_not allow_value(target_acct).for(:acct) }
expect(subject).to model_have_error_on_field(:acct)
end
end end
end end
end end

View file

@ -3,29 +3,24 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe AccountModerationNote do RSpec.describe AccountModerationNote do
describe 'chronological scope' do describe 'Scopes' do
it 'returns account moderation notes oldest to newest' do describe '.chronological' do
account = Fabricate(:account) it 'returns account moderation notes oldest to newest' do
note1 = Fabricate(:account_moderation_note, target_account: account) account = Fabricate(:account)
note2 = Fabricate(:account_moderation_note, target_account: account) note1 = Fabricate(:account_moderation_note, target_account: account)
note2 = Fabricate(:account_moderation_note, target_account: account)
expect(account.targeted_moderation_notes.chronological).to eq [note1, note2] expect(account.targeted_moderation_notes.chronological).to eq [note1, note2]
end
end end
end end
describe 'validations' do describe 'Validations' do
it 'is invalid if the content is empty' do subject { Fabricate.build :account_moderation_note }
report = Fabricate.build(:account_moderation_note, content: '')
expect(report.valid?).to be false
end
it 'is invalid if content is longer than character limit' do describe 'content' do
report = Fabricate.build(:account_moderation_note, content: comment_over_limit) it { is_expected.to_not allow_value('').for(:content) }
expect(report.valid?).to be false it { is_expected.to validate_length_of(:content).is_at_most(described_class::CONTENT_SIZE_LIMIT) }
end
def comment_over_limit
Faker::Lorem.paragraph_by_chars(number: described_class::CONTENT_SIZE_LIMIT * 2)
end end
end end
end end

View file

@ -10,64 +10,6 @@ RSpec.describe Account do
let(:bob) { Fabricate(:account, username: 'bob') } let(:bob) { Fabricate(:account, username: 'bob') }
describe '#suspended_locally?' do
context 'when the account is not suspended' do
it 'returns false' do
expect(subject.suspended_locally?).to be false
end
end
context 'when the account is suspended locally' do
before do
subject.update!(suspended_at: 1.day.ago, suspension_origin: :local)
end
it 'returns true' do
expect(subject.suspended_locally?).to be true
end
end
context 'when the account is suspended remotely' do
before do
subject.update!(suspended_at: 1.day.ago, suspension_origin: :remote)
end
it 'returns false' do
expect(subject.suspended_locally?).to be false
end
end
end
describe '#suspend!' do
it 'marks the account as suspended and creates a deletion request' do
expect { subject.suspend! }
.to change(subject, :suspended?).from(false).to(true)
.and change(subject, :suspended_locally?).from(false).to(true)
.and(change { AccountDeletionRequest.exists?(account: subject) }.from(false).to(true))
end
context 'when the account is of a local user' do
subject { local_user_account }
let!(:local_user_account) { Fabricate(:user, email: 'foo+bar@domain.org').account }
it 'creates a canonical domain block' do
subject.suspend!
expect(CanonicalEmailBlock.block?(subject.user_email)).to be true
end
context 'when a canonical domain block already exists for that email' do
before do
Fabricate(:canonical_email_block, email: subject.user_email)
end
it 'does not raise an error' do
expect { subject.suspend! }.to_not raise_error
end
end
end
end
describe '#follow!' do describe '#follow!' do
it 'creates a follow' do it 'creates a follow' do
follow = subject.follow!(bob) follow = subject.follow!(bob)
@ -752,26 +694,42 @@ RSpec.describe Account do
end end
end end
describe '#prepare_contents' do describe 'Callbacks' do
subject { Fabricate.build :account, domain: domain, note: ' padded note ', display_name: ' padded name ' } describe 'Stripping content when required' do
context 'with a remote account' do
subject { Fabricate.build :account, domain: 'host.example', note: ' note ', display_name: ' display name ' }
context 'with local account' do it 'preserves content' do
let(:domain) { nil } expect { subject.valid? }
.to not_change(subject, :note)
it 'strips values' do .and not_change(subject, :display_name)
expect { subject.valid? } end
.to change(subject, :note).to('padded note')
.and(change(subject, :display_name).to('padded name'))
end end
end
context 'with remote account' do context 'with a local account' do
let(:domain) { 'host.example' } subject { Fabricate.build :account, domain: nil, note:, display_name: }
it 'preserves values' do context 'with populated fields' do
expect { subject.valid? } let(:note) { ' note ' }
.to not_change(subject, :note) let(:display_name) { ' display name ' }
.and(not_change(subject, :display_name))
it 'strips content' do
expect { subject.valid? }
.to change(subject, :note).to('note')
.and change(subject, :display_name).to('display name')
end
end
context 'with empty fields' do
let(:note) { nil }
let(:display_name) { nil }
it 'preserves content' do
expect { subject.valid? }
.to not_change(subject, :note)
.and not_change(subject, :display_name)
end
end
end end
end end
end end
@ -826,22 +784,19 @@ RSpec.describe Account do
end end
end end
describe 'validations' do describe 'Validations' do
it { is_expected.to validate_presence_of(:username) } it { is_expected.to validate_presence_of(:username) }
context 'when is local' do context 'when account is local' do
it 'is invalid if the username is not unique in case-insensitive comparison among local accounts' do subject { Fabricate.build :account, domain: nil }
_account = Fabricate(:account, username: 'the_doctor')
non_unique_account = Fabricate.build(:account, username: 'the_Doctor') context 'with an existing differently-cased username account' do
non_unique_account.valid? before { Fabricate :account, username: 'the_doctor' }
expect(non_unique_account).to model_have_error_on_field(:username)
it { is_expected.to_not allow_value('the_Doctor').for(:username) }
end end
it 'is invalid if the username is reserved' do it { is_expected.to_not allow_value('support').for(:username) }
account = Fabricate.build(:account, username: 'support')
account.valid?
expect(account).to model_have_error_on_field(:username)
end
it 'is valid when username is reserved but record has already been created' do it 'is valid when username is reserved but record has already been created' do
account = Fabricate.build(:account, username: 'support') account = Fabricate.build(:account, username: 'support')
@ -849,9 +804,10 @@ RSpec.describe Account do
expect(account.valid?).to be true expect(account.valid?).to be true
end end
it 'is valid if we are creating an instance actor account with a period' do context 'with the instance actor' do
account = Fabricate.build(:account, id: described_class::INSTANCE_ACTOR_ID, actor_type: 'Application', locked: true, username: 'example.com') subject { Fabricate.build :account, id: described_class::INSTANCE_ACTOR_ID, actor_type: 'Application', locked: true }
expect(account.valid?).to be true
it { is_expected.to allow_value('example.com').for(:username) }
end end
it 'is valid if we are creating a possibly-conflicting instance actor account' do it 'is valid if we are creating a possibly-conflicting instance actor account' do
@ -860,81 +816,31 @@ RSpec.describe Account do
expect(instance_account.valid?).to be true expect(instance_account.valid?).to be true
end end
it 'is invalid if the username doesn\'t only contains letters, numbers and underscores' do it { is_expected.to_not allow_values('the-doctor', 'the.doctor').for(:username) }
account = Fabricate.build(:account, username: 'the-doctor')
account.valid?
expect(account).to model_have_error_on_field(:username)
end
it 'is invalid if the username contains a period' do it { is_expected.to validate_length_of(:username).is_at_most(described_class::USERNAME_LENGTH_LIMIT) }
account = Fabricate.build(:account, username: 'the.doctor') it { is_expected.to validate_length_of(:display_name).is_at_most(described_class::DISPLAY_NAME_LENGTH_LIMIT) }
account.valid?
expect(account).to model_have_error_on_field(:username)
end
it 'is invalid if the username is longer than the character limit' do it { is_expected.to_not allow_values(account_note_over_limit).for(:note) }
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 the character limit' do
account = Fabricate.build(:account, display_name: display_name_over_limit)
account.valid?
expect(account).to model_have_error_on_field(:display_name)
end
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
end end
context 'when is remote' do context 'when account is remote' do
it 'is invalid if the username is same among accounts in the same normalized domain' do subject { Fabricate.build :account, domain: 'host.example' }
Fabricate(:account, domain: 'にゃん', username: 'username')
account = Fabricate.build(:account, domain: 'xn--r9j5b5b', username: 'username') context 'when a normalized domain account exists' do
account.valid? subject { Fabricate.build :account, domain: 'xn--r9j5b5b' }
expect(account).to model_have_error_on_field(:username)
before { Fabricate(:account, domain: 'にゃん', username: 'username') }
it { is_expected.to_not allow_values('username', 'Username').for(:username) }
end end
it 'is invalid if the username is not unique in case-insensitive comparison among accounts in the same normalized domain' do it { is_expected.to allow_values('the-doctor', username_over_limit).for(:username) }
Fabricate(:account, domain: 'にゃん', username: 'username') it { is_expected.to_not allow_values('the doctor').for(:username) }
account = Fabricate.build(:account, domain: 'xn--r9j5b5b', username: 'Username')
account.valid?
expect(account).to model_have_error_on_field(:username)
end
it 'is valid even if the username contains hyphens' do it { is_expected.to allow_values(display_name_over_limit).for(:display_name) }
account = Fabricate.build(:account, domain: 'domain', username: 'the-doctor')
account.valid?
expect(account).to_not model_have_error_on_field(:username)
end
it 'is invalid if the username doesn\'t only contains letters, numbers, underscores and hyphens' do it { is_expected.to allow_values(account_note_over_limit).for(:note) }
account = Fabricate.build(:account, domain: 'domain', username: 'the doctor')
account.valid?
expect(account).to model_have_error_on_field(:username)
end
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 the character limit' do
account = Fabricate.build(:account, domain: 'domain', display_name: display_name_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 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 end
def username_over_limit def username_over_limit
@ -1085,14 +991,6 @@ RSpec.describe Account do
end end
end end
describe 'suspended' do
it 'returns an array of accounts who are suspended' do
suspended_account = Fabricate(:account, suspended: true)
_account = Fabricate(:account, suspended: false)
expect(described_class.suspended).to contain_exactly(suspended_account)
end
end
describe 'searchable' do describe 'searchable' do
let!(:suspended_local) { Fabricate(:account, suspended: true, username: 'suspended_local') } let!(:suspended_local) { Fabricate(:account, suspended: true, username: 'suspended_local') }
let!(:suspended_remote) { Fabricate(:account, suspended: true, domain: 'example.org', username: 'suspended_remote') } let!(:suspended_remote) { Fabricate(:account, suspended: true, domain: 'example.org', username: 'suspended_remote') }

View file

@ -5,13 +5,12 @@ require 'rails_helper'
RSpec.describe AccountStatusesCleanupPolicy do RSpec.describe AccountStatusesCleanupPolicy do
let(:account) { Fabricate(:account, username: 'alice', domain: nil) } let(:account) { Fabricate(:account, username: 'alice', domain: nil) }
describe 'validation' do describe 'Validations' do
it 'disallow remote accounts' do subject { Fabricate.build :account_statuses_cleanup_policy }
account.update(domain: 'example.com')
account_statuses_cleanup_policy = Fabricate.build(:account_statuses_cleanup_policy, account: account) let(:remote_account) { Fabricate(:account, domain: 'example.com') }
account_statuses_cleanup_policy.valid?
expect(account_statuses_cleanup_policy).to model_have_error_on_field(:account) it { is_expected.to_not allow_value(remote_account).for(:account) }
end
end end
describe 'save hooks' do describe 'save hooks' do
@ -339,14 +338,7 @@ RSpec.describe AccountStatusesCleanupPolicy do
end end
context 'when policy is set to keep DMs and reject everything else' do context 'when policy is set to keep DMs and reject everything else' do
before do before { establish_policy(keep_direct: true) }
account_statuses_cleanup_policy.keep_direct = true
account_statuses_cleanup_policy.keep_pinned = false
account_statuses_cleanup_policy.keep_polls = false
account_statuses_cleanup_policy.keep_media = false
account_statuses_cleanup_policy.keep_self_fav = false
account_statuses_cleanup_policy.keep_self_bookmark = false
end
it 'returns every old status except does not return the old direct message for deletion' do it 'returns every old status except does not return the old direct message for deletion' do
expect(subject.pluck(:id)) expect(subject.pluck(:id))
@ -356,14 +348,7 @@ RSpec.describe AccountStatusesCleanupPolicy do
end end
context 'when policy is set to keep self-bookmarked toots and reject everything else' do context 'when policy is set to keep self-bookmarked toots and reject everything else' do
before do before { establish_policy(keep_self_bookmark: true) }
account_statuses_cleanup_policy.keep_direct = false
account_statuses_cleanup_policy.keep_pinned = false
account_statuses_cleanup_policy.keep_polls = false
account_statuses_cleanup_policy.keep_media = false
account_statuses_cleanup_policy.keep_self_fav = false
account_statuses_cleanup_policy.keep_self_bookmark = true
end
it 'returns every old status but does not return the old self-bookmarked message for deletion' do it 'returns every old status but does not return the old self-bookmarked message for deletion' do
expect(subject.pluck(:id)) expect(subject.pluck(:id))
@ -373,14 +358,7 @@ RSpec.describe AccountStatusesCleanupPolicy do
end end
context 'when policy is set to keep self-faved toots and reject everything else' do context 'when policy is set to keep self-faved toots and reject everything else' do
before do before { establish_policy(keep_self_fav: true) }
account_statuses_cleanup_policy.keep_direct = false
account_statuses_cleanup_policy.keep_pinned = false
account_statuses_cleanup_policy.keep_polls = false
account_statuses_cleanup_policy.keep_media = false
account_statuses_cleanup_policy.keep_self_fav = true
account_statuses_cleanup_policy.keep_self_bookmark = false
end
it 'returns every old status but does not return the old self-faved message for deletion' do it 'returns every old status but does not return the old self-faved message for deletion' do
expect(subject.pluck(:id)) expect(subject.pluck(:id))
@ -390,14 +368,7 @@ RSpec.describe AccountStatusesCleanupPolicy do
end end
context 'when policy is set to keep toots with media and reject everything else' do context 'when policy is set to keep toots with media and reject everything else' do
before do before { establish_policy(keep_media: true) }
account_statuses_cleanup_policy.keep_direct = false
account_statuses_cleanup_policy.keep_pinned = false
account_statuses_cleanup_policy.keep_polls = false
account_statuses_cleanup_policy.keep_media = true
account_statuses_cleanup_policy.keep_self_fav = false
account_statuses_cleanup_policy.keep_self_bookmark = false
end
it 'returns every old status but does not return the old message with media for deletion' do it 'returns every old status but does not return the old message with media for deletion' do
expect(subject.pluck(:id)) expect(subject.pluck(:id))
@ -407,14 +378,7 @@ RSpec.describe AccountStatusesCleanupPolicy do
end end
context 'when policy is set to keep toots with polls and reject everything else' do context 'when policy is set to keep toots with polls and reject everything else' do
before do before { establish_policy(keep_polls: true) }
account_statuses_cleanup_policy.keep_direct = false
account_statuses_cleanup_policy.keep_pinned = false
account_statuses_cleanup_policy.keep_polls = true
account_statuses_cleanup_policy.keep_media = false
account_statuses_cleanup_policy.keep_self_fav = false
account_statuses_cleanup_policy.keep_self_bookmark = false
end
it 'returns every old status but does not return the old poll message for deletion' do it 'returns every old status but does not return the old poll message for deletion' do
expect(subject.pluck(:id)) expect(subject.pluck(:id))
@ -424,14 +388,7 @@ RSpec.describe AccountStatusesCleanupPolicy do
end end
context 'when policy is set to keep pinned toots and reject everything else' do context 'when policy is set to keep pinned toots and reject everything else' do
before do before { establish_policy(keep_pinned: true) }
account_statuses_cleanup_policy.keep_direct = false
account_statuses_cleanup_policy.keep_pinned = true
account_statuses_cleanup_policy.keep_polls = false
account_statuses_cleanup_policy.keep_media = false
account_statuses_cleanup_policy.keep_self_fav = false
account_statuses_cleanup_policy.keep_self_bookmark = false
end
it 'returns every old status but does not return the old pinned message for deletion' do it 'returns every old status but does not return the old pinned message for deletion' do
expect(subject.pluck(:id)) expect(subject.pluck(:id))
@ -441,14 +398,7 @@ RSpec.describe AccountStatusesCleanupPolicy do
end end
context 'when policy is to not keep any special messages' do context 'when policy is to not keep any special messages' do
before do before { establish_policy }
account_statuses_cleanup_policy.keep_direct = false
account_statuses_cleanup_policy.keep_pinned = false
account_statuses_cleanup_policy.keep_polls = false
account_statuses_cleanup_policy.keep_media = false
account_statuses_cleanup_policy.keep_self_fav = false
account_statuses_cleanup_policy.keep_self_bookmark = false
end
it 'returns every old status but does not return the recent or unrelated statuses' do it 'returns every old status but does not return the recent or unrelated statuses' do
expect(subject.pluck(:id)) expect(subject.pluck(:id))
@ -459,14 +409,7 @@ RSpec.describe AccountStatusesCleanupPolicy do
end end
context 'when policy is set to keep every category of toots' do context 'when policy is set to keep every category of toots' do
before do before { establish_policy(keep_direct: true, keep_pinned: true, keep_polls: true, keep_media: true, keep_self_fav: true, keep_self_bookmark: true) }
account_statuses_cleanup_policy.keep_direct = true
account_statuses_cleanup_policy.keep_pinned = true
account_statuses_cleanup_policy.keep_polls = true
account_statuses_cleanup_policy.keep_media = true
account_statuses_cleanup_policy.keep_self_fav = true
account_statuses_cleanup_policy.keep_self_bookmark = true
end
it 'returns normal statuses and does not return unrelated old status' do it 'returns normal statuses and does not return unrelated old status' do
expect(subject.pluck(:id)) expect(subject.pluck(:id))
@ -502,5 +445,24 @@ RSpec.describe AccountStatusesCleanupPolicy do
.and include(very_old_status.id, faved_primary.id, reblogged_primary.id, reblogged_secondary.id) .and include(very_old_status.id, faved_primary.id, reblogged_primary.id, reblogged_secondary.id)
end end
end end
private
def establish_policy(options = {})
default_policy_options.merge(options).each do |attribute, value|
account_statuses_cleanup_policy.send :"#{attribute}=", value
end
end
def default_policy_options
{
keep_direct: false,
keep_media: false,
keep_pinned: false,
keep_polls: false,
keep_self_bookmark: false,
keep_self_fav: false,
}
end
end end
end end

View file

@ -0,0 +1,65 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Account::Suspensions do
subject { Fabricate(:account) }
describe '.suspended' do
let!(:suspended_account) { Fabricate :account, suspended: true }
before { Fabricate :account, suspended: false }
it 'returns accounts that are suspended' do
expect(Account.suspended)
.to contain_exactly(suspended_account)
end
end
describe '#suspended_locally?' do
context 'when the account is not suspended' do
it { is_expected.to_not be_suspended_locally }
end
context 'when the account is suspended locally' do
before { subject.update!(suspended_at: 1.day.ago, suspension_origin: :local) }
it { is_expected.to be_suspended_locally }
end
context 'when the account is suspended remotely' do
before { subject.update!(suspended_at: 1.day.ago, suspension_origin: :remote) }
it { is_expected.to_not be_suspended_locally }
end
end
describe '#suspend!' do
it 'marks the account as suspended and creates a deletion request' do
expect { subject.suspend! }
.to change(subject, :suspended?).from(false).to(true)
.and change(subject, :suspended_locally?).from(false).to(true)
.and(change { AccountDeletionRequest.exists?(account: subject) }.from(false).to(true))
end
context 'when the account is of a local user' do
subject { local_user_account }
let!(:local_user_account) { Fabricate(:user, email: 'foo+bar@domain.org').account }
it 'creates a canonical domain block' do
expect { subject.suspend! }
.to change { CanonicalEmailBlock.block?(subject.user_email) }.from(false).to(true)
end
context 'when a canonical domain block already exists for that email' do
before { Fabricate(:canonical_email_block, email: subject.user_email) }
it 'does not raise an error' do
expect { subject.suspend! }
.to_not raise_error
end
end
end
end
end

View file

@ -6,11 +6,10 @@ RSpec.describe DomainAllow do
describe 'Validations' do describe 'Validations' do
it { is_expected.to validate_presence_of(:domain) } it { is_expected.to validate_presence_of(:domain) }
it 'is invalid if the same normalized domain already exists' do context 'when a normalized domain exists' do
_domain_allow = Fabricate(:domain_allow, domain: 'にゃん') before { Fabricate(:domain_allow, domain: 'にゃん') }
domain_allow_with_normalized_value = Fabricate.build(:domain_allow, domain: 'xn--r9j5b5b')
domain_allow_with_normalized_value.valid? it { is_expected.to_not allow_value('xn--r9j5b5b').for(:domain) }
expect(domain_allow_with_normalized_value).to model_have_error_on_field(:domain)
end end
end end
end end

View file

@ -3,33 +3,17 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe Form::AdminSettings do RSpec.describe Form::AdminSettings do
describe 'validations' do describe 'Validations' do
describe 'site_contact_username' do describe 'site_contact_username' do
context 'with no accounts' do context 'with no accounts' do
it 'is not valid' do it { is_expected.to_not allow_value('Test').for(:site_contact_username) }
setting = described_class.new(site_contact_username: 'Test')
setting.valid?
expect(setting).to model_have_error_on_field(:site_contact_username)
end
end end
context 'with an account' do context 'with an account' do
before { Fabricate(:account, username: 'Glorp') } before { Fabricate(:account, username: 'Glorp') }
it 'is not valid when account doesnt match' do it { is_expected.to_not allow_value('Test').for(:site_contact_username) }
setting = described_class.new(site_contact_username: 'Test') it { is_expected.to allow_value('Glorp').for(:site_contact_username) }
setting.valid?
expect(setting).to model_have_error_on_field(:site_contact_username)
end
it 'is valid when account matches' do
setting = described_class.new(site_contact_username: 'Glorp')
setting.valid?
expect(setting).to_not model_have_error_on_field(:site_contact_username)
end
end end
end end
end end

View file

@ -3,18 +3,13 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe IpBlock do RSpec.describe IpBlock do
describe 'validations' do describe 'Validations' do
subject { Fabricate.build :ip_block }
it { is_expected.to validate_presence_of(:ip) } it { is_expected.to validate_presence_of(:ip) }
it { is_expected.to validate_presence_of(:severity) } it { is_expected.to validate_presence_of(:severity) }
it 'validates ip uniqueness', :aggregate_failures do it { is_expected.to validate_uniqueness_of(:ip) }
described_class.create!(ip: '127.0.0.1', severity: :no_access)
ip_block = described_class.new(ip: '127.0.0.1', severity: :no_access)
expect(ip_block).to_not be_valid
expect(ip_block).to model_have_error_on_field(:ip)
end
end end
describe '#to_log_human_identifier' do describe '#to_log_human_identifier' do

View file

@ -9,26 +9,10 @@ RSpec.describe PreviewCard do
end end
end end
describe 'validations' do describe 'Validations' do
describe 'urls' do describe 'url' do
it 'allows http schemes' do it { is_expected.to allow_values('http://example.host/path', 'https://example.host/path').for(:url) }
record = described_class.new(url: 'http://example.host/path') it { is_expected.to_not allow_value('javascript:alert()').for(:url) }
expect(record).to be_valid
end
it 'allows https schemes' do
record = described_class.new(url: 'https://example.host/path')
expect(record).to be_valid
end
it 'does not allow javascript: schemes' do
record = described_class.new(url: 'javascript:alert()')
expect(record).to_not be_valid
expect(record).to model_have_error_on_field(:url)
end
end end
end end
end end

View file

@ -3,29 +3,24 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe ReportNote do RSpec.describe ReportNote do
describe 'chronological scope' do describe 'Scopes' do
it 'returns report notes oldest to newest' do describe '.chronological' do
report = Fabricate(:report) it 'returns report notes oldest to newest' do
note1 = Fabricate(:report_note, report: report) report = Fabricate(:report)
note2 = Fabricate(:report_note, report: report) note1 = Fabricate(:report_note, report: report)
note2 = Fabricate(:report_note, report: report)
expect(report.notes.chronological).to eq [note1, note2] expect(report.notes.chronological).to eq [note1, note2]
end
end end
end end
describe 'validations' do describe 'Validations' do
it 'is invalid if the content is empty' do subject { Fabricate.build :report_note }
report = Fabricate.build(:report_note, content: '')
expect(report.valid?).to be false
end
it 'is invalid if content is longer than character limit' do describe 'content' do
report = Fabricate.build(:report_note, content: comment_over_limit) it { is_expected.to_not allow_value('').for(:content) }
expect(report.valid?).to be false it { is_expected.to validate_length_of(:content).is_at_most(described_class::CONTENT_SIZE_LIMIT) }
end
def comment_over_limit
Faker::Lorem.paragraph_by_chars(number: described_class::CONTENT_SIZE_LIMIT * 2)
end end
end end
end end

View file

@ -3,53 +3,17 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe WebauthnCredential do RSpec.describe WebauthnCredential do
describe 'validations' do describe 'Validations' do
subject { Fabricate.build :webauthn_credential }
it { is_expected.to validate_presence_of(:external_id) } it { is_expected.to validate_presence_of(:external_id) }
it { is_expected.to validate_presence_of(:public_key) } it { is_expected.to validate_presence_of(:public_key) }
it { is_expected.to validate_presence_of(:nickname) } it { is_expected.to validate_presence_of(:nickname) }
it { is_expected.to validate_presence_of(:sign_count) } it { is_expected.to validate_presence_of(:sign_count) }
it 'is invalid if already exist a webauthn credential with the same external id' do it { is_expected.to validate_uniqueness_of(:external_id) }
Fabricate(:webauthn_credential, external_id: '_Typ0ygudDnk9YUVWLQayw') it { is_expected.to validate_uniqueness_of(:nickname).scoped_to(:user_id) }
new_webauthn_credential = Fabricate.build(:webauthn_credential, external_id: '_Typ0ygudDnk9YUVWLQayw')
new_webauthn_credential.valid? it { is_expected.to validate_numericality_of(:sign_count).only_integer.is_greater_than_or_equal_to(0).is_less_than_or_equal_to(described_class::SIGN_COUNT_LIMIT - 1) }
expect(new_webauthn_credential).to model_have_error_on_field(:external_id)
end
it 'is invalid if user already registered a webauthn credential with the same nickname' do
user = Fabricate(:user)
Fabricate(:webauthn_credential, user_id: user.id, nickname: 'USB Key')
new_webauthn_credential = Fabricate.build(:webauthn_credential, user_id: user.id, nickname: 'USB Key')
new_webauthn_credential.valid?
expect(new_webauthn_credential).to model_have_error_on_field(:nickname)
end
it 'is invalid if sign_count is not a number' do
webauthn_credential = Fabricate.build(:webauthn_credential, sign_count: 'invalid sign_count')
webauthn_credential.valid?
expect(webauthn_credential).to model_have_error_on_field(:sign_count)
end
it 'is invalid if sign_count is negative number' do
webauthn_credential = Fabricate.build(:webauthn_credential, sign_count: -1)
webauthn_credential.valid?
expect(webauthn_credential).to model_have_error_on_field(:sign_count)
end
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?
expect(webauthn_credential).to model_have_error_on_field(:sign_count)
end
end end
end end

2037
yarn.lock

File diff suppressed because it is too large Load diff