Merge branch 'master' into feature-circles
This commit is contained in:
commit
824d1b8893
906 changed files with 37100 additions and 11600 deletions
4
spec/models/account_deletion_request_spec.rb
Normal file
4
spec/models/account_deletion_request_spec.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountDeletionRequest, type: :model do
|
||||
end
|
|
@ -5,7 +5,7 @@ describe AccountFilter do
|
|||
it 'defaults to recent local not-suspended account list' do
|
||||
filter = described_class.new({})
|
||||
|
||||
expect(filter.results).to eq Account.local.recent.without_suspended
|
||||
expect(filter.results).to eq Account.local.without_instance_actor.recent.without_suspended
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -440,13 +440,6 @@ RSpec.describe Account, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.domains' do
|
||||
it 'returns domains' do
|
||||
Fabricate(:account, domain: 'domain')
|
||||
expect(Account.remote.domains).to match_array(['domain'])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#statuses_count' do
|
||||
subject { Fabricate(:account) }
|
||||
|
||||
|
@ -737,20 +730,6 @@ RSpec.describe Account, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'by_domain_accounts' do
|
||||
it 'returns accounts grouped by domain sorted by accounts' do
|
||||
2.times { Fabricate(:account, domain: 'example.com') }
|
||||
Fabricate(:account, domain: 'example2.com')
|
||||
|
||||
results = Account.where('id > 0').by_domain_accounts
|
||||
expect(results.length).to eq 2
|
||||
expect(results.first.domain).to eq 'example.com'
|
||||
expect(results.first.accounts_count).to eq 2
|
||||
expect(results.last.domain).to eq 'example2.com'
|
||||
expect(results.last.accounts_count).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
describe 'local' do
|
||||
it 'returns an array of accounts who do not have a domain' do
|
||||
account_1 = Fabricate(:account, domain: nil)
|
||||
|
@ -817,4 +796,27 @@ RSpec.describe Account, type: :model do
|
|||
|
||||
include_examples 'AccountAvatar', :account
|
||||
include_examples 'AccountHeader', :account
|
||||
|
||||
describe '#increment_count!' do
|
||||
subject { Fabricate(:account) }
|
||||
|
||||
it 'increments the count in multi-threaded an environment when account_stat is not yet initialized' do
|
||||
subject
|
||||
|
||||
increment_by = 15
|
||||
wait_for_start = true
|
||||
|
||||
threads = Array.new(increment_by) do
|
||||
Thread.new do
|
||||
true while wait_for_start
|
||||
Account.find(subject.id).increment_count!(:followers_count)
|
||||
end
|
||||
end
|
||||
|
||||
wait_for_start = false
|
||||
threads.each(&:join)
|
||||
|
||||
expect(subject.reload.followers_count).to eq 15
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -115,16 +115,16 @@ RSpec.describe Admin::AccountAction, type: :model do
|
|||
context 'account.local?' do
|
||||
let(:account) { Fabricate(:account, domain: nil) }
|
||||
|
||||
it 'returns ["none", "disable", "silence", "suspend"]' do
|
||||
expect(subject).to eq %w(none disable silence suspend)
|
||||
it 'returns ["none", "disable", "sensitive", "silence", "suspend"]' do
|
||||
expect(subject).to eq %w(none disable sensitive silence suspend)
|
||||
end
|
||||
end
|
||||
|
||||
context '!account.local?' do
|
||||
let(:account) { Fabricate(:account, domain: 'hoge.com') }
|
||||
|
||||
it 'returns ["silence", "suspend"]' do
|
||||
expect(subject).to eq %w(silence suspend)
|
||||
it 'returns ["sensitive", "silence", "suspend"]' do
|
||||
expect(subject).to eq %w(sensitive silence suspend)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@ describe AccountInteractions do
|
|||
context 'account with Follow' do
|
||||
it 'returns { target_account_id => true }' do
|
||||
Fabricate(:follow, account: account, target_account: target_account)
|
||||
is_expected.to eq(target_account_id => { reblogs: true })
|
||||
is_expected.to eq(target_account_id => { reblogs: true, notify: false })
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -539,6 +539,49 @@ describe AccountInteractions do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#followers_hash' do
|
||||
let(:me) { Fabricate(:account, username: 'Me') }
|
||||
let(:remote_1) { Fabricate(:account, username: 'alice', domain: 'example.org', uri: 'https://example.org/users/alice') }
|
||||
let(:remote_2) { Fabricate(:account, username: 'bob', domain: 'example.org', uri: 'https://example.org/users/bob') }
|
||||
let(:remote_3) { Fabricate(:account, username: 'eve', domain: 'foo.org', uri: 'https://foo.org/users/eve') }
|
||||
|
||||
before do
|
||||
remote_1.follow!(me)
|
||||
remote_2.follow!(me)
|
||||
remote_3.follow!(me)
|
||||
me.follow!(remote_1)
|
||||
end
|
||||
|
||||
context 'on a local user' do
|
||||
it 'returns correct hash for remote domains' do
|
||||
expect(me.remote_followers_hash('https://example.org/')).to eq '707962e297b7bd94468a21bc8e506a1bcea607a9142cd64e27c9b106b2a5f6ec'
|
||||
expect(me.remote_followers_hash('https://foo.org/')).to eq 'ccb9c18a67134cfff9d62c7f7e7eb88e6b803446c244b84265565f4eba29df0e'
|
||||
end
|
||||
|
||||
it 'invalidates cache as needed when removing or adding followers' do
|
||||
expect(me.remote_followers_hash('https://example.org/')).to eq '707962e297b7bd94468a21bc8e506a1bcea607a9142cd64e27c9b106b2a5f6ec'
|
||||
remote_1.unfollow!(me)
|
||||
expect(me.remote_followers_hash('https://example.org/')).to eq '241b00794ce9b46aa864f3220afadef128318da2659782985bac5ed5bd436bff'
|
||||
remote_1.follow!(me)
|
||||
expect(me.remote_followers_hash('https://example.org/')).to eq '707962e297b7bd94468a21bc8e506a1bcea607a9142cd64e27c9b106b2a5f6ec'
|
||||
end
|
||||
end
|
||||
|
||||
context 'on a remote user' do
|
||||
it 'returns correct hash for remote domains' do
|
||||
expect(remote_1.local_followers_hash).to eq Digest::SHA256.hexdigest(ActivityPub::TagManager.instance.uri_for(me))
|
||||
end
|
||||
|
||||
it 'invalidates cache as needed when removing or adding followers' do
|
||||
expect(remote_1.local_followers_hash).to eq Digest::SHA256.hexdigest(ActivityPub::TagManager.instance.uri_for(me))
|
||||
me.unfollow!(remote_1)
|
||||
expect(remote_1.local_followers_hash).to eq '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
me.follow!(remote_1)
|
||||
expect(remote_1.local_followers_hash).to eq Digest::SHA256.hexdigest(ActivityPub::TagManager.instance.uri_for(me))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'muting an account' do
|
||||
let(:me) { Fabricate(:account, username: 'Me') }
|
||||
let(:you) { Fabricate(:account, username: 'You') }
|
||||
|
|
|
@ -7,7 +7,7 @@ RSpec.describe FollowRequest, type: :model do
|
|||
let(:target_account) { Fabricate(:account) }
|
||||
|
||||
it 'calls Account#follow!, MergeWorker.perform_async, and #destroy!' do
|
||||
expect(account).to receive(:follow!).with(target_account, reblogs: true, uri: follow_request.uri)
|
||||
expect(account).to receive(:follow!).with(target_account, reblogs: true, notify: false, uri: follow_request.uri)
|
||||
expect(MergeWorker).to receive(:perform_async).with(target_account.id, account.id)
|
||||
expect(follow_request).to receive(:destroy!)
|
||||
follow_request.authorize!
|
||||
|
|
|
@ -5,7 +5,7 @@ RSpec.describe Follow, type: :model do
|
|||
let(:bob) { Fabricate(:account, username: 'bob') }
|
||||
|
||||
describe 'validations' do
|
||||
subject { Follow.new(account: alice, target_account: bob) }
|
||||
subject { Follow.new(account: alice, target_account: bob, rate_limit: true) }
|
||||
|
||||
it 'has a valid fabricator' do
|
||||
follow = Fabricate.build(:follow)
|
||||
|
|
|
@ -20,5 +20,15 @@ RSpec.describe Import, type: :model do
|
|||
import = Import.create(account: account, type: type)
|
||||
expect(import).to model_have_error_on_field(:data)
|
||||
end
|
||||
|
||||
it 'is invalid with too many rows in data' do
|
||||
import = Import.create(account: account, type: type, data: StringIO.new("foo@bar.com\n" * (ImportService::ROWS_PROCESSING_LIMIT + 10)))
|
||||
expect(import).to model_have_error_on_field(:data)
|
||||
end
|
||||
|
||||
it 'is invalid when there are more rows when following limit' do
|
||||
import = Import.create(account: account, type: type, data: StringIO.new("foo@bar.com\n" * (FollowLimitValidator.limit_for_account(account) + 10)))
|
||||
expect(import).to model_have_error_on_field(:data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,7 +29,7 @@ RSpec.describe Invite, type: :model do
|
|||
|
||||
it 'returns false when invite creator has been disabled' do
|
||||
invite = Fabricate(:invite, max_uses: nil, expires_at: nil)
|
||||
SuspendAccountService.new.call(invite.user.account)
|
||||
invite.user.account.suspend!
|
||||
expect(invite.valid_for_use?).to be false
|
||||
end
|
||||
end
|
||||
|
|
5
spec/models/ip_block_spec.rb
Normal file
5
spec/models/ip_block_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe IpBlock, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
212
spec/models/public_feed_spec.rb
Normal file
212
spec/models/public_feed_spec.rb
Normal file
|
@ -0,0 +1,212 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe PublicFeed, type: :model do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
describe '#get' do
|
||||
subject { described_class.new(nil).get(20).map(&:id) }
|
||||
|
||||
it 'only includes statuses with public visibility' do
|
||||
public_status = Fabricate(:status, visibility: :public)
|
||||
private_status = Fabricate(:status, visibility: :private)
|
||||
|
||||
expect(subject).to include(public_status.id)
|
||||
expect(subject).not_to include(private_status.id)
|
||||
end
|
||||
|
||||
it 'does not include replies' do
|
||||
status = Fabricate(:status)
|
||||
reply = Fabricate(:status, in_reply_to_id: status.id)
|
||||
|
||||
expect(subject).to include(status.id)
|
||||
expect(subject).not_to include(reply.id)
|
||||
end
|
||||
|
||||
it 'does not include boosts' do
|
||||
status = Fabricate(:status)
|
||||
boost = Fabricate(:status, reblog_of_id: status.id)
|
||||
|
||||
expect(subject).to include(status.id)
|
||||
expect(subject).not_to include(boost.id)
|
||||
end
|
||||
|
||||
it 'filters out silenced accounts' do
|
||||
account = Fabricate(:account)
|
||||
silenced_account = Fabricate(:account, silenced: true)
|
||||
status = Fabricate(:status, account: account)
|
||||
silenced_status = Fabricate(:status, account: silenced_account)
|
||||
|
||||
expect(subject).to include(status.id)
|
||||
expect(subject).not_to include(silenced_status.id)
|
||||
end
|
||||
|
||||
context 'without local_only option' do
|
||||
let(:viewer) { nil }
|
||||
|
||||
let!(:local_account) { Fabricate(:account, domain: nil) }
|
||||
let!(:remote_account) { Fabricate(:account, domain: 'test.com') }
|
||||
let!(:local_status) { Fabricate(:status, account: local_account) }
|
||||
let!(:remote_status) { Fabricate(:status, account: remote_account) }
|
||||
|
||||
subject { described_class.new(viewer).get(20).map(&:id) }
|
||||
|
||||
context 'without a viewer' do
|
||||
let(:viewer) { nil }
|
||||
|
||||
it 'includes remote instances statuses' do
|
||||
expect(subject).to include(remote_status.id)
|
||||
end
|
||||
|
||||
it 'includes local statuses' do
|
||||
expect(subject).to include(local_status.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a viewer' do
|
||||
let(:viewer) { Fabricate(:account, username: 'viewer') }
|
||||
|
||||
it 'includes remote instances statuses' do
|
||||
expect(subject).to include(remote_status.id)
|
||||
end
|
||||
|
||||
it 'includes local statuses' do
|
||||
expect(subject).to include(local_status.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a local_only option set' do
|
||||
let!(:local_account) { Fabricate(:account, domain: nil) }
|
||||
let!(:remote_account) { Fabricate(:account, domain: 'test.com') }
|
||||
let!(:local_status) { Fabricate(:status, account: local_account) }
|
||||
let!(:remote_status) { Fabricate(:status, account: remote_account) }
|
||||
|
||||
subject { described_class.new(viewer, local: true).get(20).map(&:id) }
|
||||
|
||||
context 'without a viewer' do
|
||||
let(:viewer) { nil }
|
||||
|
||||
it 'does not include remote instances statuses' do
|
||||
expect(subject).to include(local_status.id)
|
||||
expect(subject).not_to include(remote_status.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a viewer' do
|
||||
let(:viewer) { Fabricate(:account, username: 'viewer') }
|
||||
|
||||
it 'does not include remote instances statuses' do
|
||||
expect(subject).to include(local_status.id)
|
||||
expect(subject).not_to include(remote_status.id)
|
||||
end
|
||||
|
||||
it 'is not affected by personal domain blocks' do
|
||||
viewer.block_domain!('test.com')
|
||||
expect(subject).to include(local_status.id)
|
||||
expect(subject).not_to include(remote_status.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a remote_only option set' do
|
||||
let!(:local_account) { Fabricate(:account, domain: nil) }
|
||||
let!(:remote_account) { Fabricate(:account, domain: 'test.com') }
|
||||
let!(:local_status) { Fabricate(:status, account: local_account) }
|
||||
let!(:remote_status) { Fabricate(:status, account: remote_account) }
|
||||
|
||||
subject { described_class.new(viewer, remote: true).get(20).map(&:id) }
|
||||
|
||||
context 'without a viewer' do
|
||||
let(:viewer) { nil }
|
||||
|
||||
it 'does not include local instances statuses' do
|
||||
expect(subject).not_to include(local_status.id)
|
||||
expect(subject).to include(remote_status.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a viewer' do
|
||||
let(:viewer) { Fabricate(:account, username: 'viewer') }
|
||||
|
||||
it 'does not include local instances statuses' do
|
||||
expect(subject).not_to include(local_status.id)
|
||||
expect(subject).to include(remote_status.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with an account passed in' do
|
||||
before do
|
||||
@account = Fabricate(:account)
|
||||
end
|
||||
|
||||
subject { described_class.new(@account).get(20).map(&:id) }
|
||||
|
||||
it 'excludes statuses from accounts blocked by the account' do
|
||||
blocked = Fabricate(:account)
|
||||
@account.block!(blocked)
|
||||
blocked_status = Fabricate(:status, account: blocked)
|
||||
|
||||
expect(subject).not_to include(blocked_status.id)
|
||||
end
|
||||
|
||||
it 'excludes statuses from accounts who have blocked the account' do
|
||||
blocker = Fabricate(:account)
|
||||
blocker.block!(@account)
|
||||
blocked_status = Fabricate(:status, account: blocker)
|
||||
|
||||
expect(subject).not_to include(blocked_status.id)
|
||||
end
|
||||
|
||||
it 'excludes statuses from accounts muted by the account' do
|
||||
muted = Fabricate(:account)
|
||||
@account.mute!(muted)
|
||||
muted_status = Fabricate(:status, account: muted)
|
||||
|
||||
expect(subject).not_to include(muted_status.id)
|
||||
end
|
||||
|
||||
it 'excludes statuses from accounts from personally blocked domains' do
|
||||
blocked = Fabricate(:account, domain: 'example.com')
|
||||
@account.block_domain!(blocked.domain)
|
||||
blocked_status = Fabricate(:status, account: blocked)
|
||||
|
||||
expect(subject).not_to include(blocked_status.id)
|
||||
end
|
||||
|
||||
context 'with language preferences' do
|
||||
it 'excludes statuses in languages not allowed by the account user' do
|
||||
user = Fabricate(:user, chosen_languages: [:en, :es])
|
||||
@account.update(user: user)
|
||||
en_status = Fabricate(:status, language: 'en')
|
||||
es_status = Fabricate(:status, language: 'es')
|
||||
fr_status = Fabricate(:status, language: 'fr')
|
||||
|
||||
expect(subject).to include(en_status.id)
|
||||
expect(subject).to include(es_status.id)
|
||||
expect(subject).not_to include(fr_status.id)
|
||||
end
|
||||
|
||||
it 'includes all languages when user does not have a setting' do
|
||||
user = Fabricate(:user, chosen_languages: nil)
|
||||
@account.update(user: user)
|
||||
|
||||
en_status = Fabricate(:status, language: 'en')
|
||||
es_status = Fabricate(:status, language: 'es')
|
||||
|
||||
expect(subject).to include(en_status.id)
|
||||
expect(subject).to include(es_status.id)
|
||||
end
|
||||
|
||||
it 'includes all languages when account does not have a user' do
|
||||
expect(@account.user).to be_nil
|
||||
en_status = Fabricate(:status, language: 'en')
|
||||
es_status = Fabricate(:status, language: 'es')
|
||||
|
||||
expect(subject).to include(en_status.id)
|
||||
expect(subject).to include(es_status.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -267,241 +267,6 @@ RSpec.describe Status, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.as_public_timeline' do
|
||||
it 'only includes statuses with public visibility' do
|
||||
public_status = Fabricate(:status, visibility: :public)
|
||||
private_status = Fabricate(:status, visibility: :private)
|
||||
|
||||
results = Status.as_public_timeline
|
||||
expect(results).to include(public_status)
|
||||
expect(results).not_to include(private_status)
|
||||
end
|
||||
|
||||
it 'does not include replies' do
|
||||
status = Fabricate(:status)
|
||||
reply = Fabricate(:status, in_reply_to_id: status.id)
|
||||
|
||||
results = Status.as_public_timeline
|
||||
expect(results).to include(status)
|
||||
expect(results).not_to include(reply)
|
||||
end
|
||||
|
||||
it 'does not include boosts' do
|
||||
status = Fabricate(:status)
|
||||
boost = Fabricate(:status, reblog_of_id: status.id)
|
||||
|
||||
results = Status.as_public_timeline
|
||||
expect(results).to include(status)
|
||||
expect(results).not_to include(boost)
|
||||
end
|
||||
|
||||
it 'filters out silenced accounts' do
|
||||
account = Fabricate(:account)
|
||||
silenced_account = Fabricate(:account, silenced: true)
|
||||
status = Fabricate(:status, account: account)
|
||||
silenced_status = Fabricate(:status, account: silenced_account)
|
||||
|
||||
results = Status.as_public_timeline
|
||||
expect(results).to include(status)
|
||||
expect(results).not_to include(silenced_status)
|
||||
end
|
||||
|
||||
context 'without local_only option' do
|
||||
let(:viewer) { nil }
|
||||
|
||||
let!(:local_account) { Fabricate(:account, domain: nil) }
|
||||
let!(:remote_account) { Fabricate(:account, domain: 'test.com') }
|
||||
let!(:local_status) { Fabricate(:status, account: local_account) }
|
||||
let!(:remote_status) { Fabricate(:status, account: remote_account) }
|
||||
|
||||
subject { Status.as_public_timeline(viewer, false) }
|
||||
|
||||
context 'without a viewer' do
|
||||
let(:viewer) { nil }
|
||||
|
||||
it 'includes remote instances statuses' do
|
||||
expect(subject).to include(remote_status)
|
||||
end
|
||||
|
||||
it 'includes local statuses' do
|
||||
expect(subject).to include(local_status)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a viewer' do
|
||||
let(:viewer) { Fabricate(:account, username: 'viewer') }
|
||||
|
||||
it 'includes remote instances statuses' do
|
||||
expect(subject).to include(remote_status)
|
||||
end
|
||||
|
||||
it 'includes local statuses' do
|
||||
expect(subject).to include(local_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a local_only option set' do
|
||||
let!(:local_account) { Fabricate(:account, domain: nil) }
|
||||
let!(:remote_account) { Fabricate(:account, domain: 'test.com') }
|
||||
let!(:local_status) { Fabricate(:status, account: local_account) }
|
||||
let!(:remote_status) { Fabricate(:status, account: remote_account) }
|
||||
|
||||
subject { Status.as_public_timeline(viewer, true) }
|
||||
|
||||
context 'without a viewer' do
|
||||
let(:viewer) { nil }
|
||||
|
||||
it 'does not include remote instances statuses' do
|
||||
expect(subject).to include(local_status)
|
||||
expect(subject).not_to include(remote_status)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a viewer' do
|
||||
let(:viewer) { Fabricate(:account, username: 'viewer') }
|
||||
|
||||
it 'does not include remote instances statuses' do
|
||||
expect(subject).to include(local_status)
|
||||
expect(subject).not_to include(remote_status)
|
||||
end
|
||||
|
||||
it 'is not affected by personal domain blocks' do
|
||||
viewer.block_domain!('test.com')
|
||||
expect(subject).to include(local_status)
|
||||
expect(subject).not_to include(remote_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a remote_only option set' do
|
||||
let!(:local_account) { Fabricate(:account, domain: nil) }
|
||||
let!(:remote_account) { Fabricate(:account, domain: 'test.com') }
|
||||
let!(:local_status) { Fabricate(:status, account: local_account) }
|
||||
let!(:remote_status) { Fabricate(:status, account: remote_account) }
|
||||
|
||||
subject { Status.as_public_timeline(viewer, :remote) }
|
||||
|
||||
context 'without a viewer' do
|
||||
let(:viewer) { nil }
|
||||
|
||||
it 'does not include local instances statuses' do
|
||||
expect(subject).not_to include(local_status)
|
||||
expect(subject).to include(remote_status)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a viewer' do
|
||||
let(:viewer) { Fabricate(:account, username: 'viewer') }
|
||||
|
||||
it 'does not include local instances statuses' do
|
||||
expect(subject).not_to include(local_status)
|
||||
expect(subject).to include(remote_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with an account passed in' do
|
||||
before do
|
||||
@account = Fabricate(:account)
|
||||
end
|
||||
|
||||
it 'excludes statuses from accounts blocked by the account' do
|
||||
blocked = Fabricate(:account)
|
||||
Fabricate(:block, account: @account, target_account: blocked)
|
||||
blocked_status = Fabricate(:status, account: blocked)
|
||||
|
||||
results = Status.as_public_timeline(@account)
|
||||
expect(results).not_to include(blocked_status)
|
||||
end
|
||||
|
||||
it 'excludes statuses from accounts who have blocked the account' do
|
||||
blocked = Fabricate(:account)
|
||||
Fabricate(:block, account: blocked, target_account: @account)
|
||||
blocked_status = Fabricate(:status, account: blocked)
|
||||
|
||||
results = Status.as_public_timeline(@account)
|
||||
expect(results).not_to include(blocked_status)
|
||||
end
|
||||
|
||||
it 'excludes statuses from accounts muted by the account' do
|
||||
muted = Fabricate(:account)
|
||||
Fabricate(:mute, account: @account, target_account: muted)
|
||||
muted_status = Fabricate(:status, account: muted)
|
||||
|
||||
results = Status.as_public_timeline(@account)
|
||||
expect(results).not_to include(muted_status)
|
||||
end
|
||||
|
||||
it 'excludes statuses from accounts from personally blocked domains' do
|
||||
blocked = Fabricate(:account, domain: 'example.com')
|
||||
@account.block_domain!(blocked.domain)
|
||||
blocked_status = Fabricate(:status, account: blocked)
|
||||
|
||||
results = Status.as_public_timeline(@account)
|
||||
expect(results).not_to include(blocked_status)
|
||||
end
|
||||
|
||||
context 'with language preferences' do
|
||||
it 'excludes statuses in languages not allowed by the account user' do
|
||||
user = Fabricate(:user, chosen_languages: [:en, :es])
|
||||
@account.update(user: user)
|
||||
en_status = Fabricate(:status, language: 'en')
|
||||
es_status = Fabricate(:status, language: 'es')
|
||||
fr_status = Fabricate(:status, language: 'fr')
|
||||
|
||||
results = Status.as_public_timeline(@account)
|
||||
expect(results).to include(en_status)
|
||||
expect(results).to include(es_status)
|
||||
expect(results).not_to include(fr_status)
|
||||
end
|
||||
|
||||
it 'includes all languages when user does not have a setting' do
|
||||
user = Fabricate(:user, chosen_languages: nil)
|
||||
@account.update(user: user)
|
||||
|
||||
en_status = Fabricate(:status, language: 'en')
|
||||
es_status = Fabricate(:status, language: 'es')
|
||||
|
||||
results = Status.as_public_timeline(@account)
|
||||
expect(results).to include(en_status)
|
||||
expect(results).to include(es_status)
|
||||
end
|
||||
|
||||
it 'includes all languages when account does not have a user' do
|
||||
expect(@account.user).to be_nil
|
||||
en_status = Fabricate(:status, language: 'en')
|
||||
es_status = Fabricate(:status, language: 'es')
|
||||
|
||||
results = Status.as_public_timeline(@account)
|
||||
expect(results).to include(en_status)
|
||||
expect(results).to include(es_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.as_tag_timeline' do
|
||||
it 'includes statuses with a tag' do
|
||||
tag = Fabricate(:tag)
|
||||
status = Fabricate(:status, tags: [tag])
|
||||
other = Fabricate(:status)
|
||||
|
||||
results = Status.as_tag_timeline(tag)
|
||||
expect(results).to include(status)
|
||||
expect(results).not_to include(other)
|
||||
end
|
||||
|
||||
it 'allows replies to be included' do
|
||||
original = Fabricate(:status)
|
||||
tag = Fabricate(:tag)
|
||||
status = Fabricate(:status, tags: [tag], in_reply_to_id: original.id)
|
||||
|
||||
results = Status.as_tag_timeline(tag)
|
||||
expect(results).to include(status)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.permitted_for' do
|
||||
subject { described_class.permitted_for(target_account, account).pluck(:visibility) }
|
||||
|
||||
|
|
68
spec/models/tag_feed_spec.rb
Normal file
68
spec/models/tag_feed_spec.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe TagFeed, type: :service do
|
||||
describe '#get' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:tag1) { Fabricate(:tag) }
|
||||
let(:tag2) { Fabricate(:tag) }
|
||||
let!(:status1) { Fabricate(:status, tags: [tag1]) }
|
||||
let!(:status2) { Fabricate(:status, tags: [tag2]) }
|
||||
let!(:both) { Fabricate(:status, tags: [tag1, tag2]) }
|
||||
|
||||
it 'can add tags in "any" mode' do
|
||||
results = described_class.new(tag1, nil, any: [tag2.name]).get(20)
|
||||
expect(results).to include status1
|
||||
expect(results).to include status2
|
||||
expect(results).to include both
|
||||
end
|
||||
|
||||
it 'can remove tags in "all" mode' do
|
||||
results = described_class.new(tag1, nil, all: [tag2.name]).get(20)
|
||||
expect(results).to_not include status1
|
||||
expect(results).to_not include status2
|
||||
expect(results).to include both
|
||||
end
|
||||
|
||||
it 'can remove tags in "none" mode' do
|
||||
results = described_class.new(tag1, nil, none: [tag2.name]).get(20)
|
||||
expect(results).to include status1
|
||||
expect(results).to_not include status2
|
||||
expect(results).to_not include both
|
||||
end
|
||||
|
||||
it 'ignores an invalid mode' do
|
||||
results = described_class.new(tag1, nil, wark: [tag2.name]).get(20)
|
||||
expect(results).to include status1
|
||||
expect(results).to_not include status2
|
||||
expect(results).to include both
|
||||
end
|
||||
|
||||
it 'handles being passed non existant tag names' do
|
||||
results = described_class.new(tag1, nil, any: ['wark']).get(20)
|
||||
expect(results).to include status1
|
||||
expect(results).to_not include status2
|
||||
expect(results).to include both
|
||||
end
|
||||
|
||||
it 'can restrict to an account' do
|
||||
BlockService.new.call(account, status1.account)
|
||||
results = described_class.new(tag1, account, none: [tag2.name]).get(20)
|
||||
expect(results).to_not include status1
|
||||
end
|
||||
|
||||
it 'can restrict to local' do
|
||||
status1.account.update(domain: 'example.com')
|
||||
status1.update(local: false, uri: 'example.com/toot')
|
||||
results = described_class.new(tag1, nil, any: [tag2.name], local: true).get(20)
|
||||
expect(results).to_not include status1
|
||||
end
|
||||
|
||||
it 'allows replies to be included' do
|
||||
original = Fabricate(:status)
|
||||
status = Fabricate(:status, tags: [tag1], in_reply_to_id: original.id)
|
||||
|
||||
results = described_class.new(tag1, nil).get(20)
|
||||
expect(results).to include(status)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -151,6 +151,12 @@ RSpec.describe User, type: :model do
|
|||
expect(user.reload.otp_required_for_login).to be false
|
||||
end
|
||||
|
||||
it 'saves nil for otp_secret' do
|
||||
user = Fabricate.build(:user, otp_secret: 'oldotpcode')
|
||||
user.disable_two_factor!
|
||||
expect(user.reload.otp_secret).to be nil
|
||||
end
|
||||
|
||||
it 'saves cleared otp_backup_codes' do
|
||||
user = Fabricate.build(:user, otp_backup_codes: %w(dummy dummy))
|
||||
user.disable_two_factor!
|
||||
|
|
80
spec/models/webauthn_credentials_spec.rb
Normal file
80
spec/models/webauthn_credentials_spec.rb
Normal file
|
@ -0,0 +1,80 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe WebauthnCredential, type: :model do
|
||||
describe 'validations' do
|
||||
it 'is invalid without an external id' do
|
||||
webauthn_credential = Fabricate.build(:webauthn_credential, external_id: nil)
|
||||
|
||||
webauthn_credential.valid?
|
||||
|
||||
expect(webauthn_credential).to model_have_error_on_field(:external_id)
|
||||
end
|
||||
|
||||
it 'is invalid without a public key' do
|
||||
webauthn_credential = Fabricate.build(:webauthn_credential, public_key: nil)
|
||||
|
||||
webauthn_credential.valid?
|
||||
|
||||
expect(webauthn_credential).to model_have_error_on_field(:public_key)
|
||||
end
|
||||
|
||||
it 'is invalid without a nickname' do
|
||||
webauthn_credential = Fabricate.build(:webauthn_credential, nickname: nil)
|
||||
|
||||
webauthn_credential.valid?
|
||||
|
||||
expect(webauthn_credential).to model_have_error_on_field(:nickname)
|
||||
end
|
||||
|
||||
it 'is invalid without a sign_count' do
|
||||
webauthn_credential = Fabricate.build(:webauthn_credential, sign_count: nil)
|
||||
|
||||
webauthn_credential.valid?
|
||||
|
||||
expect(webauthn_credential).to model_have_error_on_field(:sign_count)
|
||||
end
|
||||
|
||||
it 'is invalid if already exist a webauthn credential with the same external id' do
|
||||
existing_webauthn_credential = Fabricate(:webauthn_credential, external_id: "_Typ0ygudDnk9YUVWLQayw")
|
||||
new_webauthn_credential = Fabricate.build(:webauthn_credential, external_id: "_Typ0ygudDnk9YUVWLQayw")
|
||||
|
||||
new_webauthn_credential.valid?
|
||||
|
||||
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)
|
||||
existing_webauthn_credential = 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 2**63 - 1' do
|
||||
webauthn_credential = Fabricate.build(:webauthn_credential, sign_count: 2**63)
|
||||
|
||||
webauthn_credential.valid?
|
||||
|
||||
expect(webauthn_credential).to model_have_error_on_field(:sign_count)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue