require 'spec_helper' describe TopicUser do it { should belong_to :user } it { should belong_to :topic } let!(:yesterday) { DateTime.now.yesterday } before do DateTime.expects(:now).at_least_once.returns(yesterday) end let!(:topic) { Fabricate(:topic) } let!(:user) { Fabricate(:coding_horror) } let(:topic_user) { TopicUser.get(topic,user) } let(:topic_creator_user) { TopicUser.get(topic, topic.user) } let(:post) { Fabricate(:post, topic: topic, user: user) } let(:new_user) { Fabricate(:user, auto_track_topics_after_msecs: 1000) } let(:topic_new_user) { TopicUser.get(topic, new_user)} describe "unpinned" do before do TopicUser.change(user, topic, {starred_at: yesterday}) end it "defaults to blank" do topic_user.cleared_pinned_at.should be_blank end end describe 'notifications' do it 'should be set to tracking if auto_track_topics is enabled' do user.update_column(:auto_track_topics_after_msecs, 0) TopicUser.change(user, topic, {starred_at: yesterday}) TopicUser.get(topic, user).notification_level.should == TopicUser.notification_levels[:tracking] end it 'should reset regular topics to tracking topics if auto track is changed' do TopicUser.change(user, topic, {starred_at: yesterday}) user.auto_track_topics_after_msecs = 0 user.save topic_user.notification_level.should == TopicUser.notification_levels[:tracking] end it 'should be set to "regular" notifications, by default on non creators' do TopicUser.change(user, topic, {starred_at: yesterday}) TopicUser.get(topic,user).notification_level.should == TopicUser.notification_levels[:regular] end it 'reason should reset when changed' do topic.notify_muted!(topic.user) TopicUser.get(topic,topic.user).notifications_reason_id.should == TopicUser.notification_reasons[:user_changed] end it 'should have the correct reason for a user change when watched' do topic.notify_watch!(user) topic_user.notification_level.should == TopicUser.notification_levels[:watching] topic_user.notifications_reason_id.should == TopicUser.notification_reasons[:user_changed] topic_user.notifications_changed_at.should_not be_nil end it 'should have the correct reason for a user change when set to regular' do topic.notify_regular!(user) topic_user.notification_level.should == TopicUser.notification_levels[:regular] topic_user.notifications_reason_id.should == TopicUser.notification_reasons[:user_changed] topic_user.notifications_changed_at.should_not be_nil end it 'should have the correct reason for a user change when set to regular' do topic.notify_muted!(user) topic_user.notification_level.should == TopicUser.notification_levels[:muted] topic_user.notifications_reason_id.should == TopicUser.notification_reasons[:user_changed] topic_user.notifications_changed_at.should_not be_nil end it 'should watch topics a user created' do topic_creator_user.notification_level.should == TopicUser.notification_levels[:watching] topic_creator_user.notifications_reason_id.should == TopicUser.notification_reasons[:created_topic] end end describe 'visited at' do before do TopicUser.track_visit!(topic, user) end it 'set upon initial visit' do topic_user.first_visited_at.to_i.should == yesterday.to_i topic_user.last_visited_at.to_i.should == yesterday.to_i end it 'updates upon repeat visit' do today = yesterday.tomorrow DateTime.expects(:now).returns(today) TopicUser.track_visit!(topic,user) # reload is a no go topic_user = TopicUser.get(topic,user) topic_user.first_visited_at.to_i.should == yesterday.to_i topic_user.last_visited_at.to_i.should == today.to_i end it 'triggers the observer callbacks when updating' do UserActionObserver.instance.expects(:after_save).twice 2.times { TopicUser.track_visit!(topic, user) } end end describe 'read tracking' do context "without auto tracking" do before do TopicUser.update_last_read(user, topic.id, 1, 0) end let(:topic_user) { TopicUser.get(topic,user) } it 'should create a new record for a visit' do topic_user.last_read_post_number.should == 1 topic_user.last_visited_at.to_i.should == yesterday.to_i topic_user.first_visited_at.to_i.should == yesterday.to_i end it 'should update the record for repeat visit' do Fabricate(:post, topic: topic, user: user) TopicUser.update_last_read(user, topic.id, 2, 0) topic_user = TopicUser.get(topic,user) topic_user.last_read_post_number.should == 2 topic_user.last_visited_at.to_i.should == yesterday.to_i topic_user.first_visited_at.to_i.should == yesterday.to_i end end context 'auto tracking' do let(:post_creator) { PostCreator.new(new_user, raw: Fabricate.build(:post).raw, topic_id: topic.id) } before do TopicUser.update_last_read(new_user, topic.id, 2, 0) end it 'should automatically track topics you reply to' do post_creator.create topic_new_user.notification_level.should == TopicUser.notification_levels[:tracking] topic_new_user.notifications_reason_id.should == TopicUser.notification_reasons[:created_post] end it 'should not automatically track topics you reply to and have set state manually' do post_creator.create TopicUser.change(new_user, topic, notification_level: TopicUser.notification_levels[:regular]) topic_new_user.notification_level.should == TopicUser.notification_levels[:regular] topic_new_user.notifications_reason_id.should == TopicUser.notification_reasons[:user_changed] end it 'should automatically track topics after they are read for long enough' do topic_new_user.notification_level.should ==TopicUser.notification_levels[:regular] TopicUser.update_last_read(new_user, topic.id, 2, 1001) TopicUser.get(topic, new_user).notification_level.should == TopicUser.notification_levels[:tracking] end it 'should not automatically track topics after they are read for long enough if changed manually' do TopicUser.change(new_user, topic, notification_level: TopicUser.notification_levels[:regular]) TopicUser.update_last_read(new_user, topic, 2, 1001) topic_new_user.notification_level.should == TopicUser.notification_levels[:regular] end end end describe 'change a flag' do it 'creates a forum topic user record' do lambda { TopicUser.change(user, topic.id, starred: true) }.should change(TopicUser, :count).by(1) end it "only inserts a row once, even on repeated calls" do lambda { TopicUser.change(user, topic.id, starred: true) TopicUser.change(user, topic.id, starred: false) TopicUser.change(user, topic.id, starred: true) }.should change(TopicUser, :count).by(1) end it 'triggers the observer callbacks when updating' do UserActionObserver.instance.expects(:after_save).twice 3.times { TopicUser.change(user, topic.id, starred: true) } end describe 'after creating a row' do before do TopicUser.change(user, topic.id, starred: true) end it 'has the correct starred value' do TopicUser.get(topic, user).should be_starred end it 'has a lookup' do TopicUser.lookup_for(user, [topic]).should be_present end it 'has a key in the lookup for this forum topic' do TopicUser.lookup_for(user, [topic]).has_key?(topic.id).should be_true end end end it "can scope by tracking" do TopicUser.create!(user_id: 1, topic_id: 1, notification_level: TopicUser.notification_levels[:tracking]) TopicUser.create!(user_id: 2, topic_id: 1, notification_level: TopicUser.notification_levels[:watching]) TopicUser.create!(user_id: 3, topic_id: 1, notification_level: TopicUser.notification_levels[:regular]) TopicUser.tracking(1).count.should == 2 TopicUser.tracking(10).count.should == 0 end it "is able to self heal" do p1 = Fabricate(:post) p2 = Fabricate(:post, user: p1.user, topic: p1.topic, post_number: 2) TopicUser.exec_sql("UPDATE topic_users set seen_post_count=100, last_read_post_number=0 WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: p1.topic_id, user_id: p1.user_id) [p1,p2].each do |p| PostTiming.create(topic_id: p.topic_id, post_number: p.post_number, user_id: p.user_id, msecs: 100) end TopicUser.ensure_consistency! tu = TopicUser.where(user_id: p1.user_id, topic_id: p1.topic_id).first tu.last_read_post_number.should == p2.post_number tu.seen_post_count.should == 2 end end