Merge tag 'v3.0.1' into instance_only_statuses
This commit is contained in:
5
spec/models/account_alias_spec.rb
Normal file
5
spec/models/account_alias_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountAlias, type: :model do
|
||||
|
||||
end
|
5
spec/models/account_migration_spec.rb
Normal file
5
spec/models/account_migration_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountMigration, type: :model do
|
||||
|
||||
end
|
@ -126,8 +126,8 @@ RSpec.describe Account, type: :model do
|
||||
end
|
||||
|
||||
it 'sets default avatar, header, avatar_remote_url, and header_remote_url' do
|
||||
expect(account.avatar_remote_url).to eq ''
|
||||
expect(account.header_remote_url).to eq ''
|
||||
expect(account.avatar_remote_url).to eq 'https://remote.test/invalid_avatar'
|
||||
expect(account.header_remote_url).to eq expectation.header_remote_url
|
||||
expect(account.avatar_file_name).to eq nil
|
||||
expect(account.header_file_name).to eq nil
|
||||
end
|
||||
@ -450,7 +450,7 @@ RSpec.describe Account, type: :model do
|
||||
describe '.domains' do
|
||||
it 'returns domains' do
|
||||
Fabricate(:account, domain: 'domain')
|
||||
expect(Account.domains).to match_array(['domain'])
|
||||
expect(Account.remote.domains).to match_array(['domain'])
|
||||
end
|
||||
end
|
||||
|
||||
@ -583,26 +583,43 @@ RSpec.describe Account, type: :model do
|
||||
expect(account.valid?).to be true
|
||||
end
|
||||
|
||||
it 'is valid if we are creating an instance actor account with a period' do
|
||||
account = Fabricate.build(:account, id: -99, actor_type: 'Application', locked: true, username: 'example.com')
|
||||
expect(account.valid?).to be true
|
||||
end
|
||||
|
||||
it 'is valid if we are creating a possibly-conflicting instance actor account' do
|
||||
account_1 = Fabricate(:account, username: 'examplecom')
|
||||
account_2 = Fabricate.build(:account, id: -99, actor_type: 'Application', locked: true, username: 'example.com')
|
||||
expect(account_2.valid?).to be true
|
||||
end
|
||||
|
||||
it 'is invalid if the username doesn\'t only contains letters, numbers and underscores' do
|
||||
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
|
||||
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 is longer then 30 characters' do
|
||||
account = Fabricate.build(:account, username: Faker::Lorem.characters(31))
|
||||
account = Fabricate.build(:account, username: Faker::Lorem.characters(number: 31))
|
||||
account.valid?
|
||||
expect(account).to model_have_error_on_field(:username)
|
||||
end
|
||||
|
||||
it 'is invalid if the display name is longer than 30 characters' do
|
||||
account = Fabricate.build(:account, display_name: Faker::Lorem.characters(31))
|
||||
account = Fabricate.build(:account, display_name: Faker::Lorem.characters(number: 31))
|
||||
account.valid?
|
||||
expect(account).to model_have_error_on_field(:display_name)
|
||||
end
|
||||
|
||||
it 'is invalid if the note is longer than 500 characters' do
|
||||
account = Fabricate.build(:account, note: Faker::Lorem.characters(501))
|
||||
account = Fabricate.build(:account, note: Faker::Lorem.characters(number: 501))
|
||||
account.valid?
|
||||
expect(account).to model_have_error_on_field(:note)
|
||||
end
|
||||
@ -636,19 +653,19 @@ RSpec.describe Account, type: :model do
|
||||
end
|
||||
|
||||
it 'is valid even if the username is longer then 30 characters' do
|
||||
account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(31))
|
||||
account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(number: 31))
|
||||
account.valid?
|
||||
expect(account).not_to model_have_error_on_field(:username)
|
||||
end
|
||||
|
||||
it 'is valid even if the display name is longer than 30 characters' do
|
||||
account = Fabricate.build(:account, domain: 'domain', display_name: Faker::Lorem.characters(31))
|
||||
account = Fabricate.build(:account, domain: 'domain', display_name: Faker::Lorem.characters(number: 31))
|
||||
account.valid?
|
||||
expect(account).not_to model_have_error_on_field(:display_name)
|
||||
end
|
||||
|
||||
it 'is valid even if the note is longer than 500 characters' do
|
||||
account = Fabricate.build(:account, domain: 'domain', note: Faker::Lorem.characters(501))
|
||||
account = Fabricate.build(:account, domain: 'domain', note: Faker::Lorem.characters(number: 501))
|
||||
account.valid?
|
||||
expect(account).not_to model_have_error_on_field(:note)
|
||||
end
|
||||
@ -665,7 +682,7 @@ RSpec.describe Account, type: :model do
|
||||
{ username: 'b', domain: 'b' },
|
||||
].map(&method(:Fabricate).curry(2).call(:account))
|
||||
|
||||
expect(Account.alphabetic).to eq matches
|
||||
expect(Account.where('id > 0').alphabetic).to eq matches
|
||||
end
|
||||
end
|
||||
|
||||
@ -732,7 +749,7 @@ RSpec.describe Account, type: :model do
|
||||
2.times { Fabricate(:account, domain: 'example.com') }
|
||||
Fabricate(:account, domain: 'example2.com')
|
||||
|
||||
results = Account.by_domain_accounts
|
||||
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
|
||||
@ -745,7 +762,7 @@ RSpec.describe Account, type: :model do
|
||||
it 'returns an array of accounts who do not have a domain' do
|
||||
account_1 = Fabricate(:account, domain: nil)
|
||||
account_2 = Fabricate(:account, domain: 'example.com')
|
||||
expect(Account.local).to match_array([account_1])
|
||||
expect(Account.where('id > 0').local).to match_array([account_1])
|
||||
end
|
||||
end
|
||||
|
||||
@ -756,14 +773,14 @@ RSpec.describe Account, type: :model do
|
||||
matches[index] = Fabricate(:account, domain: matches[index])
|
||||
end
|
||||
|
||||
expect(Account.partitioned).to match_array(matches)
|
||||
expect(Account.where('id > 0').partitioned).to match_array(matches)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'recent' do
|
||||
it 'returns a relation of accounts sorted by recent creation' do
|
||||
matches = 2.times.map { Fabricate(:account) }
|
||||
expect(Account.recent).to match_array(matches)
|
||||
expect(Account.where('id > 0').recent).to match_array(matches)
|
||||
end
|
||||
end
|
||||
|
||||
@ -787,7 +804,7 @@ RSpec.describe Account, type: :model do
|
||||
context 'when is local' do
|
||||
# Test disabled because test environment omits autogenerating keys for performance
|
||||
xit 'generates keys' do
|
||||
account = Account.create!(domain: nil, username: Faker::Internet.user_name(nil, ['_']))
|
||||
account = Account.create!(domain: nil, username: Faker::Internet.user_name(separators: ['_']))
|
||||
expect(account.keypair.private?).to eq true
|
||||
end
|
||||
end
|
||||
@ -795,12 +812,12 @@ RSpec.describe Account, type: :model do
|
||||
context 'when is remote' do
|
||||
it 'does not generate keys' do
|
||||
key = OpenSSL::PKey::RSA.new(1024).public_key
|
||||
account = Account.create!(domain: 'remote', username: Faker::Internet.user_name(nil, ['_']), public_key: key.to_pem)
|
||||
account = Account.create!(domain: 'remote', username: Faker::Internet.user_name(separators: ['_']), public_key: key.to_pem)
|
||||
expect(account.keypair.params).to eq key.params
|
||||
end
|
||||
|
||||
it 'normalizes domain' do
|
||||
account = Account.create!(domain: 'にゃん', username: Faker::Internet.user_name(nil, ['_']))
|
||||
account = Account.create!(domain: 'にゃん', username: Faker::Internet.user_name(separators: ['_']))
|
||||
expect(account.domain).to eq 'xn--r9j5b5b'
|
||||
end
|
||||
end
|
||||
|
@ -1,4 +1,57 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountStat, type: :model do
|
||||
describe '#increment_count!' do
|
||||
it 'increments the count' do
|
||||
account_stat = AccountStat.create(account: Fabricate(:account))
|
||||
expect(account_stat.followers_count).to eq 0
|
||||
account_stat.increment_count!(:followers_count)
|
||||
expect(account_stat.followers_count).to eq 1
|
||||
end
|
||||
|
||||
it 'increments the count in multi-threaded an environment' do
|
||||
account_stat = AccountStat.create(account: Fabricate(:account), statuses_count: 0)
|
||||
increment_by = 15
|
||||
wait_for_start = true
|
||||
|
||||
threads = Array.new(increment_by) do
|
||||
Thread.new do
|
||||
true while wait_for_start
|
||||
AccountStat.find(account_stat.id).increment_count!(:statuses_count)
|
||||
end
|
||||
end
|
||||
|
||||
wait_for_start = false
|
||||
threads.each(&:join)
|
||||
|
||||
expect(account_stat.reload.statuses_count).to eq increment_by
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decrement_count!' do
|
||||
it 'decrements the count' do
|
||||
account_stat = AccountStat.create(account: Fabricate(:account), followers_count: 15)
|
||||
expect(account_stat.followers_count).to eq 15
|
||||
account_stat.decrement_count!(:followers_count)
|
||||
expect(account_stat.followers_count).to eq 14
|
||||
end
|
||||
|
||||
it 'decrements the count in multi-threaded an environment' do
|
||||
account_stat = AccountStat.create(account: Fabricate(:account), statuses_count: 15)
|
||||
decrement_by = 10
|
||||
wait_for_start = true
|
||||
|
||||
threads = Array.new(decrement_by) do
|
||||
Thread.new do
|
||||
true while wait_for_start
|
||||
AccountStat.find(account_stat.id).decrement_count!(:statuses_count)
|
||||
end
|
||||
end
|
||||
|
||||
wait_for_start = false
|
||||
threads.each(&:join)
|
||||
|
||||
expect(account_stat.reload.statuses_count).to eq 5
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -58,8 +58,8 @@ RSpec.describe Admin::AccountAction, type: :model do
|
||||
end.to change { Admin::ActionLog.count }.by 1
|
||||
end
|
||||
|
||||
it 'calls queue_email!' do
|
||||
expect(account_action).to receive(:queue_email!)
|
||||
it 'calls process_email!' do
|
||||
expect(account_action).to receive(:process_email!)
|
||||
subject
|
||||
end
|
||||
|
||||
|
@ -18,6 +18,8 @@ RSpec.describe Remotable do
|
||||
|
||||
def hoge=(arg); end
|
||||
|
||||
def hoge_file_name; end
|
||||
|
||||
def hoge_file_name=(arg); end
|
||||
|
||||
def has_attribute?(arg); end
|
||||
@ -109,12 +111,21 @@ RSpec.describe Remotable do
|
||||
end
|
||||
|
||||
context 'foo[attribute_name] == url' do
|
||||
it 'makes no request' do
|
||||
it 'makes no request if file is saved' do
|
||||
allow(foo).to receive(:[]).with(attribute_name).and_return(url)
|
||||
allow(foo).to receive(:hoge_file_name).and_return('foo.jpg')
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(request).not_to have_been_requested
|
||||
end
|
||||
|
||||
it 'makes request if file is not saved' do
|
||||
allow(foo).to receive(:[]).with(attribute_name).and_return(url)
|
||||
allow(foo).to receive(:hoge_file_name).and_return(nil)
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(request).to have_been_requested
|
||||
end
|
||||
end
|
||||
|
||||
context "scheme is https, parsed_url.host isn't empty, and foo[attribute_name] != url" do
|
||||
|
@ -1,63 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Streamable do
|
||||
class Parent
|
||||
def title; end
|
||||
|
||||
def target; end
|
||||
|
||||
def thread; end
|
||||
|
||||
def self.has_one(*); end
|
||||
|
||||
def self.after_create; end
|
||||
end
|
||||
|
||||
class Child < Parent
|
||||
include Streamable
|
||||
end
|
||||
|
||||
child = Child.new
|
||||
|
||||
describe '#title' do
|
||||
it 'calls Parent#title' do
|
||||
expect_any_instance_of(Parent).to receive(:title)
|
||||
child.title
|
||||
end
|
||||
end
|
||||
|
||||
describe '#content' do
|
||||
it 'calls #title' do
|
||||
expect_any_instance_of(Parent).to receive(:title)
|
||||
child.content
|
||||
end
|
||||
end
|
||||
|
||||
describe '#target' do
|
||||
it 'calls Parent#target' do
|
||||
expect_any_instance_of(Parent).to receive(:target)
|
||||
child.target
|
||||
end
|
||||
end
|
||||
|
||||
describe '#object_type' do
|
||||
it 'returns :activity' do
|
||||
expect(child.object_type).to eq :activity
|
||||
end
|
||||
end
|
||||
|
||||
describe '#thread' do
|
||||
it 'calls Parent#thread' do
|
||||
expect_any_instance_of(Parent).to receive(:thread)
|
||||
child.thread
|
||||
end
|
||||
end
|
||||
|
||||
describe '#hidden?' do
|
||||
it 'returns false' do
|
||||
expect(child.hidden?).to be false
|
||||
end
|
||||
end
|
||||
end
|
5
spec/models/custom_emoji_category_spec.rb
Normal file
5
spec/models/custom_emoji_category_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe CustomEmojiCategory, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
5
spec/models/domain_allow_spec.rb
Normal file
5
spec/models/domain_allow_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe DomainAllow, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
@ -41,12 +41,12 @@ describe Form::StatusBatch do
|
||||
|
||||
it 'call RemovalWorker' do
|
||||
form.save
|
||||
expect(RemovalWorker).to have_received(:perform_async).with(status.id)
|
||||
expect(RemovalWorker).to have_received(:perform_async).with(status.id, immediate: true)
|
||||
end
|
||||
|
||||
it 'do not call RemovalWorker' do
|
||||
form.save
|
||||
expect(RemovalWorker).not_to have_received(:perform_async).with(another_status.id)
|
||||
expect(RemovalWorker).not_to have_received(:perform_async).with(another_status.id, immediate: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -34,11 +34,10 @@ RSpec.describe HomeFeed, type: :model do
|
||||
Redis.current.set("account:#{account.id}:regeneration", true)
|
||||
end
|
||||
|
||||
it 'gets statuses with ids in the range from database' do
|
||||
it 'returns nothing' do
|
||||
results = subject.get(3)
|
||||
|
||||
expect(results.map(&:id)).to eq [10, 3, 2]
|
||||
expect(results.first.attributes.keys).to include('id', 'updated_at')
|
||||
expect(results.map(&:id)).to eq []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
5
spec/models/marker_spec.rb
Normal file
5
spec/models/marker_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Marker, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
@ -1,5 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe PollVote, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
describe '#object_type' do
|
||||
let(:poll_vote) { Fabricate.build(:poll_vote) }
|
||||
|
||||
it 'returns :vote' do
|
||||
expect(poll_vote.object_type).to eq :vote
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -61,7 +61,7 @@ RSpec.describe RemoteFollow do
|
||||
subject { remote_follow.subscribe_address_for(account) }
|
||||
|
||||
it 'returns subscribe address' do
|
||||
is_expected.to eq 'https://quitter.no/main/ostatussub?profile=alice%40cb6e6126.ngrok.io'
|
||||
is_expected.to eq 'https://quitter.no/main/ostatussub?profile=https%3A%2F%2Fcb6e6126.ngrok.io%2Fusers%2Falice'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,143 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe RemoteProfile do
|
||||
let(:remote_profile) { RemoteProfile.new(body) }
|
||||
let(:body) do
|
||||
<<-XML
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<author>John</author>
|
||||
XML
|
||||
end
|
||||
|
||||
describe '.initialize' do
|
||||
it 'calls Nokogiri::XML.parse' do
|
||||
expect(Nokogiri::XML).to receive(:parse).with(body, nil, 'utf-8')
|
||||
RemoteProfile.new(body)
|
||||
end
|
||||
|
||||
it 'sets document' do
|
||||
remote_profile = RemoteProfile.new(body)
|
||||
expect(remote_profile).not_to be nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#root' do
|
||||
let(:document) { remote_profile.document }
|
||||
|
||||
it 'callse document.at_xpath' do
|
||||
expect(document).to receive(:at_xpath).with(
|
||||
'/atom:feed|/atom:entry',
|
||||
atom: OStatus::TagManager::XMLNS
|
||||
)
|
||||
|
||||
remote_profile.root
|
||||
end
|
||||
end
|
||||
|
||||
describe '#author' do
|
||||
let(:root) { remote_profile.root }
|
||||
|
||||
it 'calls root.at_xpath' do
|
||||
expect(root).to receive(:at_xpath).with(
|
||||
'./atom:author|./dfrn:owner',
|
||||
atom: OStatus::TagManager::XMLNS,
|
||||
dfrn: OStatus::TagManager::DFRN_XMLNS
|
||||
)
|
||||
|
||||
remote_profile.author
|
||||
end
|
||||
end
|
||||
|
||||
describe '#hub_link' do
|
||||
let(:root) { remote_profile.root }
|
||||
|
||||
it 'calls #link_href_from_xml' do
|
||||
expect(remote_profile).to receive(:link_href_from_xml).with(root, 'hub')
|
||||
remote_profile.hub_link
|
||||
end
|
||||
end
|
||||
|
||||
describe '#display_name' do
|
||||
let(:author) { remote_profile.author }
|
||||
|
||||
it 'calls author.at_xpath.content' do
|
||||
expect(author).to receive_message_chain(:at_xpath, :content).with(
|
||||
'./poco:displayName',
|
||||
poco: OStatus::TagManager::POCO_XMLNS
|
||||
).with(no_args)
|
||||
|
||||
remote_profile.display_name
|
||||
end
|
||||
end
|
||||
|
||||
describe '#note' do
|
||||
let(:author) { remote_profile.author }
|
||||
|
||||
it 'calls author.at_xpath.content' do
|
||||
expect(author).to receive_message_chain(:at_xpath, :content).with(
|
||||
'./atom:summary|./poco:note',
|
||||
atom: OStatus::TagManager::XMLNS,
|
||||
poco: OStatus::TagManager::POCO_XMLNS
|
||||
).with(no_args)
|
||||
|
||||
remote_profile.note
|
||||
end
|
||||
end
|
||||
|
||||
describe '#scope' do
|
||||
let(:author) { remote_profile.author }
|
||||
|
||||
it 'calls author.at_xpath.content' do
|
||||
expect(author).to receive_message_chain(:at_xpath, :content).with(
|
||||
'./mastodon:scope',
|
||||
mastodon: OStatus::TagManager::MTDN_XMLNS
|
||||
).with(no_args)
|
||||
|
||||
remote_profile.scope
|
||||
end
|
||||
end
|
||||
|
||||
describe '#avatar' do
|
||||
let(:author) { remote_profile.author }
|
||||
|
||||
it 'calls #link_href_from_xml' do
|
||||
expect(remote_profile).to receive(:link_href_from_xml).with(author, 'avatar')
|
||||
remote_profile.avatar
|
||||
end
|
||||
end
|
||||
|
||||
describe '#header' do
|
||||
let(:author) { remote_profile.author }
|
||||
|
||||
it 'calls #link_href_from_xml' do
|
||||
expect(remote_profile).to receive(:link_href_from_xml).with(author, 'header')
|
||||
remote_profile.header
|
||||
end
|
||||
end
|
||||
|
||||
describe '#locked?' do
|
||||
before do
|
||||
allow(remote_profile).to receive(:scope).and_return(scope)
|
||||
end
|
||||
|
||||
subject { remote_profile.locked? }
|
||||
|
||||
context 'scope is private' do
|
||||
let(:scope) { 'private' }
|
||||
|
||||
it 'returns true' do
|
||||
is_expected.to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'scope is not private' do
|
||||
let(:scope) { 'public' }
|
||||
|
||||
it 'returns false' do
|
||||
is_expected.to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -125,7 +125,7 @@ describe Report do
|
||||
end
|
||||
|
||||
it 'is invalid if comment is longer than 1000 characters' do
|
||||
report = Fabricate.build(:report, comment: Faker::Lorem.characters(1001))
|
||||
report = Fabricate.build(:report, comment: Faker::Lorem.characters(number: 1001))
|
||||
report.valid?
|
||||
expect(report).to model_have_error_on_field(:comment)
|
||||
end
|
||||
|
@ -296,99 +296,6 @@ RSpec.describe Status, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe '.as_home_timeline' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:followed) { Fabricate(:account) }
|
||||
let(:not_followed) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
Fabricate(:follow, account: account, target_account: followed)
|
||||
|
||||
@self_status = Fabricate(:status, account: account, visibility: :public)
|
||||
@self_direct_status = Fabricate(:status, account: account, visibility: :direct)
|
||||
@followed_status = Fabricate(:status, account: followed, visibility: :public)
|
||||
@followed_direct_status = Fabricate(:status, account: followed, visibility: :direct)
|
||||
@not_followed_status = Fabricate(:status, account: not_followed, visibility: :public)
|
||||
|
||||
@results = Status.as_home_timeline(account)
|
||||
end
|
||||
|
||||
it 'includes statuses from self' do
|
||||
expect(@results).to include(@self_status)
|
||||
end
|
||||
|
||||
it 'does not include direct statuses from self' do
|
||||
expect(@results).to_not include(@self_direct_status)
|
||||
end
|
||||
|
||||
it 'includes statuses from followed' do
|
||||
expect(@results).to include(@followed_status)
|
||||
end
|
||||
|
||||
it 'does not include direct statuses mentioning recipient from followed' do
|
||||
Fabricate(:mention, account: account, status: @followed_direct_status)
|
||||
expect(@results).to_not include(@followed_direct_status)
|
||||
end
|
||||
|
||||
it 'does not include direct statuses not mentioning recipient from followed' do
|
||||
expect(@results).not_to include(@followed_direct_status)
|
||||
end
|
||||
|
||||
it 'does not include statuses from non-followed' do
|
||||
expect(@results).not_to include(@not_followed_status)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.as_direct_timeline' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:followed) { Fabricate(:account) }
|
||||
let(:not_followed) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
Fabricate(:follow, account: account, target_account: followed)
|
||||
|
||||
@self_public_status = Fabricate(:status, account: account, visibility: :public)
|
||||
@self_direct_status = Fabricate(:status, account: account, visibility: :direct)
|
||||
@followed_public_status = Fabricate(:status, account: followed, visibility: :public)
|
||||
@followed_direct_status = Fabricate(:status, account: followed, visibility: :direct)
|
||||
@not_followed_direct_status = Fabricate(:status, account: not_followed, visibility: :direct)
|
||||
|
||||
@results = Status.as_direct_timeline(account)
|
||||
end
|
||||
|
||||
it 'does not include public statuses from self' do
|
||||
expect(@results).to_not include(@self_public_status)
|
||||
end
|
||||
|
||||
it 'includes direct statuses from self' do
|
||||
expect(@results).to include(@self_direct_status)
|
||||
end
|
||||
|
||||
it 'does not include public statuses from followed' do
|
||||
expect(@results).to_not include(@followed_public_status)
|
||||
end
|
||||
|
||||
it 'does not include direct statuses not mentioning recipient from followed' do
|
||||
expect(@results).to_not include(@followed_direct_status)
|
||||
end
|
||||
|
||||
it 'does not include direct statuses not mentioning recipient from non-followed' do
|
||||
expect(@results).to_not include(@not_followed_direct_status)
|
||||
end
|
||||
|
||||
it 'includes direct statuses mentioning recipient from followed' do
|
||||
Fabricate(:mention, account: account, status: @followed_direct_status)
|
||||
results2 = Status.as_direct_timeline(account)
|
||||
expect(results2).to include(@followed_direct_status)
|
||||
end
|
||||
|
||||
it 'includes direct statuses mentioning recipient from non-followed' do
|
||||
Fabricate(:mention, account: account, status: @not_followed_direct_status)
|
||||
results2 = Status.as_direct_timeline(account)
|
||||
expect(results2).to include(@not_followed_direct_status)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.as_public_timeline' do
|
||||
it 'only includes statuses with public visibility' do
|
||||
public_status = Fabricate(:status, visibility: :public)
|
||||
|
@ -1,192 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe StreamEntry, type: :model do
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
let(:bob) { Fabricate(:account, username: 'bob') }
|
||||
let(:status) { Fabricate(:status, account: alice) }
|
||||
let(:reblog) { Fabricate(:status, account: bob, reblog: status) }
|
||||
let(:reply) { Fabricate(:status, account: bob, thread: status) }
|
||||
let(:stream_entry) { Fabricate(:stream_entry, activity: activity) }
|
||||
let(:activity) { reblog }
|
||||
|
||||
describe '#object_type' do
|
||||
before do
|
||||
allow(stream_entry).to receive(:orphaned?).and_return(orphaned)
|
||||
allow(stream_entry).to receive(:targeted?).and_return(targeted)
|
||||
end
|
||||
|
||||
subject { stream_entry.object_type }
|
||||
|
||||
context 'orphaned? is true' do
|
||||
let(:orphaned) { true }
|
||||
let(:targeted) { false }
|
||||
|
||||
it 'returns :activity' do
|
||||
is_expected.to be :activity
|
||||
end
|
||||
end
|
||||
|
||||
context 'targeted? is true' do
|
||||
let(:orphaned) { false }
|
||||
let(:targeted) { true }
|
||||
|
||||
it 'returns :activity' do
|
||||
is_expected.to be :activity
|
||||
end
|
||||
end
|
||||
|
||||
context 'orphaned? and targeted? are false' do
|
||||
let(:orphaned) { false }
|
||||
let(:targeted) { false }
|
||||
|
||||
context 'activity is reblog' do
|
||||
let(:activity) { reblog }
|
||||
|
||||
it 'returns :note' do
|
||||
is_expected.to be :note
|
||||
end
|
||||
end
|
||||
|
||||
context 'activity is reply' do
|
||||
let(:activity) { reply }
|
||||
|
||||
it 'returns :comment' do
|
||||
is_expected.to be :comment
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#verb' do
|
||||
before do
|
||||
allow(stream_entry).to receive(:orphaned?).and_return(orphaned)
|
||||
end
|
||||
|
||||
subject { stream_entry.verb }
|
||||
|
||||
context 'orphaned? is true' do
|
||||
let(:orphaned) { true }
|
||||
|
||||
it 'returns :delete' do
|
||||
is_expected.to be :delete
|
||||
end
|
||||
end
|
||||
|
||||
context 'orphaned? is false' do
|
||||
let(:orphaned) { false }
|
||||
|
||||
context 'activity is reblog' do
|
||||
let(:activity) { reblog }
|
||||
|
||||
it 'returns :share' do
|
||||
is_expected.to be :share
|
||||
end
|
||||
end
|
||||
|
||||
context 'activity is reply' do
|
||||
let(:activity) { reply }
|
||||
|
||||
it 'returns :post' do
|
||||
is_expected.to be :post
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#mentions' do
|
||||
before do
|
||||
allow(stream_entry).to receive(:orphaned?).and_return(orphaned)
|
||||
end
|
||||
|
||||
subject { stream_entry.mentions }
|
||||
|
||||
context 'orphaned? is true' do
|
||||
let(:orphaned) { true }
|
||||
|
||||
it 'returns []' do
|
||||
is_expected.to eq []
|
||||
end
|
||||
end
|
||||
|
||||
context 'orphaned? is false' do
|
||||
before do
|
||||
reblog.mentions << Fabricate(:mention, account: alice)
|
||||
reblog.mentions << Fabricate(:mention, account: bob)
|
||||
end
|
||||
|
||||
let(:orphaned) { false }
|
||||
|
||||
it 'returns [Account] includes alice and bob' do
|
||||
is_expected.to eq [alice, bob]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#targeted?' do
|
||||
it 'returns true for a reblog' do
|
||||
expect(reblog.stream_entry.targeted?).to be true
|
||||
end
|
||||
|
||||
it 'returns false otherwise' do
|
||||
expect(status.stream_entry.targeted?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#threaded?' do
|
||||
it 'returns true for a reply' do
|
||||
expect(reply.stream_entry.threaded?).to be true
|
||||
end
|
||||
|
||||
it 'returns false otherwise' do
|
||||
expect(status.stream_entry.threaded?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe 'delegated methods' do
|
||||
context 'with a nil status' do
|
||||
subject { described_class.new(status: nil) }
|
||||
|
||||
it 'returns nil for target' do
|
||||
expect(subject.target).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil for title' do
|
||||
expect(subject.title).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil for content' do
|
||||
expect(subject.content).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil for thread' do
|
||||
expect(subject.thread).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a real status' do
|
||||
let(:original) { Fabricate(:status, text: 'Test status') }
|
||||
let(:status) { Fabricate(:status, reblog: original, thread: original) }
|
||||
subject { described_class.new(status: status) }
|
||||
|
||||
it 'delegates target' do
|
||||
expect(status.target).not_to be_nil
|
||||
expect(subject.target).to eq(status.target)
|
||||
end
|
||||
|
||||
it 'delegates title' do
|
||||
expect(status.title).not_to be_nil
|
||||
expect(subject.title).to eq(status.title)
|
||||
end
|
||||
|
||||
it 'delegates content' do
|
||||
expect(status.content).not_to be_nil
|
||||
expect(subject.content).to eq(status.content)
|
||||
end
|
||||
|
||||
it 'delegates thread' do
|
||||
expect(status.thread).not_to be_nil
|
||||
expect(subject.thread).to eq(status.thread)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,67 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Subscription, type: :model do
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
|
||||
subject { Fabricate(:subscription, account: alice) }
|
||||
|
||||
describe '#expired?' do
|
||||
it 'return true when expires_at is past' do
|
||||
subject.expires_at = 2.days.ago
|
||||
expect(subject.expired?).to be true
|
||||
end
|
||||
|
||||
it 'return false when expires_at is future' do
|
||||
subject.expires_at = 2.days.from_now
|
||||
expect(subject.expired?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe 'lease_seconds' do
|
||||
it 'returns the time remaining until expiration' do
|
||||
datetime = 1.day.from_now
|
||||
subscription = Subscription.new(expires_at: datetime)
|
||||
travel_to(datetime - 12.hours) do
|
||||
expect(subscription.lease_seconds).to eq(12.hours)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'lease_seconds=' do
|
||||
it 'sets expires_at to min expiration when small value is provided' do
|
||||
subscription = Subscription.new
|
||||
datetime = 1.day.from_now
|
||||
too_low = Subscription::MIN_EXPIRATION - 1000
|
||||
travel_to(datetime) do
|
||||
subscription.lease_seconds = too_low
|
||||
end
|
||||
|
||||
expected = datetime + Subscription::MIN_EXPIRATION.seconds
|
||||
expect(subscription.expires_at).to be_within(1.0).of(expected)
|
||||
end
|
||||
|
||||
it 'sets expires_at to value when valid value is provided' do
|
||||
subscription = Subscription.new
|
||||
datetime = 1.day.from_now
|
||||
valid = Subscription::MIN_EXPIRATION + 1000
|
||||
travel_to(datetime) do
|
||||
subscription.lease_seconds = valid
|
||||
end
|
||||
|
||||
expected = datetime + valid.seconds
|
||||
expect(subscription.expires_at).to be_within(1.0).of(expected)
|
||||
end
|
||||
|
||||
it 'sets expires_at to max expiration when large value is provided' do
|
||||
subscription = Subscription.new
|
||||
datetime = 1.day.from_now
|
||||
too_high = Subscription::MAX_EXPIRATION + 1000
|
||||
travel_to(datetime) do
|
||||
subscription.lease_seconds = too_high
|
||||
end
|
||||
|
||||
expected = datetime + Subscription::MAX_EXPIRATION.seconds
|
||||
expect(subscription.expires_at).to be_within(1.0).of(expected)
|
||||
end
|
||||
end
|
||||
end
|
@ -62,6 +62,10 @@ RSpec.describe Tag, type: :model do
|
||||
expect(subject.match('hello #one·two·three').to_s).to eq ' #one·two·three'
|
||||
end
|
||||
|
||||
it 'matches ZWNJ' do
|
||||
expect(subject.match('just add #نرمافزار and').to_s).to eq ' #نرمافزار'
|
||||
end
|
||||
|
||||
it 'does not match middle dots at the start' do
|
||||
expect(subject.match('hello #·one·two·three')).to be_nil
|
||||
end
|
||||
@ -82,6 +86,40 @@ RSpec.describe Tag, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe '.find_normalized' do
|
||||
it 'returns tag for a multibyte case-insensitive name' do
|
||||
upcase_string = 'abcABCabcABCやゆよ'
|
||||
downcase_string = 'abcabcabcabcやゆよ';
|
||||
|
||||
tag = Fabricate(:tag, name: downcase_string)
|
||||
expect(Tag.find_normalized(upcase_string)).to eq tag
|
||||
end
|
||||
end
|
||||
|
||||
describe '.matching_name' do
|
||||
it 'returns tags for multibyte case-insensitive names' do
|
||||
upcase_string = 'abcABCabcABCやゆよ'
|
||||
downcase_string = 'abcabcabcabcやゆよ';
|
||||
|
||||
tag = Fabricate(:tag, name: downcase_string)
|
||||
expect(Tag.matching_name(upcase_string)).to eq [tag]
|
||||
end
|
||||
end
|
||||
|
||||
describe '.find_or_create_by_names' do
|
||||
it 'runs a passed block once per tag regardless of duplicates' do
|
||||
upcase_string = 'abcABCabcABCやゆよ'
|
||||
downcase_string = 'abcabcabcabcやゆよ';
|
||||
count = 0
|
||||
|
||||
Tag.find_or_create_by_names([upcase_string, downcase_string]) do |tag|
|
||||
count += 1
|
||||
end
|
||||
|
||||
expect(count).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
describe '.search_for' do
|
||||
it 'finds tag records with matching names' do
|
||||
tag = Fabricate(:tag, name: "match")
|
||||
@ -102,8 +140,8 @@ RSpec.describe Tag, type: :model do
|
||||
end
|
||||
|
||||
it 'finds the exact matching tag as the first item' do
|
||||
similar_tag = Fabricate(:tag, name: "matchlater")
|
||||
tag = Fabricate(:tag, name: "match")
|
||||
similar_tag = Fabricate(:tag, name: "matchlater", reviewed_at: Time.now.utc)
|
||||
tag = Fabricate(:tag, name: "match", reviewed_at: Time.now.utc)
|
||||
|
||||
results = Tag.search_for("match")
|
||||
|
||||
|
68
spec/models/trending_tags_spec.rb
Normal file
68
spec/models/trending_tags_spec.rb
Normal file
@ -0,0 +1,68 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe TrendingTags do
|
||||
describe '.record_use!' do
|
||||
pending
|
||||
end
|
||||
|
||||
describe '.update!' do
|
||||
let!(:at_time) { Time.now.utc }
|
||||
let!(:tag1) { Fabricate(:tag, name: 'Catstodon') }
|
||||
let!(:tag2) { Fabricate(:tag, name: 'DogsOfMastodon') }
|
||||
let!(:tag3) { Fabricate(:tag, name: 'OCs') }
|
||||
|
||||
before do
|
||||
allow(Redis.current).to receive(:pfcount) do |key|
|
||||
case key
|
||||
when "activity:tags:#{tag1.id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts"
|
||||
2
|
||||
when "activity:tags:#{tag1.id}:#{at_time.beginning_of_day.to_i}:accounts"
|
||||
16
|
||||
when "activity:tags:#{tag2.id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts"
|
||||
0
|
||||
when "activity:tags:#{tag2.id}:#{at_time.beginning_of_day.to_i}:accounts"
|
||||
4
|
||||
when "activity:tags:#{tag3.id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts"
|
||||
13
|
||||
end
|
||||
end
|
||||
|
||||
Redis.current.zadd('trending_tags', 0.9, tag3.id)
|
||||
Redis.current.sadd("trending_tags:used:#{at_time.beginning_of_day.to_i}", [tag1.id, tag2.id])
|
||||
|
||||
tag3.update(max_score: 0.9, max_score_at: (at_time - 1.day).beginning_of_day + 12.hours)
|
||||
|
||||
described_class.update!(at_time)
|
||||
end
|
||||
|
||||
it 'calculates and re-calculates scores' do
|
||||
expect(described_class.get(10, filtered: false)).to eq [tag1, tag3]
|
||||
end
|
||||
|
||||
it 'omits hashtags below threshold' do
|
||||
expect(described_class.get(10, filtered: false)).to_not include(tag2)
|
||||
end
|
||||
|
||||
it 'decays scores' do
|
||||
expect(Redis.current.zscore('trending_tags', tag3.id)).to be < 0.9
|
||||
end
|
||||
end
|
||||
|
||||
describe '.trending?' do
|
||||
let(:tag) { Fabricate(:tag) }
|
||||
|
||||
before do
|
||||
10.times { |i| Redis.current.zadd('trending_tags', i + 1, Fabricate(:tag).id) }
|
||||
end
|
||||
|
||||
it 'returns true if the hashtag is within limit' do
|
||||
Redis.current.zadd('trending_tags', 11, tag.id)
|
||||
expect(described_class.trending?(tag)).to be true
|
||||
end
|
||||
|
||||
it 'returns false if the hashtag is outside the limit' do
|
||||
Redis.current.zadd('trending_tags', 0, tag.id)
|
||||
expect(described_class.trending?(tag)).to be false
|
||||
end
|
||||
end
|
||||
end
|
@ -506,7 +506,7 @@ RSpec.describe User, type: :model do
|
||||
context 'when user is not confirmed' do
|
||||
let(:confirmed_at) { nil }
|
||||
|
||||
it { is_expected.to be false }
|
||||
it { is_expected.to be true }
|
||||
end
|
||||
end
|
||||
|
||||
@ -522,7 +522,7 @@ RSpec.describe User, type: :model do
|
||||
context 'when user is not confirmed' do
|
||||
let(:confirmed_at) { nil }
|
||||
|
||||
it { is_expected.to be false }
|
||||
it { is_expected.to be true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Reference in New Issue
Block a user