Merge tag 'v3.0.0' into hometown-dev

This commit is contained in:
Darius Kazemi
2019-10-08 13:24:20 -07:00
1012 changed files with 31176 additions and 15165 deletions

View File

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe AccountAlias, type: :model do
end

View File

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe AccountMigration, type: :model do
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe CustomEmojiCategory, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end

View File

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe DomainAllow, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end

View File

@ -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

View File

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe Marker, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -339,56 +339,6 @@ RSpec.describe Status, type: :model do
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)

View File

@ -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

View File

@ -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

View File

@ -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 = 'abcABCやゆよ'
downcase_string = 'abcabcやゆよ';
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 = 'abcABCやゆよ'
downcase_string = 'abcabcやゆよ';
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 = 'abcABCやゆよ'
downcase_string = 'abcabcやゆよ';
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")

View 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

View File

@ -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