Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hotfix/appeals 27980 #19173

Merged
merged 5 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions app/jobs/nightly_syncs_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@

# run once a day, overnight, to synchronize systems

# :reek:InstanceVariableAssumption
class NightlySyncsJob < CaseflowJob
queue_with_priority :low_priority
application_attr :queue # arbitrary

def perform
RequestStore.store[:current_user] = User.system_user
@slack_report = []

sync_vacols_users
sync_vacols_cases
sync_vacols_users
sync_decision_review_tasks
sync_bgs_attorneys

slack_service.send_notification(@slack_report.join("\n"), self.class.name) if @slack_report.any?
end

private
Expand All @@ -21,42 +25,62 @@ def sync_vacols_users
user_cache_start = Time.zone.now
CachedUser.sync_from_vacols
datadog_report_time_segment(segment: "sync_users_from_vacols", start_time: user_cache_start)
rescue StandardError => error
@slack_report << "*Fatal error in sync_vacols_users:* #{error}"
end

# rubocop:disable Metrics/MethodLength
def sync_vacols_cases
vacols_cases_with_error = []
start_time = Time.zone.now
dangling_legacy_appeals.each do |legacy_appeal|
next if legacy_appeal.case_record.present? # extra check

# delete pure danglers
if any_task?(legacy_appeal)
legacy_appeal.destroy!
begin
legacy_appeal.destroy!
rescue ActiveRecord::InvalidForeignKey => error
vacols_cases_with_error << legacy_appeal.id.to_s
capture_exception(error: error, extra: { legacy_appeal_id: legacy_appeal.id })
end
else
# if we have tasks and no case_record, then we need to cancel all the tasks,
# but we do not delete the dangling LegacyAppeal record.
legacy_appeal.tasks.open.where(parent_id: nil).each(&:cancel_task_and_child_subtasks)
legacy_appeal.dispatch_tasks.open.each(&:invalidate!)
end
end
if vacols_cases_with_error.any?
@slack_report.unshift("VACOLS cases which cannot be deleted by sync_vacols_cases: #{vacols_cases_with_error}")
end
datadog_report_time_segment(segment: "sync_cases_from_vacols", start_time: start_time)
rescue StandardError => error
@slack_report << "*Fatal error in sync_vacols_cases:* #{error}"
end
# rubocop:enable Metrics/MethodLength

# check both `Task` and `Dispatch::Task` (which doesn't inherit from `Task`)
def any_task?(legacy_appeal)
legacy_appeal.tasks.none? && legacy_appeal.dispatch_tasks.none?
end

# :reek:FeatureEnvy
def sync_decision_review_tasks
# tasks that went unfinished while the case was completed should be cancelled
checker = DecisionReviewTasksForInactiveAppealsChecker.new
checker.call
checker.buffer.map { |task_id| Task.find(task_id).cancelled! }
rescue StandardError => error
@slack_report << "*Fatal error in sync_decision_review_tasks:* #{error}"
end

def sync_bgs_attorneys
start_time = Time.zone.now
BgsAttorney.sync_bgs_attorneys
datadog_report_time_segment(segment: "sync_bgs_attorneys", start_time: start_time)
rescue StandardError => error
@slack_report << "*Fatal error in sync_bgs_attorneys:* #{error}"
end

def dangling_legacy_appeals
Expand Down
70 changes: 67 additions & 3 deletions spec/jobs/nightly_syncs_job_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

describe NightlySyncsJob, :all_dbs do
subject { described_class.perform_now }

context "when the job runs successfully" do
before do
5.times { create(:staff) }
Expand All @@ -11,8 +13,6 @@
end
end

subject { described_class.perform_now }

it "updates cached_user_attributes table" do
subject

Expand All @@ -37,9 +37,13 @@ class FakeTask < Dispatch::Task
context "with zero tasks" do
let!(:legacy_appeal) { create(:legacy_appeal) }

it "deletes the legacy appeal" do
it "deletes the legacy appeal and does not include it in slack report" do
slack_msg = ""
slack_msg_error_text = "VACOLS cases which cannot be deleted by sync_vacols_cases:"
allow_any_instance_of(SlackService).to receive(:send_notification) { |_, first_arg| slack_msg = first_arg }
subject

expect(slack_msg.include?(slack_msg_error_text)).to be false
expect { legacy_appeal.reload }.to raise_error(ActiveRecord::RecordNotFound)
end

Expand Down Expand Up @@ -140,5 +144,65 @@ class FakeTask < Dispatch::Task
expect(board_grant_effectuation_task.reload).to be_assigned
end
end

# our BgsService fake returns five records in the poas_list, this creates one of the records
# before the job and verifies that the job creates the remaining four records
context "with BGS attorneys" do
before { create(:bgs_attorney, participant_id: "12345678") }

it "creates new attorney records if they do not exist" do
subject

expect(BgsAttorney.count).to eq 5
end
end
end

context "when errors occur" do
context "in the sync_vacols_cases step" do
context "due to existing FK associations" do
let!(:legacy_appeal) { create(:legacy_appeal) }
let!(:legacy_hearing) { create(:legacy_hearing, appeal: legacy_appeal) }
let!(:snapshot) { AppealStreamSnapshot.create!(appeal: legacy_appeal, hearing: legacy_hearing) }

it "handles the error and doesn't delete record" do
slack_msg = ""
expected_slack_msg = "VACOLS cases which cannot be deleted by sync_vacols_cases:"
allow_any_instance_of(SlackService).to receive(:send_notification) { |_, first_arg| slack_msg = first_arg }
allow(Raven).to receive(:capture_exception)
subject

expect(legacy_appeal.reload).to_not be_nil
expect(Raven).to have_received(:capture_exception)
.with(anything, extra: { legacy_appeal_id: legacy_appeal.id })
expect(slack_msg.include?(expected_slack_msg)).to be true
# BgsAttorney.count verifies that the rest of the job ran as expected based on our service fake
expect(BgsAttorney.count).to eq 5
end
end
end

context "in any step due to miscellaneous errors" do
it "skips step and continues job" do
# raises error during sync_vacols_users step
allow(CachedUser).to receive(:sync_from_vacols).and_raise(StandardError)

# raises error during sync_vacols_cases step
allow_any_instance_of(LegacyAppealsWithNoVacolsCase).to receive(:call).and_raise(StandardError)

# raises error during sync_decision_review_tasks
allow_any_instance_of(DecisionReviewTasksForInactiveAppealsChecker).to receive(:call).and_raise(StandardError)

# raises error during sync_bgs_attorneys
allow(BgsAttorney).to receive(:sync_bgs_attorneys).and_raise(StandardError)

slack_msg = ""
allow_any_instance_of(SlackService).to receive(:send_notification) { |_, first_arg| slack_msg = first_arg }
subject

# one result for each of the four steps in the job
expect(slack_msg.split("StandardError").count).to eq 4
end
end
end
end
Loading