From b013a5b1b61121d88c8ec1c35fae41454dcfbfb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Sat, 11 May 2024 18:49:11 +0200 Subject: [PATCH] Add task to delete duplicate voters Note that, since poll answers belong to a user and not to a voter, we aren't doing anything regarding poll answers. This is a separate topic that might be dealt with in a separate pull request. Also note that, since there are no records belonging to poll voters, and poll voters don't use `acts_as_paranoia` and don't have any callbacks on destroy, it doesn't really matter whether we call `destroy!` or `delete`. We're using `delete` so there are no unintended side-effects that might affect voters with the same `user_id` and `poll_id` on Consul Democracy installations customizing this behavior. --- lib/tasks/consul.rake | 3 ++- lib/tasks/polls.rake | 27 ++++++++++++++++++++ spec/lib/tasks/polls_spec.rb | 49 ++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 lib/tasks/polls.rake create mode 100644 spec/lib/tasks/polls_spec.rb diff --git a/lib/tasks/consul.rake b/lib/tasks/consul.rake index 73296660a..9f56f3c38 100644 --- a/lib/tasks/consul.rake +++ b/lib/tasks/consul.rake @@ -7,6 +7,7 @@ namespace :consul do desc "Runs tasks needed to upgrade from 2.1.1 to 2.2.0" task "execute_release_2.2.0_tasks": [ - "db:mask_ips" + "db:mask_ips", + "polls:remove_duplicate_voters" ] end diff --git a/lib/tasks/polls.rake b/lib/tasks/polls.rake new file mode 100644 index 000000000..689ecc335 --- /dev/null +++ b/lib/tasks/polls.rake @@ -0,0 +1,27 @@ +namespace :polls do + desc "Removes duplicate poll voters" + task remove_duplicate_voters: :environment do + logger = ApplicationLogger.new + logger.info "Removing duplicate voters in polls" + + Tenant.run_on_each do + duplicate_ids = Poll::Voter.select(:user_id, :poll_id) + .group(:user_id, :poll_id) + .having("count(*) > 1") + .pluck(:user_id, :poll_id) + + duplicate_ids.each do |user_id, poll_id| + voters = Poll::Voter.where(user_id: user_id, poll_id: poll_id) + voters.excluding(voters.first).each do |voter| + voter.delete + + tenant_info = " on tenant #{Tenant.current_schema}" unless Tenant.default? + logger.info "Deleted duplicate record with ID #{voter.id} " \ + "from the #{Poll::Voter.table_name} table " \ + "with user_id #{user_id} " \ + "and poll_id #{poll_id}" + tenant_info.to_s + end + end + end + end +end diff --git a/spec/lib/tasks/polls_spec.rb b/spec/lib/tasks/polls_spec.rb new file mode 100644 index 000000000..2b43c969d --- /dev/null +++ b/spec/lib/tasks/polls_spec.rb @@ -0,0 +1,49 @@ +require "rails_helper" + +describe "polls tasks" do + let(:poll) { create(:poll) } + let(:user) { create(:user, :level_two) } + + describe "polls:remove_duplicate_voters" do + before { Rake::Task["polls:remove_duplicate_voters"].reenable } + + it "removes duplicate voters" do + second_user = create(:user, :level_two) + + voter = create(:poll_voter, poll: poll, user: user) + second_voter = create(:poll_voter, poll: poll, user: second_user) + other_user_voter = create(:poll_voter, poll: poll, user: create(:user, :level_two)) + other_poll_voter = create(:poll_voter, poll: create(:poll), user: user) + + 2.times { insert(:poll_voter, poll_id: poll.id, user_id: user.id) } + insert(:poll_voter, poll_id: poll.id, user_id: second_user.id) + + expect(Poll::Voter.count).to eq 7 + + Rake.application.invoke_task("polls:remove_duplicate_voters") + + expect(Poll::Voter.count).to eq 4 + expect(Poll::Voter.all).to match_array [voter, second_voter, other_user_voter, other_poll_voter] + end + + it "removes duplicate voters on tenants" do + create(:tenant, schema: "voters") + + Tenant.switch("voters") do + poll = create(:poll) + user = create(:user, :level_two) + + create(:poll_voter, poll: poll, user: user) + insert(:poll_voter, poll_id: poll.id, user_id: user.id) + + expect(Poll::Voter.count).to eq 2 + end + + Rake.application.invoke_task("polls:remove_duplicate_voters") + + Tenant.switch("voters") do + expect(Poll::Voter.count).to eq 1 + end + end + end +end