From 9dec6f161144f50f5037b4a0d9d7f2f1cd924366 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 21 Apr 2023 11:40:23 +0200 Subject: [PATCH] =?UTF-8?q?correctif(procedure.declarative):=20ETQ=20admin?= =?UTF-8?q?istrateur=20d'une=20procedure=20declarative,=20certains=20de=20?= =?UTF-8?q?mes=20dossiers=20restent=20en=20construction=20[ex:=20l'object?= =?UTF-8?q?=20storage=20est=20down,=20le=20dossier=20reste=20bloqu=C3=A9]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalled_declarative_procedures_job.rb | 14 ++ app/models/procedure.rb | 17 +++ ...stalled_declarative_procedures_job_spec.rb | 126 ++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 app/jobs/cron/stalled_declarative_procedures_job.rb create mode 100644 spec/jobs/cron/stalled_declarative_procedures_job_spec.rb diff --git a/app/jobs/cron/stalled_declarative_procedures_job.rb b/app/jobs/cron/stalled_declarative_procedures_job.rb new file mode 100644 index 000000000..c713bac0e --- /dev/null +++ b/app/jobs/cron/stalled_declarative_procedures_job.rb @@ -0,0 +1,14 @@ +class Cron::StalledDeclarativeProceduresJob < Cron::CronJob + self.schedule_expression = "every 10 minute" + + def perform(*args) + Procedure.declarative.find_each do |procedure| + begin + procedure.process_stalled_dossiers! + rescue => e + Sentry.set_tags(procedure: procedure.id) + Sentry.capture_exception(e) + end + end + end +end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 1519105c2..8a089571f 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -479,6 +479,23 @@ class Procedure < ApplicationRecord end end + def process_stalled_dossiers! + case declarative_with_state + when Procedure.declarative_with_states.fetch(:en_instruction) + dossiers + .state_en_construction + .where(declarative_triggered_at: nil) + .find_each(&:passer_automatiquement_en_instruction!) + when Procedure.declarative_with_states.fetch(:accepte) + dossiers + .state_en_construction + .where(declarative_triggered_at: nil) + .find_each do |dossier| + dossier.accepter_automatiquement! if dossier.can_accepter_automatiquement? + end + end + end + def feature_enabled?(feature) Flipper.enabled?(feature, self) end diff --git a/spec/jobs/cron/stalled_declarative_procedures_job_spec.rb b/spec/jobs/cron/stalled_declarative_procedures_job_spec.rb new file mode 100644 index 000000000..2d2f359aa --- /dev/null +++ b/spec/jobs/cron/stalled_declarative_procedures_job_spec.rb @@ -0,0 +1,126 @@ +RSpec.describe Cron::StalledDeclarativeProceduresJob, type: :job do + describe "perform" do + let(:date) { Time.utc(2017, 9, 1, 10, 5, 0) } + let(:instruction_date) { date + 120 } + + let(:state) { nil } + let(:procedure) { create(:procedure, :published, :for_individual, :with_instructeur, declarative_with_state: state) } + let(:nouveau_dossier1) { create(:dossier, :en_construction, :with_individual, :with_attestation, procedure: procedure) } + let(:nouveau_dossier2) { create(:dossier, :en_construction, :with_individual, :with_attestation, procedure: procedure) } + let(:dossier_recu) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) } + let(:dossier_brouillon) { create(:dossier, procedure: procedure) } + let(:dossier_repasse_en_construction) { create(:dossier, :en_construction, :with_individual, procedure: procedure) } + + before do + Timecop.freeze(date) + dossier_repasse_en_construction&.touch(:declarative_triggered_at) + end + + subject(:perform_job) do + dossiers = [ + nouveau_dossier1, + nouveau_dossier2, + dossier_recu, + dossier_brouillon, + dossier_repasse_en_construction + ].compact + + Cron::StalledDeclarativeProceduresJob.new.perform + + dossiers.each(&:reload) + end + + after { Timecop.return } + + context "with some dossiers" do + context "en_construction" do + let(:state) { Dossier.states.fetch(:en_instruction) } + let(:last_operation) { nouveau_dossier1.dossier_operation_logs.last } + + it { + perform_job + expect(nouveau_dossier1.en_instruction?).to be_truthy + expect(nouveau_dossier1.en_instruction_at).to eq(date) + expect(last_operation.operation).to eq('passer_en_instruction') + + expect(last_operation.automatic_operation?).to be_truthy + + expect(nouveau_dossier2.en_instruction?).to be_truthy + expect(nouveau_dossier2.en_instruction_at).to eq(date) + + expect(dossier_recu.en_instruction?).to be_truthy + expect(dossier_recu.en_instruction_at).to eq(instruction_date) + + expect(dossier_brouillon.brouillon?).to be_truthy + expect(dossier_brouillon.en_instruction_at).to eq(nil) + + expect(dossier_repasse_en_construction.en_construction?).to be_truthy + } + end + + context "accepte" do + let(:state) { Dossier.states.fetch(:accepte) } + let(:last_operation) { nouveau_dossier1.dossier_operation_logs.last } + + it { + perform_job + expect(nouveau_dossier1.accepte?).to be true + expect(nouveau_dossier1.en_instruction_at).to eq(date) + expect(nouveau_dossier1.processed_at).to eq(date) + expect(nouveau_dossier1.attestation).to be_present + expect(last_operation.operation).to eq('accepter') + expect(last_operation.automatic_operation?).to be_truthy + + expect(nouveau_dossier2.accepte?).to be true + expect(nouveau_dossier2.en_instruction_at).to eq(date) + expect(nouveau_dossier2.processed_at).to eq(date) + expect(nouveau_dossier2.attestation).to be_present + + expect(dossier_recu.en_instruction?).to be true + expect(dossier_recu.en_instruction_at).to eq(instruction_date) + expect(dossier_recu.processed_at).to eq(nil) + + expect(dossier_brouillon.brouillon?).to be true + expect(dossier_brouillon.en_instruction_at).to eq(nil) + expect(dossier_brouillon.processed_at).to eq(nil) + } + + context "having etablissement in degraded_mode" do + let(:procedure) { create(:procedure, :published, :with_instructeur, for_individual: false, declarative_with_state: state) } + + let(:nouveau_dossier1) { create(:dossier, :en_construction, :with_entreprise, :with_attestation, procedure: procedure, as_degraded_mode: false) } + let(:nouveau_dossier2) { create(:dossier, :en_construction, :with_entreprise, :with_attestation, procedure: procedure, as_degraded_mode: true) } + let(:dossier_recu) { nil } + let(:dossier_repasse_en_construction) { nil } + + before do + expect(nouveau_dossier2).to_not receive(:accepter_automatiquement!) + expect(Sentry).to_not receive(:capture_exception) + end + + it { + perform_job + expect(nouveau_dossier1).to be_accepte + expect(nouveau_dossier2).to be_en_construction + } + end + end + end + end + + describe 'safer perform' do + let(:state) { Dossier.states.fetch(:en_instruction) } + + it 'works no matter if one raise' do + procedure_1 = instance_double("Procedure", id: 1) + expect(procedure_1).to receive(:process_stalled_dossiers!) + procedure_2 = instance_double("Procedure", id: 2) + expect(procedure_2).to receive(:process_stalled_dossiers!).and_raise("boom") + procedure_3 = double(process_stalled_dossiers!: true) + expect(procedure_3).to receive(:process_stalled_dossiers!) + + expect(Procedure).to receive_message_chain(:declarative, :find_each).and_yield(procedure_1).and_yield(procedure_2).and_yield(procedure_3) + Cron::StalledDeclarativeProceduresJob.perform_now + end + end +end