mirror of
https://github.com/discourse/discourse.git
synced 2026-08-09 21:45:25 +08:00
Previously, opening the "who voted" popup on a closed topic returned an empty list even though the vote count still showed the correct total. The same was true for any topic whose votes had been archived (e.g. moved out of a voting category, or trashed). This was a regression from #39394, which added `votes.active` to `Topic#who_voted` alongside the same filter applied to "my votes" and `/topics/voted-by/:username`. That filter is correct for those per-user listings — you don't want closed-topic votes polluting "your votes" — but the `who_voted` popup is about historical participation on a specific topic, not per-user vote accounting. This change drops `.active` from `Topic#who_voted` so archived votes are included, restoring the pre-regression behaviour. The accounting filters in `Votes::Remove`, `User#topics_with_active_vote`, the voted-topics query, and the `current_user_voted` flag are unchanged. https://meta.discourse.org/t/403286 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
200 lines
6.7 KiB
Ruby
Vendored
200 lines
6.7 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
describe DiscourseTopicVoting::VotesController do
|
|
let(:user) { Fabricate(:user) }
|
|
let(:category) { Fabricate(:category) }
|
|
let(:topic) { Fabricate(:topic, category_id: category.id) }
|
|
|
|
before do
|
|
DiscourseTopicVoting::CategorySetting.create!(category:)
|
|
Category.reset_voting_cache
|
|
SiteSetting.topic_voting_show_who_voted = true
|
|
SiteSetting.topic_voting_enabled = true
|
|
sign_in(user)
|
|
end
|
|
|
|
it "does not allow voting if voting is not enabled" do
|
|
SiteSetting.topic_voting_enabled = false
|
|
post "/voting/vote.json", params: { topic_id: topic.id }
|
|
expect(response.status).to eq(404)
|
|
end
|
|
|
|
it "returns not found for a stale topic id when voting" do
|
|
topic.destroy
|
|
post "/voting/vote.json", params: { topic_id: topic.id }
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
|
|
it "can correctly show deal with voting workflow" do
|
|
SiteSetting.public_send "topic_voting_tl#{user.trust_level}_vote_limit=", 2
|
|
|
|
post "/voting/vote.json", params: { topic_id: topic.id }
|
|
expect(response.status).to eq(200)
|
|
|
|
post "/voting/vote.json", params: { topic_id: topic.id }
|
|
expect(response.status).to eq(403)
|
|
expect(topic.reload.vote_count).to eq(1)
|
|
expect(user.reload.vote_count).to eq(1)
|
|
|
|
get "/voting/who.json", params: { topic_id: topic.id }
|
|
expect(response.status).to eq(200)
|
|
json = JSON.parse(response.body)
|
|
expect(json.length).to eq(1)
|
|
expect(json.first.keys.sort).to eq(%w[avatar_template id name username])
|
|
expect(json.first["id"]).to eq(user.id)
|
|
|
|
post "/voting/unvote.json", params: { topic_id: topic.id }
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(topic.reload.vote_count).to eq(0)
|
|
expect(user.reload.vote_count).to eq(0)
|
|
end
|
|
|
|
it "returns 403 when the user already voted" do
|
|
DiscourseTopicVoting::Vote.create!(user:, topic:)
|
|
|
|
post "/voting/vote.json", params: { topic_id: topic.id }
|
|
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "returns 403 with voting payload when the user reached the vote limit" do
|
|
SiteSetting.public_send("topic_voting_tl#{user.trust_level}_vote_limit=", 0)
|
|
|
|
post "/voting/vote.json", params: { topic_id: topic.id }
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
json = response.parsed_body
|
|
expect(json["can_vote"]).to eq(false)
|
|
expect(json["vote_limit"]).to eq(0)
|
|
expect(json["vote_count"]).to eq(0)
|
|
expect(json["votes_left"]).to eq(0)
|
|
expect(json["alert"]).to eq(true)
|
|
end
|
|
|
|
context "when vote limits are disabled" do
|
|
before do
|
|
SiteSetting.topic_voting_enable_vote_limits = false
|
|
SiteSetting.public_send("topic_voting_tl#{user.trust_level}_vote_limit=", 0)
|
|
end
|
|
|
|
it "allows voting and returns nil for limit fields" do
|
|
post "/voting/vote.json", params: { topic_id: topic.id }
|
|
expect(response.status).to eq(200)
|
|
|
|
json = response.parsed_body
|
|
expect(json["can_vote"]).to eq(true)
|
|
expect(json["vote_limit"]).to be_nil
|
|
expect(json["votes_left"]).to be_nil
|
|
expect(json["alert"]).to eq(false)
|
|
end
|
|
|
|
it "returns nil for limit fields on unvote" do
|
|
DiscourseTopicVoting::Vote.create!(user:, topic:)
|
|
|
|
post "/voting/unvote.json", params: { topic_id: topic.id }
|
|
expect(response.status).to eq(200)
|
|
|
|
json = response.parsed_body
|
|
expect(json["vote_limit"]).to be_nil
|
|
expect(json["votes_left"]).to be_nil
|
|
end
|
|
end
|
|
|
|
it "triggers a topic_upvote webhook when voting" do
|
|
Fabricate(:topic_voting_web_hook)
|
|
post "/voting/vote.json", params: { topic_id: topic.id }
|
|
expect(response.status).to eq(200)
|
|
|
|
job_args = Jobs::EmitWebHookEvent.jobs[0]["args"].first
|
|
expect(job_args["event_name"]).to eq("topic_upvote")
|
|
payload = JSON.parse(job_args["payload"])
|
|
expect(payload["topic_id"]).to eq(topic.id)
|
|
expect(payload["topic_slug"]).to eq(topic.slug)
|
|
expect(payload["voter_id"]).to eq(user.id)
|
|
expect(payload["vote_count"]).to eq(1)
|
|
end
|
|
|
|
it "triggers a topic_unvote webhook when unvoting" do
|
|
DiscourseTopicVoting::Vote.create!(user:, topic:)
|
|
topic.update_vote_count
|
|
|
|
Fabricate(:topic_voting_web_hook)
|
|
post "/voting/unvote.json", params: { topic_id: topic.id }
|
|
expect(response.status).to eq(200)
|
|
job_args = Jobs::EmitWebHookEvent.jobs[0]["args"].first
|
|
expect(job_args["event_name"]).to eq("topic_unvote")
|
|
payload = JSON.parse(job_args["payload"])
|
|
expect(payload["topic_id"]).to eq(topic.id)
|
|
expect(payload["topic_slug"]).to eq(topic.slug)
|
|
expect(payload["voter_id"]).to eq(user.id)
|
|
expect(payload["vote_count"]).to eq(0)
|
|
end
|
|
|
|
it "does not remove an archived vote when unvoting" do
|
|
DiscourseTopicVoting::Vote.create!(user:, topic:, archive: true)
|
|
topic.update_vote_count
|
|
|
|
post "/voting/unvote.json", params: { topic_id: topic.id }
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(DiscourseTopicVoting::Vote.where(user:, topic:, archive: true).count).to eq(1)
|
|
expect(topic.reload.vote_count).to eq(1)
|
|
end
|
|
|
|
it "returns 200 when there is no active vote to remove" do
|
|
post "/voting/unvote.json", params: { topic_id: topic.id }
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["vote_count"]).to eq(0)
|
|
end
|
|
|
|
it "limits who-voted previews to VOTE_PREVIEW_LIMIT users by default" do
|
|
stub_const(DiscourseTopicVoting, "VOTER_PREVIEW_LIMIT", 10) do
|
|
Fabricate
|
|
.times(11, :user)
|
|
.each { |voter| DiscourseTopicVoting::Vote.create!(user: voter, topic:) }
|
|
|
|
get "/voting/who.json", params: { topic_id: topic.id }
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body.length).to eq(DiscourseTopicVoting::VOTER_PREVIEW_LIMIT)
|
|
end
|
|
end
|
|
|
|
it "still returns voters after the topic has been closed" do
|
|
voter = Fabricate(:user)
|
|
DiscourseTopicVoting::Vote.create!(user: voter, topic:)
|
|
topic.update_vote_count
|
|
|
|
Jobs.run_immediately!
|
|
topic.update_status("closed", true, Discourse.system_user)
|
|
|
|
get "/voting/who.json", params: { topic_id: topic.id }
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body.pluck("id")).to eq([voter.id])
|
|
end
|
|
|
|
it "includes archived votes and honors a smaller who-voted limit" do
|
|
older_voter = Fabricate(:user)
|
|
newer_voter = Fabricate(:user)
|
|
archived_voter = Fabricate(:user)
|
|
|
|
DiscourseTopicVoting::Vote.create!(user: older_voter, topic:, created_at: 2.hours.ago)
|
|
DiscourseTopicVoting::Vote.create!(user: newer_voter, topic:, created_at: 1.hour.ago)
|
|
DiscourseTopicVoting::Vote.create!(
|
|
user: archived_voter,
|
|
topic:,
|
|
archive: true,
|
|
created_at: Time.zone.now,
|
|
)
|
|
|
|
get "/voting/who.json", params: { topic_id: topic.id, limit: 1 }
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body.pluck("id")).to eq([archived_voter.id])
|
|
end
|
|
end
|