diff --git a/lib/tasks/backfill_sponsor_id.rake b/lib/tasks/backfill_sponsor_id.rake new file mode 100644 index 00000000000..f3463f13440 --- /dev/null +++ b/lib/tasks/backfill_sponsor_id.rake @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +namespace :in_person_enrollments do + desc 'Backfill the sponsor_id column.' + + ## + # Usage: + # + # bundle exec rake in_person_enrollments:backfill_sponsor_id + # + task backfill_sponsor_id: :environment do |_task, _args| + with_timeout do + ipp_sponsor_id = IdentityConfig.store.usps_ipp_sponsor_id + enrollments_without_sponsor_id = InPersonEnrollment.where(sponsor_id: nil) + enrollments_without_sponsor_id_count = enrollments_without_sponsor_id.count + + warn("Found #{enrollments_without_sponsor_id_count} in_person_enrollments needing backfill") + + tally = 0 + enrollments_without_sponsor_id.in_batches(of: batch_size) do |batch| + tally += batch.update_all(sponsor_id: ipp_sponsor_id) # rubocop:disable Rails/SkipsModelValidations + warn("set sponsor_id for #{tally} in_person_enrollments") + end + warn("COMPLETE: Updated #{tally} in_person_enrollments") + + enrollments_without_sponsor_id = InPersonEnrollment.where(sponsor_id: nil) + enrollments_without_sponsor_id_count = enrollments_without_sponsor_id.count + warn("#{enrollments_without_sponsor_id_count} enrollments without a sponsor id") + end + end + + def batch_size + ENV['BATCH_SIZE'] ? ENV['BATCH_SIZE'].to_i : 1000 + end + + def with_timeout + timeout_in_seconds ||= if ENV['STATEMENT_TIMEOUT_IN_SECONDS'] + ENV['STATEMENT_TIMEOUT_IN_SECONDS'].to_i.seconds + else + 60.seconds + end + ActiveRecord::Base.transaction do + quoted_timeout = ActiveRecord::Base.connection.quote(timeout_in_seconds.in_milliseconds) + ActiveRecord::Base.connection.execute("SET statement_timeout = #{quoted_timeout}") + yield + end + end +end diff --git a/spec/factories/in_person_enrollments.rb b/spec/factories/in_person_enrollments.rb index 6e691db084d..70238b182c2 100644 --- a/spec/factories/in_person_enrollments.rb +++ b/spec/factories/in_person_enrollments.rb @@ -46,5 +46,9 @@ trait :enhanced_ipp do sponsor_id { IdentityConfig.store.usps_eipp_sponsor_id } end + + trait :with_nil_sponsor_id do + sponsor_id { nil } + end end end diff --git a/spec/lib/tasks/backfill_sponsor_id_rake_spec.rb b/spec/lib/tasks/backfill_sponsor_id_rake_spec.rb new file mode 100644 index 00000000000..2992ff7bd27 --- /dev/null +++ b/spec/lib/tasks/backfill_sponsor_id_rake_spec.rb @@ -0,0 +1,73 @@ +require 'rails_helper' +require 'rake' + +RSpec.describe 'in_person_enrollments:backfill_sponsor_id rake task' do + let!(:task) do + Rake.application.rake_require 'tasks/backfill_sponsor_id' + Rake::Task.define_task(:environment) + Rake::Task['in_person_enrollments:backfill_sponsor_id'] + end + + subject(:invoke_task) do + actual_stderr = $stderr + proxy_stderr = StringIO.new + begin + $stderr = proxy_stderr + task.reenable + task.invoke + proxy_stderr.string + ensure + $stderr = actual_stderr + end + end + + let(:pending_enrollment) { create(:in_person_enrollment, :pending, :with_nil_sponsor_id) } + let(:expired_enrollment) { create(:in_person_enrollment, :expired, :with_nil_sponsor_id) } + let(:failed_enrollment) { create(:in_person_enrollment, :failed, :with_nil_sponsor_id) } + let(:enrollment_with_service_provider) do + create(:in_person_enrollment, :with_service_provider, :with_nil_sponsor_id) + end + let(:enrollment_with_sponsor_id) { create(:in_person_enrollment) } + + before do + allow(IdentityConfig.store).to receive(:usps_ipp_sponsor_id).and_return('31459') + expect(pending_enrollment.sponsor_id).to be_nil + expect(expired_enrollment.sponsor_id).to be_nil + expect(failed_enrollment.sponsor_id).to be_nil + expect(enrollment_with_service_provider.sponsor_id).to be_nil + expect(enrollment_with_sponsor_id.sponsor_id).not_to be_nil + end + + it 'does not change the value of an existing sponsor id' do + original_sponsor_id = enrollment_with_sponsor_id.sponsor_id + subject + expect(enrollment_with_sponsor_id.sponsor_id).to eq(original_sponsor_id) + end + + it 'sets a sponsor id for every enrollment with a nil sponsor id' do + enrollments_with_nil_sponsor_id_count = InPersonEnrollment.where(sponsor_id: nil).count + expect(enrollments_with_nil_sponsor_id_count).to eq(4) + subject + enrollments_with_nil_sponsor_id_count = InPersonEnrollment.where(sponsor_id: nil).count + expect(enrollments_with_nil_sponsor_id_count).to eq(0) + end + + it 'sets a sponsor id that is a string' do + subject + enrollments = InPersonEnrollment.all + enrollments.each do |enrollment| + expect(enrollment.sponsor_id).to be_a String + end + end + + it 'outputs what it did' do + expect(invoke_task.to_s).to eql( + <<~END, + Found 4 in_person_enrollments needing backfill + set sponsor_id for 4 in_person_enrollments + COMPLETE: Updated 4 in_person_enrollments + 0 enrollments without a sponsor id + END + ) + end +end