2019-04-30 08:27:42 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-10-11 17:41:23 +08:00
|
|
|
require 'rails_helper'
|
2013-09-12 02:50:26 +08:00
|
|
|
|
|
|
|
describe UserStat do
|
|
|
|
|
|
|
|
it "is created automatically when a user is created" do
|
|
|
|
user = Fabricate(:evil_trout)
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(user.user_stat).to be_present
|
2014-03-04 03:31:29 +08:00
|
|
|
|
|
|
|
# It populates the `new_since` field by default
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(user.user_stat.new_since).to be_present
|
2013-09-12 02:50:26 +08:00
|
|
|
end
|
|
|
|
|
2013-10-04 11:28:49 +08:00
|
|
|
context '#update_view_counts' do
|
|
|
|
|
|
|
|
let(:user) { Fabricate(:user) }
|
|
|
|
let(:stat) { user.user_stat }
|
|
|
|
|
|
|
|
context 'topics_entered' do
|
|
|
|
context 'without any views' do
|
|
|
|
it "doesn't increase the user's topics_entered" do
|
2014-12-31 22:55:03 +08:00
|
|
|
expect { UserStat.update_view_counts; stat.reload }.not_to change(stat, :topics_entered)
|
2013-10-04 11:28:49 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with a view' do
|
2019-05-07 11:12:20 +08:00
|
|
|
fab!(:topic) { Fabricate(:topic) }
|
2014-08-04 17:07:55 +08:00
|
|
|
let!(:view) { TopicViewItem.add(topic.id, '127.0.0.1', user.id) }
|
2013-10-04 11:28:49 +08:00
|
|
|
|
|
|
|
before do
|
|
|
|
user.update_column :last_seen_at, 1.second.ago
|
|
|
|
end
|
|
|
|
|
|
|
|
it "adds one to the topics entered" do
|
|
|
|
UserStat.update_view_counts
|
|
|
|
stat.reload
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(stat.topics_entered).to eq(1)
|
2013-10-04 11:28:49 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it "won't record a second view as a different topic" do
|
2014-08-04 17:07:55 +08:00
|
|
|
TopicViewItem.add(topic.id, '127.0.0.1', user.id)
|
2013-10-04 11:28:49 +08:00
|
|
|
UserStat.update_view_counts
|
|
|
|
stat.reload
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(stat.topics_entered).to eq(1)
|
2013-10-04 11:28:49 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'posts_read_count' do
|
|
|
|
context 'without any post timings' do
|
|
|
|
it "doesn't increase the user's posts_read_count" do
|
2014-12-31 22:55:03 +08:00
|
|
|
expect { UserStat.update_view_counts; stat.reload }.not_to change(stat, :posts_read_count)
|
2013-10-04 11:28:49 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with a post timing' do
|
|
|
|
let!(:post) { Fabricate(:post) }
|
|
|
|
let!(:post_timings) do
|
|
|
|
PostTiming.record_timing(msecs: 1234, topic_id: post.topic_id, user_id: user.id, post_number: post.post_number)
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
user.update_column :last_seen_at, 1.second.ago
|
|
|
|
end
|
|
|
|
|
|
|
|
it "increases posts_read_count" do
|
|
|
|
UserStat.update_view_counts
|
|
|
|
stat.reload
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(stat.posts_read_count).to eq(1)
|
2013-10-04 11:28:49 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-04-05 09:44:36 +08:00
|
|
|
describe 'ensure consistency!' do
|
|
|
|
it 'can update first unread' do
|
|
|
|
post = create_post
|
|
|
|
|
|
|
|
freeze_time 10.minutes.from_now
|
|
|
|
create_post(topic_id: post.topic_id)
|
|
|
|
|
|
|
|
post.user.update!(last_seen_at: Time.now)
|
|
|
|
|
|
|
|
UserStat.ensure_consistency!
|
|
|
|
|
|
|
|
post.user.user_stat.reload
|
2020-03-11 05:13:17 +08:00
|
|
|
expect(post.user.user_stat.first_unread_at).to eq_time(Time.zone.now)
|
2019-04-05 09:44:36 +08:00
|
|
|
end
|
2020-09-03 14:02:15 +08:00
|
|
|
|
|
|
|
it 'updates first unread pm timestamp correctly' do
|
|
|
|
pm_topic = Fabricate(:private_message_topic)
|
|
|
|
user = pm_topic.user
|
|
|
|
user.update!(last_seen_at: Time.zone.now)
|
|
|
|
create_post(user: user, topic_id: pm_topic.id)
|
|
|
|
|
|
|
|
TopicUser.change(user.id, pm_topic.id,
|
|
|
|
notification_level: TopicUser.notification_levels[:tracking]
|
|
|
|
)
|
|
|
|
|
|
|
|
# user that is not tracking PM topic
|
|
|
|
user_2 = Fabricate(:user, last_seen_at: Time.zone.now)
|
|
|
|
pm_topic.allowed_users << user_2
|
|
|
|
|
|
|
|
TopicUser.change(user_2.id, pm_topic.id,
|
|
|
|
notification_level: TopicUser.notification_levels[:regular]
|
|
|
|
)
|
|
|
|
|
|
|
|
# User that has not been seen
|
|
|
|
user_3 = Fabricate(:user)
|
|
|
|
pm_topic.allowed_users << user_3
|
|
|
|
|
|
|
|
TopicUser.change(user_3.id, pm_topic.id,
|
|
|
|
notification_level: TopicUser.notification_levels[:tracking]
|
|
|
|
)
|
|
|
|
|
|
|
|
freeze_time 10.minutes.from_now
|
|
|
|
|
|
|
|
post = create_post(
|
|
|
|
user: Fabricate(:admin),
|
|
|
|
topic_id: pm_topic.id
|
|
|
|
)
|
|
|
|
|
|
|
|
UserStat.ensure_consistency!
|
|
|
|
|
|
|
|
expect(user.user_stat.reload.first_unread_pm_at).to eq_time(post.topic.updated_at)
|
|
|
|
expect(user_2.user_stat.reload.first_unread_pm_at).to_not eq_time(post.topic.updated_at)
|
|
|
|
expect(user_3.user_stat.reload.first_unread_pm_at).to_not eq_time(post.topic.updated_at)
|
|
|
|
end
|
2019-04-05 09:44:36 +08:00
|
|
|
end
|
|
|
|
|
2013-10-04 11:28:49 +08:00
|
|
|
describe 'update_time_read!' do
|
2019-05-07 11:12:20 +08:00
|
|
|
fab!(:user) { Fabricate(:user) }
|
2013-10-04 11:28:49 +08:00
|
|
|
let(:stat) { user.user_stat }
|
|
|
|
|
2018-12-03 05:35:09 +08:00
|
|
|
it 'always expires redis key' do
|
|
|
|
# this tests implementation which is not 100% ideal
|
|
|
|
# that said, redis key leaks are not good
|
|
|
|
stat.update_time_read!
|
2019-12-03 17:05:53 +08:00
|
|
|
ttl = Discourse.redis.ttl(UserStat.last_seen_key(user.id))
|
2018-12-03 05:35:09 +08:00
|
|
|
expect(ttl).to be > 0
|
|
|
|
expect(ttl).to be <= UserStat::MAX_TIME_READ_DIFF
|
|
|
|
end
|
|
|
|
|
2013-10-04 11:28:49 +08:00
|
|
|
it 'makes no changes if nothing is cached' do
|
2019-12-03 17:05:53 +08:00
|
|
|
Discourse.redis.del(UserStat.last_seen_key(user.id))
|
2013-10-04 11:28:49 +08:00
|
|
|
stat.update_time_read!
|
|
|
|
stat.reload
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(stat.time_read).to eq(0)
|
2013-10-04 11:28:49 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'makes a change if time read is below threshold' do
|
2018-01-17 12:49:35 +08:00
|
|
|
freeze_time
|
|
|
|
UserStat.cache_last_seen(user.id, (Time.now - 10).to_f)
|
2013-10-04 11:28:49 +08:00
|
|
|
stat.update_time_read!
|
|
|
|
stat.reload
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(stat.time_read).to eq(10)
|
2013-10-04 11:28:49 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'makes no change if time read is above threshold' do
|
2018-01-17 12:49:35 +08:00
|
|
|
freeze_time
|
|
|
|
|
2013-10-04 11:28:49 +08:00
|
|
|
t = Time.now - 1 - UserStat::MAX_TIME_READ_DIFF
|
2018-01-17 12:49:35 +08:00
|
|
|
UserStat.cache_last_seen(user.id, t.to_f)
|
|
|
|
|
2013-10-04 11:28:49 +08:00
|
|
|
stat.update_time_read!
|
|
|
|
stat.reload
|
2014-12-31 22:55:03 +08:00
|
|
|
expect(stat.time_read).to eq(0)
|
2013-10-04 11:28:49 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
2019-12-30 19:19:59 +08:00
|
|
|
|
|
|
|
describe 'update_distinct_badge_count' do
|
|
|
|
fab!(:user) { Fabricate(:user) }
|
|
|
|
let(:stat) { user.user_stat }
|
|
|
|
fab!(:badge1) { Fabricate(:badge) }
|
|
|
|
fab!(:badge2) { Fabricate(:badge) }
|
|
|
|
|
|
|
|
it "updates counts correctly" do
|
|
|
|
expect(stat.reload.distinct_badge_count).to eq(0)
|
|
|
|
BadgeGranter.grant(badge1, user)
|
|
|
|
expect(stat.reload.distinct_badge_count).to eq(1)
|
|
|
|
BadgeGranter.grant(badge1, user)
|
|
|
|
expect(stat.reload.distinct_badge_count).to eq(1)
|
|
|
|
BadgeGranter.grant(badge2, user)
|
|
|
|
expect(stat.reload.distinct_badge_count).to eq(2)
|
|
|
|
user.reload.user_badges.destroy_all
|
|
|
|
expect(stat.reload.distinct_badge_count).to eq(0)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "can update counts for all users simultaneously" do
|
|
|
|
user2 = Fabricate(:user)
|
|
|
|
stat2 = user2.user_stat
|
|
|
|
|
|
|
|
BadgeGranter.grant(badge1, user)
|
|
|
|
BadgeGranter.grant(badge1, user)
|
|
|
|
BadgeGranter.grant(badge2, user)
|
|
|
|
|
|
|
|
BadgeGranter.grant(badge1, user2)
|
|
|
|
|
|
|
|
# Set some clearly incorrect values
|
|
|
|
stat.update(distinct_badge_count: 999)
|
|
|
|
stat2.update(distinct_badge_count: 999)
|
|
|
|
|
|
|
|
UserStat.ensure_consistency!
|
|
|
|
|
|
|
|
expect(stat.reload.distinct_badge_count).to eq(2)
|
|
|
|
expect(stat2.reload.distinct_badge_count).to eq(1)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "ignores disabled badges" do
|
|
|
|
BadgeGranter.grant(badge1, user)
|
|
|
|
BadgeGranter.grant(badge2, user)
|
|
|
|
expect(stat.reload.distinct_badge_count).to eq(2)
|
|
|
|
|
|
|
|
badge2.update(enabled: false)
|
|
|
|
expect(stat.reload.distinct_badge_count).to eq(1)
|
|
|
|
|
|
|
|
badge2.update(enabled: true)
|
|
|
|
expect(stat.reload.distinct_badge_count).to eq(2)
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
2013-10-04 11:28:49 +08:00
|
|
|
end
|