# frozen_string_literal: true require 'rails_helper' describe DiscourseVoting do let(:user0) { Fabricate(:user) } let(:user1) { Fabricate(:user) } let(:user2) { Fabricate(:user) } let(:user3) { Fabricate(:user) } let(:user4) { Fabricate(:user) } let(:category1) { Fabricate(:category) } let(:category2) { Fabricate(:category) } let(:topic0) { Fabricate(:topic, category: category1) } let(:topic1) { Fabricate(:topic, category: category2) } before do SiteSetting.voting_enabled = true SiteSetting.voting_show_who_voted = true end it "doesn't allow users to vote more than they are allowed" do SiteSetting.voting_tl1_vote_limit = 1 user0.update!(trust_level: 1) expect(user0.reached_voting_limit?).to eq(false) user0.custom_fields["votes"] = [topic0.id.to_s] user0.save! expect(user0.reached_voting_limit?).to eq(true) end context "with two topics" do let(:users) { [user0, user1, user2, user3, user4] } before do Fabricate(:post, topic: topic0, user: user0) Fabricate(:post, topic: topic0, user: user0) # +user0+ votes +topic0+, +user1+ votes +topic1+ and +user2+ votes both # topics. users[0].custom_fields[DiscourseVoting::VOTES] = (users[0].custom_fields[DiscourseVoting::VOTES].dup || []).push(topic0.id.to_s) users[1].custom_fields[DiscourseVoting::VOTES] = (users[1].custom_fields[DiscourseVoting::VOTES].dup || []).push(topic1.id.to_s) users[2].custom_fields[DiscourseVoting::VOTES] = (users[2].custom_fields[DiscourseVoting::VOTES].dup || []).push(topic0.id.to_s) users[2].custom_fields[DiscourseVoting::VOTES] = (users[2].custom_fields[DiscourseVoting::VOTES].dup || []).push(topic1.id.to_s) users[4].custom_fields[DiscourseVoting::VOTES_ARCHIVE] = (users[4].custom_fields[DiscourseVoting::VOTES_ARCHIVE].dup || []).push(topic0.id.to_s) users.each { |u| u.save! } [topic0, topic1].each { |t| t.update_vote_count } end it 'moves votes when entire topic is merged' do topic0.move_posts(Discourse.system_user, topic0.posts.pluck(:id), destination_topic_id: topic1.id) users.each { |user| user.reload } expect(users[0].votes).to contain_exactly(topic1.id) expect(users[0].votes_archive).to contain_exactly() expect(users[1].votes).to contain_exactly(topic1.id) expect(users[1].votes_archive).to contain_exactly() expect(users[2].votes).to contain_exactly(topic1.id) expect(users[2].votes_archive).to contain_exactly() expect(users[3].votes).to contain_exactly() expect(users[3].votes_archive).to contain_exactly() expect(users[4].votes).to contain_exactly() expect(users[4].votes_archive).to contain_exactly(topic1.id) expect(topic0.reload.vote_count).to eq(0) expect(topic1.reload.vote_count).to eq(4) merged_post = topic0.posts.find_by(action_code: 'split_topic') expect(merged_post.raw).to include(I18n.t('voting.votes_moved', count: 2)) expect(merged_post.raw).to include(I18n.t('voting.duplicated_votes', count: 1)) end it 'does not move votes when a single post is moved' do topic0.move_posts(Discourse.system_user, topic0.posts[1, 2].map(&:id), destination_topic_id: topic1.id) users.each { |user| user.reload } expect(users[0].votes).to contain_exactly(topic0.id) expect(users[0].votes_archive).to contain_exactly() expect(users[1].votes).to contain_exactly(topic1.id) expect(users[1].votes_archive).to contain_exactly() expect(users[2].votes).to contain_exactly(topic0.id, topic1.id) expect(users[2].votes_archive).to contain_exactly() expect(users[3].votes).to contain_exactly() expect(users[3].votes_archive).to contain_exactly() expect(users[4].votes).to contain_exactly() expect(users[4].votes_archive).to contain_exactly(topic0.id) expect(topic0.reload.vote_count).to eq(3) expect(topic1.reload.vote_count).to eq(2) end end context "when a user has an empty string as the votes custom field" do before do user0.custom_fields[DiscourseVoting::VOTES] = "" user0.custom_fields[DiscourseVoting::VOTES_ARCHIVE] = "" user0.save user0.reload end it "returns a vote count of zero" do expect(user0.vote_count).to eq (0) expect(user0.votes_archive).to eq ([]) end end context "when topic status is changed" do it "enqueues a job for releasing/reclaiming votes" do DiscourseEvent.on(:topic_status_updated) do |topic| expect(topic).to be_instance_of(Topic) end topic1.update_status('closed', true, Discourse.system_user) expect(Jobs::VoteRelease.jobs.first["args"].first["topic_id"]).to eq(topic1.id) topic1.update_status('closed', false, Discourse.system_user) expect(Jobs::VoteReclaim.jobs.first["args"].first["topic_id"]).to eq(topic1.id) end end context "when a job is trashed and then recovered" do it "released the vote back to the user, then reclaims it on topic recovery" do Jobs.run_immediately! user0.custom_fields[DiscourseVoting::VOTES] = [topic1.id] user0.save topic1.reload.trash! expect(user0.reload.votes).to eq([]) topic1.recover! expect(user0.reload.votes).to eq([topic1.id]) end end context "when a topic is moved to a category" do let(:admin) { Fabricate(:admin) } let(:post0) { Fabricate(:post, topic: topic0, post_number: 1) } let(:post1) { Fabricate(:post, topic: topic1, post_number: 1) } before do category1.custom_fields["enable_topic_voting"] = "true" category1.save! Category.reset_voting_cache end it "enqueus a job to reclaim votes if voting is enabled for the new category" do user = post1.user user.custom_fields[DiscourseVoting::VOTES_ARCHIVE] = [post1.topic_id, 456456] user.save! PostRevisor.new(post1).revise!(admin, category_id: category1.id) expect(Jobs::VoteReclaim.jobs.first["args"].first["topic_id"]).to eq(post1.reload.topic_id) Jobs::VoteReclaim.new.execute(topic_id: post1.topic_id) user.reload expect(user.votes).to eq([post1.topic_id]) expect(user.votes_archive).to eq([456456]) end it "enqueus a job to release votes if voting is disabled for the new category" do user = post0.user user.custom_fields[DiscourseVoting::VOTES] = [post0.topic_id, 456456] user.save! PostRevisor.new(post0).revise!(admin, category_id: category2.id) expect(Jobs::VoteRelease.jobs.first["args"].first["topic_id"]).to eq(post0.reload.topic_id) Jobs::VoteRelease.new.execute(topic_id: post0.topic_id) user.reload expect(user.votes_archive).to eq([post0.topic_id]) expect(user.votes).to eq([456456]) end it "doesn't enqueue a job if the topic has no votes" do PostRevisor.new(post0).revise!(admin, category_id: category2.id) expect(Jobs::VoteRelease.jobs.size).to eq(0) PostRevisor.new(post1).revise!(admin, category_id: category1.id) expect(Jobs::VoteReclaim.jobs.size).to eq(0) end end context "when a category has voting enabled/disabled" do let(:category3) { Fabricate(:category) } let(:topic2) { Fabricate(:topic, category: category3) } before do category1.custom_fields["enable_topic_voting"] = true category1.save! category2.custom_fields["enable_topic_voting"] = true category2.save! category3.custom_fields["enable_topic_voting"] = false category3.save! user0.custom_fields[DiscourseVoting::VOTES] = [topic0.id, topic1.id] user0.custom_fields[DiscourseVoting::VOTES_ARCHIVE] = [topic2.id] user0.save! end it "reclaims votes when voting is disabled on a category" do category = Category.find(category1.id) category.custom_fields["enable_topic_voting"] = false category.save! user0.reload expect(user0.custom_fields[DiscourseVoting::VOTES]).to contain_exactly(topic1.id) expect(user0.custom_fields[DiscourseVoting::VOTES_ARCHIVE]).to contain_exactly(topic0.id, topic2.id) end it "restores votes when voting is enabled on a category" do category = Category.find(category3.id) category.custom_fields["enable_topic_voting"] = true category.save! user0.reload expect(user0.custom_fields[DiscourseVoting::VOTES]).to contain_exactly(topic0.id, topic1.id, topic2.id) expect(user0.custom_fields[DiscourseVoting::VOTES_ARCHIVE]).to eq(nil) end end end