Skip to content

Commit

Permalink
feat: Creation of a rake task to delete duplicated half signup users
Browse files Browse the repository at this point in the history
  • Loading branch information
AyakorK committed Dec 24, 2024
1 parent 7e0f3b2 commit 233f69f
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 0 deletions.
84 changes: 84 additions & 0 deletions app/jobs/clear_duplicated_half_signup_users_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# frozen_string_literal: true

# rubocop:disable Rails/Output
class ClearDuplicatedHalfSignupUsersJob < ApplicationJob
include Decidim::Logging

def perform
duplicated_numbers = find_duplicated_phone_numbers
return puts("No duplicated phone numbers found") if duplicated_numbers.empty?

duplicated_numbers.each do |phone_info|
phone_number, phone_country = phone_info

users_with_phone = Decidim::User.where(phone_number: phone_number, phone_country: phone_country)
quick_auth_users = users_with_phone.select { |user| user.email.include?("quick_auth") }
other_users = users_with_phone.reject { |user| user.email.include?("quick_auth") }

quick_auth_users.each { |user| soft_delete_user(user, "Duplicated account") }

alert_about_duplicated_numbers(phone_number, other_users) if other_users.map(&:email).uniq.size > 1
end
end

private

def find_duplicated_phone_numbers
Decidim::User
.where.not(phone_number: [nil, ""])
.where.not(phone_country: [nil, ""])
.group(:phone_number, :phone_country)
.having("count(*) > 1")
.pluck(:phone_number, :phone_country)
end

def soft_delete_user(user, reason)
if user.email.include?("quick_auth")
user.update(phone_number: nil, phone_country: nil)

form = Decidim::DeleteAccountForm.from_params(delete_reason: reason)
previous_email = user.email
Decidim::DestroyAccount.call(user, form) do
on(:ok) do
log!("User #{user.id} (#{previous_email}) has been deleted", :info)
puts("User #{user.id} (#{previous_email}) has been deleted")
end
on(:invalid) do
log!("Failed to delete user #{user.id} (#{user.email}): #{form.errors.full_messages}", :warn)
puts("Failed to delete user #{user.id} (#{user.email}): #{form.errors.full_messages}")
end
end
else
log!("Not a Quick Auth account, skipping deletion", :info)
puts("Not a Quick Auth account, skipping deletion")
end
end

def alert_about_duplicated_numbers(phone_number, users)
obfuscated_number = obfuscate_phone_number(phone_number)
emails = users.map(&:email)
email_pairs = emails.each_slice(2).map { |pair| pair.join(" | ") }

message = <<~MSG
\nALERT: Duplicated Phone Number Detected!
Phone Number: #{obfuscated_number}
Users with this number:
#{email_pairs.join("\n")}
MSG

log!(message, :warn)
puts(message)
end

def obfuscate_phone_number(phone_number)
return "No phone number" if phone_number.blank?

visible_prefix = phone_number[0..1]
visible_suffix = phone_number[-2..]
obfuscated_middle = "*" * (phone_number.length - 4)

visible_prefix + obfuscated_middle + visible_suffix
end
end
# rubocop:enable Rails/Output
8 changes: 8 additions & 0 deletions lib/tasks/clear_duplicated_users.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

namespace :decidim do
desc "Clear duplicated users with the same phone_numbers in the database"
task clear_duplicated_users: :environment do
ClearDuplicatedHalfSignupUsersJob.perform_now
end
end
140 changes: 140 additions & 0 deletions spec/jobs/clear_duplicated_half_signup_users_job_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# frozen_string_literal: true

require "spec_helper"

module Decidim
describe ClearDuplicatedHalfSignupUsersJob do
let!(:user1) { create(:user, phone_number: "1234567890", phone_country: "US", email: "[email protected]") }
let!(:user2) { create(:user, phone_number: "1234567890", phone_country: "US", email: "[email protected]") }
let!(:user3) { create(:user, phone_number: "1234567890", phone_country: "US", email: "[email protected]") }
let!(:user4) { create(:user, phone_number: "9876543210", phone_country: "US", email: "[email protected]") }

before do
allow_any_instance_of(Decidim::DestroyAccount).to receive(:call).and_return(true)
end

describe "#perform" do
context "when no duplicated phone numbers are found" do
it "prints that no duplicated phone numbers are found" do
allow_any_instance_of(ClearDuplicatedHalfSignupUsersJob).to receive(:find_duplicated_phone_numbers).and_return([])

expect { ClearDuplicatedHalfSignupUsersJob.perform_now }.to output(/No duplicated phone numbers found/).to_stdout
end
end

context "when duplicated phone numbers are found" do
before do
user1
user2
user3
end

it "soft deletes quick_auth users" do
expect_any_instance_of(ClearDuplicatedHalfSignupUsersJob).to receive(:soft_delete_user).with(user2, "Duplicated account")

ClearDuplicatedHalfSignupUsersJob.perform_now
end

it "alerts about duplicated phone numbers for non-quick_auth users" do
expect(Rails.logger).to receive(:warn).with(/ALERT: Duplicated Phone Number Detected/)

ClearDuplicatedHalfSignupUsersJob.perform_now
end

it "does not soft delete non quick_auth users" do
expect_any_instance_of(ClearDuplicatedHalfSignupUsersJob).not_to receive(:soft_delete_user).with(user1, "Duplicated account")

ClearDuplicatedHalfSignupUsersJob.perform_now
end

it "does not soft delete users with different phone numbers" do
expect_any_instance_of(ClearDuplicatedHalfSignupUsersJob).not_to receive(:soft_delete_user).with(user4, "Duplicated account")

ClearDuplicatedHalfSignupUsersJob.perform_now
end
end
end

describe "#soft_delete_user" do
let(:user) { create(:user, phone_number: "1234567890", phone_country: "US", email: "[email protected]") }

it "updates the user to nullify the phone number and country" do
expect(user).to receive(:update).with(phone_number: nil, phone_country: nil)

subject.send(:soft_delete_user, user, "Duplicated account")
end

it "calls the Decidim::DestroyAccount service to delete the user" do
allow_any_instance_of(Decidim::DestroyAccount).to receive(:call).and_return(true)

subject.send(:soft_delete_user, user, "Duplicated account")
end

it "does not delete a non-quick_auth account" do
user_with_diff_email = create(:user, phone_number: "1234567890", phone_country: "US", email: "[email protected]")

expect_any_instance_of(Decidim::DestroyAccount).not_to receive(:call)
expect(Rails.logger).to receive(:info).with(/Not a Quick Auth account, skipping deletion/)

subject.send(:soft_delete_user, user_with_diff_email, "Duplicated account")
end
end

describe "#alert_about_duplicated_numbers" do
let(:users) { [user1, user2, user3] }

it "obfuscates the phone number and logs an alert message" do
obfuscated_number = "12****90"
allow(subject).to receive(:obfuscate_phone_number).with("1234567890").and_return(obfuscated_number)
expect(Rails.logger).to receive(:warn).with(/ALERT: Duplicated Phone Number Detected/)

subject.send(:alert_about_duplicated_numbers, "1234567890", users)
end

it "logs the correct message with the obfuscated phone number" do
obfuscated_number = "12****90"
allow(subject).to receive(:obfuscate_phone_number).with("1234567890").and_return(obfuscated_number)

allow(Rails.logger).to receive(:warn) { |message|
expect(message).to include("Phone Number: #{obfuscated_number}")
}

subject.send(:alert_about_duplicated_numbers, "1234567890", users)
end
end

describe "#obfuscate_phone_number" do
it "returns an obfuscated phone number with visible prefix and suffix" do
result = subject.send(:obfuscate_phone_number, "1234567890")

expect(result).to eq("12******90")
end

it 'returns "No phone number" if the phone number is blank' do
result = subject.send(:obfuscate_phone_number, "")

expect(result).to eq("No phone number")
end

it 'returns "No phone number" when given nil' do
result = subject.send(:obfuscate_phone_number, nil)

expect(result).to eq("No phone number")
end
end

describe "#find_duplicated_phone_numbers" do
context "when there are duplicates" do
it "finds duplicated phone numbers" do
user1
user2
user3

result = subject.send(:find_duplicated_phone_numbers)

expect(result).to include(%w(1234567890 US))
end
end
end
end
end

0 comments on commit 233f69f

Please sign in to comment.