Merge pull request #10261 from colinux/ignore-search-terms-2
Tech (perf): ignore les `search_terms` colonnes pour ne plus les select à chaque query et debounce l'indexation
This commit is contained in:
commit
c2b1d7aaaf
25 changed files with 336 additions and 175 deletions
|
@ -280,6 +280,7 @@ module Instructeurs
|
||||||
end
|
end
|
||||||
|
|
||||||
dossier.save(context: :champs_private_value)
|
dossier.save(context: :champs_private_value)
|
||||||
|
dossier.index_search_terms_later
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.turbo_stream do
|
format.turbo_stream do
|
||||||
|
|
|
@ -303,6 +303,8 @@ module Users
|
||||||
@dossier = dossier_with_champs(pj_template: false)
|
@dossier = dossier_with_champs(pj_template: false)
|
||||||
@errors = update_dossier_and_compute_errors
|
@errors = update_dossier_and_compute_errors
|
||||||
|
|
||||||
|
@dossier.index_search_terms_later if @errors.empty?
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.turbo_stream do
|
format.turbo_stream do
|
||||||
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_attributes_params, dossier.champs.filter(&:public?))
|
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_attributes_params, dossier.champs.filter(&:public?))
|
||||||
|
|
7
app/jobs/dossier_index_search_terms_job.rb
Normal file
7
app/jobs/dossier_index_search_terms_job.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class DossierIndexSearchTermsJob < ApplicationJob
|
||||||
|
discard_on ActiveRecord::RecordNotFound
|
||||||
|
|
||||||
|
def perform(dossier)
|
||||||
|
dossier.index_search_terms
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,8 +0,0 @@
|
||||||
class DossierUpdateSearchTermsJob < ApplicationJob
|
|
||||||
discard_on ActiveRecord::RecordNotFound
|
|
||||||
|
|
||||||
def perform(dossier)
|
|
||||||
dossier.update_search_terms
|
|
||||||
dossier.save!(touch: false)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -71,7 +71,7 @@ module DossierCloneConcern
|
||||||
touch(:last_champ_updated_at)
|
touch(:last_champ_updated_at)
|
||||||
end
|
end
|
||||||
reload
|
reload
|
||||||
update_search_terms_later
|
index_search_terms_later
|
||||||
editing_fork.destroy_editing_fork!
|
editing_fork.destroy_editing_fork!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -120,6 +120,7 @@ module DossierCloneConcern
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
cloned_dossier.index_search_terms_later if !fork
|
||||||
cloned_dossier.reload
|
cloned_dossier.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,40 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module DossierSearchableConcern
|
module DossierSearchableConcern
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
before_save :update_search_terms
|
after_commit :index_search_terms_later, if: -> { previously_new_record? || user_previously_changed? || mandataire_first_name_previously_changed? || mandataire_last_name_previously_changed? }
|
||||||
|
|
||||||
def update_search_terms
|
SEARCH_TERMS_DEBOUNCE = 30.seconds
|
||||||
self.search_terms = [
|
|
||||||
|
kredis_flag :debounce_index_search_terms_flag
|
||||||
|
|
||||||
|
def index_search_terms
|
||||||
|
DossierPreloader.load_one(self)
|
||||||
|
|
||||||
|
search_terms = [
|
||||||
user&.email,
|
user&.email,
|
||||||
*champs_public.flat_map(&:search_terms),
|
*champs_public.flat_map(&:search_terms),
|
||||||
*etablissement&.search_terms,
|
*etablissement&.search_terms,
|
||||||
individual&.nom,
|
individual&.nom,
|
||||||
individual&.prenom
|
individual&.prenom,
|
||||||
|
mandataire_first_name,
|
||||||
|
mandataire_last_name
|
||||||
].compact_blank.join(' ')
|
].compact_blank.join(' ')
|
||||||
|
|
||||||
self.private_search_terms = champs_private.flat_map(&:search_terms).compact_blank.join(' ')
|
private_search_terms = champs_private.flat_map(&:search_terms).compact_blank.join(' ')
|
||||||
|
|
||||||
|
sql = "UPDATE dossiers SET search_terms = :search_terms, private_search_terms = :private_search_terms WHERE id = :id"
|
||||||
|
sanitized_sql = self.class.sanitize_sql_array([sql, search_terms:, private_search_terms:, id:])
|
||||||
|
self.class.connection.execute(sanitized_sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_search_terms_later
|
def index_search_terms_later
|
||||||
DossierUpdateSearchTermsJob.perform_later(self)
|
return if debounce_index_search_terms_flag.marked?
|
||||||
|
|
||||||
|
debounce_index_search_terms_flag.mark(expires_in: SEARCH_TERMS_DEBOUNCE)
|
||||||
|
DossierIndexSearchTermsJob.set(wait: SEARCH_TERMS_DEBOUNCE).perform_later(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,6 +13,8 @@ module DossierStateConcern
|
||||||
|
|
||||||
MailTemplatePresenterService.create_commentaire_for_state(self, Dossier.states.fetch(:en_construction))
|
MailTemplatePresenterService.create_commentaire_for_state(self, Dossier.states.fetch(:en_construction))
|
||||||
procedure.compute_dossiers_count
|
procedure.compute_dossiers_count
|
||||||
|
|
||||||
|
index_search_terms_later
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_commit_passer_en_construction
|
def after_commit_passer_en_construction
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class Dossier < ApplicationRecord
|
class Dossier < ApplicationRecord
|
||||||
self.ignored_columns += [:re_instructed_at]
|
self.ignored_columns += [:re_instructed_at, :search_terms, :private_search_terms]
|
||||||
|
|
||||||
include DossierCloneConcern
|
include DossierCloneConcern
|
||||||
include DossierCorrectableConcern
|
include DossierCorrectableConcern
|
||||||
|
|
|
@ -17,6 +17,8 @@ class Etablissement < ApplicationRecord
|
||||||
fermé: "fermé"
|
fermé: "fermé"
|
||||||
}, _prefix: true
|
}, _prefix: true
|
||||||
|
|
||||||
|
after_commit -> { dossier&.index_search_terms_later }
|
||||||
|
|
||||||
def entreprise_raison_sociale
|
def entreprise_raison_sociale
|
||||||
read_attribute(:entreprise_raison_sociale).presence || raison_sociale_for_ei
|
read_attribute(:entreprise_raison_sociale).presence || raison_sociale_for_ei
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,6 +17,8 @@ class Individual < ApplicationRecord
|
||||||
|
|
||||||
validates :email, strict_email: true, presence: true, if: -> { dossier.for_tiers? && self.email? }, on: :update
|
validates :email, strict_email: true, presence: true, if: -> { dossier.for_tiers? && self.email? }, on: :update
|
||||||
|
|
||||||
|
after_commit -> { dossier.index_search_terms_later }, if: -> { nom_previously_changed? || prenom_previously_changed? }
|
||||||
|
|
||||||
GENDER_MALE = "M."
|
GENDER_MALE = "M."
|
||||||
GENDER_FEMALE = 'Mme'
|
GENDER_FEMALE = 'Mme'
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ if Rails.env.production? && SIDEKIQ_ENABLED
|
||||||
self.queue_adapter = :sidekiq
|
self.queue_adapter = :sidekiq
|
||||||
end
|
end
|
||||||
|
|
||||||
class DossierUpdateSearchTermsJob < ApplicationJob
|
class DossierIndexSearchTermsJob < ApplicationJob
|
||||||
self.queue_adapter = :sidekiq
|
self.queue_adapter = :sidekiq
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1041,6 +1041,7 @@ describe Instructeurs::DossiersController, type: :controller do
|
||||||
expect(champ_drop_down_list.value).to eq('other value')
|
expect(champ_drop_down_list.value).to eq('other value')
|
||||||
expect(dossier.reload.last_champ_private_updated_at).to eq(now)
|
expect(dossier.reload.last_champ_private_updated_at).to eq(now)
|
||||||
expect(response).to have_http_status(200)
|
expect(response).to have_http_status(200)
|
||||||
|
assert_enqueued_jobs(1, only: DossierIndexSearchTermsJob)
|
||||||
}
|
}
|
||||||
|
|
||||||
it 'updates the annotations' do
|
it 'updates the annotations' do
|
||||||
|
|
|
@ -27,6 +27,8 @@ describe RechercheController, type: :controller do
|
||||||
dossier_with_expert.champs_private[0].value = "Dossier B is incomplete"
|
dossier_with_expert.champs_private[0].value = "Dossier B is incomplete"
|
||||||
dossier_with_expert.champs_private[1].value = "Dossier B is invalid"
|
dossier_with_expert.champs_private[1].value = "Dossier B is invalid"
|
||||||
dossier_with_expert.save!
|
dossier_with_expert.save!
|
||||||
|
|
||||||
|
perform_enqueued_jobs(only: DossierIndexSearchTermsJob)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET #index' do
|
describe 'GET #index' do
|
||||||
|
|
|
@ -770,6 +770,16 @@ describe Users::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "debounce search terms indexation" do
|
||||||
|
# dossier creation trigger a first indexation and flag,
|
||||||
|
# so we we have to remove this flag
|
||||||
|
dossier.debounce_index_search_terms_flag.remove
|
||||||
|
|
||||||
|
assert_enqueued_jobs(1, only: DossierIndexSearchTermsJob) do
|
||||||
|
3.times { patch :update, params: payload, format: :turbo_stream }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#update en_construction' do
|
describe '#update en_construction' do
|
||||||
|
|
21
spec/jobs/dossier_index_search_terms_job_spec.rb
Normal file
21
spec/jobs/dossier_index_search_terms_job_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
RSpec.describe DossierIndexSearchTermsJob, type: :job do
|
||||||
|
let(:dossier) { create(:dossier) }
|
||||||
|
|
||||||
|
subject(:perform_job) { described_class.perform_now(dossier.reload) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:champ_text, dossier:, value: "un nouveau champ")
|
||||||
|
create(:champ_text, dossier:, value: "private champ", private: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "update search terms columns" do
|
||||||
|
perform_job
|
||||||
|
|
||||||
|
sql = "SELECT search_terms, private_search_terms FROM dossiers WHERE id = :id"
|
||||||
|
sanitized_sql = Dossier.sanitize_sql_array([sql, id: dossier.id])
|
||||||
|
result = Dossier.connection.execute(sanitized_sql).first
|
||||||
|
|
||||||
|
expect(result['search_terms']).to match(/un nouveau champ/)
|
||||||
|
expect(result['private_search_terms']).to match(/private champ/)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,15 +0,0 @@
|
||||||
RSpec.describe DossierUpdateSearchTermsJob, type: :job do
|
|
||||||
let(:dossier) { create(:dossier) }
|
|
||||||
let(:champ_public) { dossier.champs_public.first }
|
|
||||||
let(:champ_private) { dossier.champs_private.first }
|
|
||||||
|
|
||||||
subject(:perform_job) { described_class.perform_now(dossier) }
|
|
||||||
|
|
||||||
context 'with an update' do
|
|
||||||
before do
|
|
||||||
create(:champ_text, dossier: dossier, value: "un nouveau champ")
|
|
||||||
end
|
|
||||||
|
|
||||||
it { expect { perform_job }.to change { dossier.reload.search_terms }.to(/un nouveau champ/) }
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -19,37 +19,50 @@ RSpec.describe DossierCloneConcern do
|
||||||
let(:types_de_champ_public) { [{}] }
|
let(:types_de_champ_public) { [{}] }
|
||||||
let(:types_de_champ_private) { [{}] }
|
let(:types_de_champ_private) { [{}] }
|
||||||
let(:fork) { false }
|
let(:fork) { false }
|
||||||
let(:new_dossier) { dossier.clone(fork:) }
|
subject(:new_dossier) { dossier.clone(fork:) }
|
||||||
|
|
||||||
context 'reset most attributes' do
|
it 'resets most of the attributes for the cloned dossier' do
|
||||||
it { expect(new_dossier.id).not_to eq(dossier.id) }
|
expect(new_dossier.id).not_to eq(dossier.id)
|
||||||
it { expect(new_dossier.api_entreprise_job_exceptions).to be_nil }
|
expect(new_dossier.api_entreprise_job_exceptions).to be_nil
|
||||||
it { expect(new_dossier.archived).to be_falsey }
|
expect(new_dossier.archived).to be_falsey
|
||||||
it { expect(new_dossier.brouillon_close_to_expiration_notice_sent_at).to be_nil }
|
expect(new_dossier.brouillon_close_to_expiration_notice_sent_at).to be_nil
|
||||||
it { expect(new_dossier.conservation_extension).to eq(0.seconds) }
|
expect(new_dossier.conservation_extension).to eq(0.seconds)
|
||||||
it { expect(new_dossier.declarative_triggered_at).to be_nil }
|
expect(new_dossier.declarative_triggered_at).to be_nil
|
||||||
it { expect(new_dossier.deleted_user_email_never_send).to be_nil }
|
expect(new_dossier.deleted_user_email_never_send).to be_nil
|
||||||
it { expect(new_dossier.depose_at).to be_nil }
|
expect(new_dossier.depose_at).to be_nil
|
||||||
it { expect(new_dossier.en_construction_at).to be_nil }
|
expect(new_dossier.en_construction_at).to be_nil
|
||||||
it { expect(new_dossier.en_construction_close_to_expiration_notice_sent_at).to be_nil }
|
expect(new_dossier.en_construction_close_to_expiration_notice_sent_at).to be_nil
|
||||||
it { expect(new_dossier.en_instruction_at).to be_nil }
|
expect(new_dossier.en_instruction_at).to be_nil
|
||||||
it { expect(new_dossier.for_procedure_preview).to be_falsey }
|
expect(new_dossier.for_procedure_preview).to be_falsey
|
||||||
it { expect(new_dossier.groupe_instructeur_updated_at).to be_nil }
|
expect(new_dossier.groupe_instructeur_updated_at).to be_nil
|
||||||
it { expect(new_dossier.hidden_at).to be_nil }
|
expect(new_dossier.hidden_at).to be_nil
|
||||||
it { expect(new_dossier.hidden_by_administration_at).to be_nil }
|
expect(new_dossier.hidden_by_administration_at).to be_nil
|
||||||
it { expect(new_dossier.hidden_by_reason).to be_nil }
|
expect(new_dossier.hidden_by_reason).to be_nil
|
||||||
it { expect(new_dossier.hidden_by_user_at).to be_nil }
|
expect(new_dossier.hidden_by_user_at).to be_nil
|
||||||
it { expect(new_dossier.identity_updated_at).to be_nil }
|
expect(new_dossier.identity_updated_at).to be_nil
|
||||||
it { expect(new_dossier.last_avis_updated_at).to be_nil }
|
expect(new_dossier.last_avis_updated_at).to be_nil
|
||||||
it { expect(new_dossier.last_champ_private_updated_at).to be_nil }
|
expect(new_dossier.last_champ_private_updated_at).to be_nil
|
||||||
it { expect(new_dossier.last_champ_updated_at).to be_nil }
|
expect(new_dossier.last_champ_updated_at).to be_nil
|
||||||
it { expect(new_dossier.last_commentaire_updated_at).to be_nil }
|
expect(new_dossier.last_commentaire_updated_at).to be_nil
|
||||||
it { expect(new_dossier.motivation).to be_nil }
|
expect(new_dossier.motivation).to be_nil
|
||||||
it { expect(new_dossier.private_search_terms).to eq("") }
|
expect(new_dossier.processed_at).to be_nil
|
||||||
it { expect(new_dossier.processed_at).to be_nil }
|
end
|
||||||
it { expect(new_dossier.search_terms).to match(dossier.user.email) }
|
|
||||||
it { expect(new_dossier.termine_close_to_expiration_notice_sent_at).to be_nil }
|
it "updates search terms" do
|
||||||
it { expect(new_dossier.dossier_transfer_id).to be_nil }
|
# In spec, dossier and flag reference are created just before deep clone,
|
||||||
|
# which keep the flag reference from the original, pointing to the original id.
|
||||||
|
# We have to remove the flag reference before the clone
|
||||||
|
dossier.remove_instance_variable(:@debounce_index_search_terms_flag_kredis_flag)
|
||||||
|
|
||||||
|
perform_enqueued_jobs(only: DossierIndexSearchTermsJob) do
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
|
||||||
|
sql = "SELECT search_terms, private_search_terms FROM dossiers where id = :id"
|
||||||
|
result = Dossier.connection.execute(Dossier.sanitize_sql_array([sql, id: new_dossier.id])).first
|
||||||
|
|
||||||
|
expect(result["search_terms"]).to match(dossier.user.email)
|
||||||
|
expect(result["private_search_terms"]).to eq("")
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'copies some attributes' do
|
context 'copies some attributes' do
|
||||||
|
@ -59,18 +72,22 @@ RSpec.describe DossierCloneConcern do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when not forked' do
|
context 'when not forked' do
|
||||||
it { expect(new_dossier.groupe_instructeur).to be_nil }
|
it "copies or reset attributes" do
|
||||||
|
expect(new_dossier.groupe_instructeur).to be_nil
|
||||||
|
expect(new_dossier.autorisation_donnees).to eq(dossier.autorisation_donnees)
|
||||||
|
expect(new_dossier.revision_id).to eq(dossier.revision_id)
|
||||||
|
expect(new_dossier.user_id).to eq(dossier.user_id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
it { expect(new_dossier.autorisation_donnees).to eq(dossier.autorisation_donnees) }
|
|
||||||
it { expect(new_dossier.revision_id).to eq(dossier.revision_id) }
|
|
||||||
it { expect(new_dossier.user_id).to eq(dossier.user_id) }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'forces some attributes' do
|
context 'forces some attributes' do
|
||||||
let(:dossier) { create(:dossier, :accepte) }
|
let(:dossier) { create(:dossier, :accepte) }
|
||||||
|
|
||||||
it { expect(new_dossier.brouillon?).to eq(true) }
|
it do
|
||||||
it { expect(new_dossier.parent_dossier).to eq(dossier) }
|
expect(new_dossier.brouillon?).to eq(true)
|
||||||
|
expect(new_dossier.parent_dossier).to eq(dossier)
|
||||||
|
end
|
||||||
|
|
||||||
context 'destroy parent' do
|
context 'destroy parent' do
|
||||||
before { new_dossier }
|
before { new_dossier }
|
||||||
|
@ -83,22 +100,28 @@ RSpec.describe DossierCloneConcern do
|
||||||
|
|
||||||
context 'procedure with_individual' do
|
context 'procedure with_individual' do
|
||||||
let(:procedure) { create(:procedure, :for_individual) }
|
let(:procedure) { create(:procedure, :for_individual) }
|
||||||
it { expect(new_dossier.individual.slice(:nom, :prenom, :gender)).to eq(dossier.individual.slice(:nom, :prenom, :gender)) }
|
it do
|
||||||
it { expect(new_dossier.individual.id).not_to eq(dossier.individual.id) }
|
expect(new_dossier.individual.slice(:nom, :prenom, :gender)).to eq(dossier.individual.slice(:nom, :prenom, :gender))
|
||||||
|
expect(new_dossier.individual.id).not_to eq(dossier.individual.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'procedure with etablissement' do
|
context 'procedure with etablissement' do
|
||||||
let(:dossier) { create(:dossier, :with_entreprise) }
|
let(:dossier) { create(:dossier, :with_entreprise) }
|
||||||
it { expect(new_dossier.etablissement.slice(:siret)).to eq(dossier.etablissement.slice(:siret)) }
|
it do
|
||||||
it { expect(new_dossier.etablissement.id).not_to eq(dossier.etablissement.id) }
|
expect(new_dossier.etablissement.slice(:siret)).to eq(dossier.etablissement.slice(:siret))
|
||||||
|
expect(new_dossier.etablissement.id).not_to eq(dossier.etablissement.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'champs' do
|
describe 'champs' do
|
||||||
it { expect(new_dossier.id).not_to eq(dossier.id) }
|
it { expect(new_dossier.id).not_to eq(dossier.id) }
|
||||||
|
|
||||||
context 'public are duplicated' do
|
context 'public are duplicated' do
|
||||||
it { expect(new_dossier.champs_public.count).to eq(dossier.champs_public.count) }
|
it do
|
||||||
it { expect(new_dossier.champs_public.ids).not_to eq(dossier.champs_public.ids) }
|
expect(new_dossier.champs_public.count).to eq(dossier.champs_public.count)
|
||||||
|
expect(new_dossier.champs_public.ids).not_to eq(dossier.champs_public.ids)
|
||||||
|
end
|
||||||
|
|
||||||
it 'keeps champs.values' do
|
it 'keeps champs.values' do
|
||||||
original_first_champ = dossier.champs_public.first
|
original_first_champ = dossier.champs_public.first
|
||||||
|
@ -113,8 +136,10 @@ RSpec.describe DossierCloneConcern do
|
||||||
let(:champ_repetition) { create(:champ_repetition, type_de_champ: type_de_champ_repetition, dossier: dossier) }
|
let(:champ_repetition) { create(:champ_repetition, type_de_champ: type_de_champ_repetition, dossier: dossier) }
|
||||||
before { dossier.champs_public << champ_repetition }
|
before { dossier.champs_public << champ_repetition }
|
||||||
|
|
||||||
it { expect(Champs::RepetitionChamp.where(dossier: new_dossier).first.champs.count).to eq(4) }
|
it do
|
||||||
it { expect(Champs::RepetitionChamp.where(dossier: new_dossier).first.champs.ids).not_to eq(champ_repetition.champs.ids) }
|
expect(Champs::RepetitionChamp.where(dossier: new_dossier).first.champs.count).to eq(4)
|
||||||
|
expect(Champs::RepetitionChamp.where(dossier: new_dossier).first.champs.ids).not_to eq(champ_repetition.champs.ids)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'for Champs::CarteChamp with geo areas, original_champ.geo_areas are duped' do
|
context 'for Champs::CarteChamp with geo areas, original_champ.geo_areas are duped' do
|
||||||
|
@ -124,8 +149,10 @@ RSpec.describe DossierCloneConcern do
|
||||||
let(:champ_carte) { create(:champ_carte, type_de_champ: type_de_champ_carte, geo_areas: [geo_area]) }
|
let(:champ_carte) { create(:champ_carte, type_de_champ: type_de_champ_carte, geo_areas: [geo_area]) }
|
||||||
before { dossier.champs_public << champ_carte }
|
before { dossier.champs_public << champ_carte }
|
||||||
|
|
||||||
it { expect(Champs::CarteChamp.where(dossier: new_dossier).first.geo_areas.count).to eq(1) }
|
it do
|
||||||
it { expect(Champs::CarteChamp.where(dossier: new_dossier).first.geo_areas.ids).not_to eq(champ_carte.geo_areas.ids) }
|
expect(Champs::CarteChamp.where(dossier: new_dossier).first.geo_areas.count).to eq(1)
|
||||||
|
expect(Champs::CarteChamp.where(dossier: new_dossier).first.geo_areas.ids).not_to eq(champ_carte.geo_areas.ids)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'for Champs::SiretChamp, original_champ.etablissement is duped' do
|
context 'for Champs::SiretChamp, original_champ.etablissement is duped' do
|
||||||
|
@ -135,8 +162,10 @@ RSpec.describe DossierCloneConcern do
|
||||||
let(:champ_siret) { create(:champ_siret, type_de_champ: type_de_champs_siret, etablissement: create(:etablissement)) }
|
let(:champ_siret) { create(:champ_siret, type_de_champ: type_de_champs_siret, etablissement: create(:etablissement)) }
|
||||||
before { dossier.champs_public << champ_siret }
|
before { dossier.champs_public << champ_siret }
|
||||||
|
|
||||||
it { expect(Champs::SiretChamp.where(dossier: dossier).first.etablissement).not_to be_nil }
|
it do
|
||||||
it { expect(Champs::SiretChamp.where(dossier: new_dossier).first.etablissement.id).not_to eq(champ_siret.etablissement.id) }
|
expect(Champs::SiretChamp.where(dossier: dossier).first.etablissement).not_to be_nil
|
||||||
|
expect(Champs::SiretChamp.where(dossier: new_dossier).first.etablissement.id).not_to eq(champ_siret.etablissement.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'for Champs::PieceJustificative, original_champ.piece_justificative_file is duped' do
|
context 'for Champs::PieceJustificative, original_champ.piece_justificative_file is duped' do
|
||||||
|
@ -153,18 +182,19 @@ RSpec.describe DossierCloneConcern do
|
||||||
let(:champ_address) { create(:champ_address, type_de_champ: type_de_champs_adress, external_id: 'Address', data: { city_code: '75019' }) }
|
let(:champ_address) { create(:champ_address, type_de_champ: type_de_champs_adress, external_id: 'Address', data: { city_code: '75019' }) }
|
||||||
before { dossier.champs_public << champ_address }
|
before { dossier.champs_public << champ_address }
|
||||||
|
|
||||||
it { expect(Champs::AddressChamp.where(dossier: dossier).first.data).not_to be_nil }
|
it do
|
||||||
it { expect(Champs::AddressChamp.where(dossier: dossier).first.external_id).not_to be_nil }
|
expect(Champs::AddressChamp.where(dossier: dossier).first.data).not_to be_nil
|
||||||
it { expect(Champs::AddressChamp.where(dossier: new_dossier).first.external_id).to eq(champ_address.external_id) }
|
expect(Champs::AddressChamp.where(dossier: dossier).first.external_id).not_to be_nil
|
||||||
it { expect(Champs::AddressChamp.where(dossier: new_dossier).first.data).to eq(champ_address.data) }
|
expect(Champs::AddressChamp.where(dossier: new_dossier).first.external_id).to eq(champ_address.external_id)
|
||||||
|
expect(Champs::AddressChamp.where(dossier: new_dossier).first.data).to eq(champ_address.data)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'private are renewd' do
|
context 'private are renewd' do
|
||||||
it { expect(new_dossier.champs_private.count).to eq(dossier.champs_private.count) }
|
|
||||||
it { expect(new_dossier.champs_private.ids).not_to eq(dossier.champs_private.ids) }
|
|
||||||
|
|
||||||
it 'reset champs private values' do
|
it 'reset champs private values' do
|
||||||
|
expect(new_dossier.champs_private.count).to eq(dossier.champs_private.count)
|
||||||
|
expect(new_dossier.champs_private.ids).not_to eq(dossier.champs_private.ids)
|
||||||
original_first_champs_private = dossier.champs_private.first
|
original_first_champs_private = dossier.champs_private.first
|
||||||
original_first_champs_private.update!(value: 'kthxbye')
|
original_first_champs_private.update!(value: 'kthxbye')
|
||||||
|
|
||||||
|
@ -178,10 +208,12 @@ RSpec.describe DossierCloneConcern do
|
||||||
let(:new_dossier) { dossier.clone(fork: true) }
|
let(:new_dossier) { dossier.clone(fork: true) }
|
||||||
before { dossier.champs_public.reload } # we compare timestamps so we have to get the precision limit from the db }
|
before { dossier.champs_public.reload } # we compare timestamps so we have to get the precision limit from the db }
|
||||||
|
|
||||||
it { expect(new_dossier.editing_fork_origin).to eq(dossier) }
|
it do
|
||||||
it { expect(new_dossier.champs_public[0].id).not_to eq(dossier.champs_public[0].id) }
|
expect(new_dossier.editing_fork_origin).to eq(dossier)
|
||||||
it { expect(new_dossier.champs_public[0].created_at).to eq(dossier.champs_public[0].created_at) }
|
expect(new_dossier.champs_public[0].id).not_to eq(dossier.champs_public[0].id)
|
||||||
it { expect(new_dossier.champs_public[0].updated_at).to eq(dossier.champs_public[0].updated_at) }
|
expect(new_dossier.champs_public[0].created_at).to eq(dossier.champs_public[0].created_at)
|
||||||
|
expect(new_dossier.champs_public[0].updated_at).to eq(dossier.champs_public[0].updated_at)
|
||||||
|
end
|
||||||
|
|
||||||
context "piece justificative champ" do
|
context "piece justificative champ" do
|
||||||
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
|
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
|
||||||
|
@ -253,8 +285,10 @@ RSpec.describe DossierCloneConcern do
|
||||||
forked_dossier.assign_to_groupe_instructeur(dossier.procedure.defaut_groupe_instructeur, DossierAssignment.modes.fetch(:manual))
|
forked_dossier.assign_to_groupe_instructeur(dossier.procedure.defaut_groupe_instructeur, DossierAssignment.modes.fetch(:manual))
|
||||||
}
|
}
|
||||||
|
|
||||||
it { is_expected.to eq(added: [], updated: [], removed: []) }
|
it do
|
||||||
it { expect(forked_dossier.forked_with_changes?).to be_truthy }
|
expect(subject).to eq(added: [], updated: [], removed: [])
|
||||||
|
expect(forked_dossier.forked_with_changes?).to be_truthy
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with updated champ' do
|
context 'with updated champ' do
|
||||||
|
@ -262,8 +296,8 @@ RSpec.describe DossierCloneConcern do
|
||||||
|
|
||||||
before { updated_champ.update(value: 'new value') }
|
before { updated_champ.update(value: 'new value') }
|
||||||
|
|
||||||
it { is_expected.to eq(added: [], updated: [updated_champ], removed: []) }
|
|
||||||
it 'forked_with_changes? should reflect dossier state' do
|
it 'forked_with_changes? should reflect dossier state' do
|
||||||
|
expect(subject).to eq(added: [], updated: [updated_champ], removed: [])
|
||||||
expect(dossier.forked_with_changes?).to be_falsey
|
expect(dossier.forked_with_changes?).to be_falsey
|
||||||
expect(forked_dossier.forked_with_changes?).to be_truthy
|
expect(forked_dossier.forked_with_changes?).to be_truthy
|
||||||
expect(updated_champ.forked_with_changes?).to be_truthy
|
expect(updated_champ.forked_with_changes?).to be_truthy
|
||||||
|
@ -306,17 +340,15 @@ RSpec.describe DossierCloneConcern do
|
||||||
end
|
end
|
||||||
updated_champ.update(value: 'new value')
|
updated_champ.update(value: 'new value')
|
||||||
updated_repetition_champ.update(value: 'new value in repetition')
|
updated_repetition_champ.update(value: 'new value in repetition')
|
||||||
|
dossier.debounce_index_search_terms_flag.remove
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect { subject }.to change { dossier.reload.champs.size }.by(0) }
|
it { expect { subject }.to change { dossier.reload.champs.size }.by(0) }
|
||||||
it { expect { subject }.not_to change { dossier.reload.champs.order(:created_at).reject { _1.stable_id.in?([99, 994]) }.map(&:value) } }
|
it { expect { subject }.not_to change { dossier.reload.champs.order(:created_at).reject { _1.stable_id.in?([99, 994]) }.map(&:value) } }
|
||||||
|
it { expect { subject }.to have_enqueued_job(DossierIndexSearchTermsJob).with(dossier) }
|
||||||
it { expect { subject }.to change { dossier.reload.champs.find { _1.stable_id == 99 }.value }.from('old value').to('new value') }
|
it { expect { subject }.to change { dossier.reload.champs.find { _1.stable_id == 99 }.value }.from('old value').to('new value') }
|
||||||
it { expect { subject }.to change { dossier.reload.champs.find { _1.stable_id == 994 }.value }.from('old value').to('new value in repetition') }
|
it { expect { subject }.to change { dossier.reload.champs.find { _1.stable_id == 994 }.value }.from('old value').to('new value in repetition') }
|
||||||
|
|
||||||
it 'update dossier search terms' do
|
|
||||||
expect { subject }.to have_enqueued_job(DossierUpdateSearchTermsJob).with(dossier)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'fork is hidden after merge' do
|
it 'fork is hidden after merge' do
|
||||||
subject
|
subject
|
||||||
expect(forked_dossier.reload.hidden_by_reason).to eq("stale_fork")
|
expect(forked_dossier.reload.hidden_by_reason).to eq("stale_fork")
|
||||||
|
|
|
@ -2,9 +2,7 @@ describe DossierSearchableConcern do
|
||||||
let(:champ_public) { dossier.champs_public.first }
|
let(:champ_public) { dossier.champs_public.first }
|
||||||
let(:champ_private) { dossier.champs_private.first }
|
let(:champ_private) { dossier.champs_private.first }
|
||||||
|
|
||||||
subject { dossier }
|
describe '#index_search_terms' do
|
||||||
|
|
||||||
describe '#update_search_terms' do
|
|
||||||
let(:etablissement) { dossier.etablissement }
|
let(:etablissement) { dossier.etablissement }
|
||||||
let(:dossier) { create(:dossier, :with_entreprise, user: user) }
|
let(:dossier) { create(:dossier, :with_entreprise, user: user) }
|
||||||
let(:etablissement) { build(:etablissement, entreprise_nom: 'Dupont', entreprise_prenom: 'Thomas', association_rna: '12345', association_titre: 'asso de test', association_objet: 'tests unitaires') }
|
let(:etablissement) { build(:etablissement, entreprise_nom: 'Dupont', entreprise_prenom: 'Thomas', association_rna: '12345', association_titre: 'asso de test', association_objet: 'tests unitaires') }
|
||||||
|
@ -13,29 +11,72 @@ describe DossierSearchableConcern do
|
||||||
let(:france_connect_information) { build(:france_connect_information, given_name: 'Chris', family_name: 'Harrisson') }
|
let(:france_connect_information) { build(:france_connect_information, given_name: 'Chris', family_name: 'Harrisson') }
|
||||||
let(:user) { build(:user, france_connect_informations: [france_connect_information]) }
|
let(:user) { build(:user, france_connect_informations: [france_connect_information]) }
|
||||||
|
|
||||||
before do
|
let(:result) do
|
||||||
champ_public.update_attribute(:value, "champ public")
|
Dossier.connection.execute(
|
||||||
champ_private.update_attribute(:value, "champ privé")
|
Dossier.sanitize_sql_array(["SELECT search_terms, private_search_terms FROM dossiers WHERE id = :id", id: dossier.id])
|
||||||
|
).first
|
||||||
dossier.update_search_terms
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(dossier.search_terms).to eq("#{user.email} champ public #{etablissement.entreprise_siren} #{etablissement.entreprise_numero_tva_intracommunautaire} #{etablissement.entreprise_forme_juridique} #{etablissement.entreprise_forme_juridique_code} #{etablissement.entreprise_nom_commercial} #{etablissement.entreprise_raison_sociale} #{etablissement.entreprise_siret_siege_social} #{etablissement.entreprise_nom} #{etablissement.entreprise_prenom} #{etablissement.association_rna} #{etablissement.association_titre} #{etablissement.association_objet} #{etablissement.siret} #{etablissement.naf} #{etablissement.libelle_naf} #{etablissement.adresse} #{etablissement.code_postal} #{etablissement.localite} #{etablissement.code_insee_localite}") }
|
it "update columns" do
|
||||||
it { expect(dossier.private_search_terms).to eq('champ privé') }
|
champ_public.update_attribute(:value, "champ public")
|
||||||
|
champ_private.update_attribute(:value, "champ privé")
|
||||||
|
perform_enqueued_jobs(only: DossierIndexSearchTermsJob)
|
||||||
|
|
||||||
|
expect(result["search_terms"]).to eq("#{user.email} champ public #{etablissement.entreprise_siren} #{etablissement.entreprise_numero_tva_intracommunautaire} #{etablissement.entreprise_forme_juridique} #{etablissement.entreprise_forme_juridique_code} #{etablissement.entreprise_nom_commercial} #{etablissement.entreprise_raison_sociale} #{etablissement.entreprise_siret_siege_social} #{etablissement.entreprise_nom} #{etablissement.entreprise_prenom} #{etablissement.association_rna} #{etablissement.association_titre} #{etablissement.association_objet} #{etablissement.siret} #{etablissement.naf} #{etablissement.libelle_naf} #{etablissement.adresse} #{etablissement.code_postal} #{etablissement.localite} #{etablissement.code_insee_localite}")
|
||||||
|
expect(result["private_search_terms"]).to eq('champ privé')
|
||||||
|
end
|
||||||
|
|
||||||
context 'with an update' do
|
context 'with an update' do
|
||||||
before do
|
before do
|
||||||
|
stub_const("DossierSearchableConcern::SEARCH_TERMS_DEBOUNCE", 1.second)
|
||||||
|
|
||||||
|
# dossier creation trigger a first indexation and flag,
|
||||||
|
# so we we have to remove this flag
|
||||||
|
dossier.debounce_index_search_terms_flag.remove
|
||||||
|
end
|
||||||
|
|
||||||
|
it "update columns en construction" do
|
||||||
dossier.update(
|
dossier.update(
|
||||||
champs_public_attributes: [{ id: champ_public.id, value: 'nouvelle valeur publique' }],
|
champs_public_attributes: [{ id: champ_public.id, value: 'nouvelle valeur publique' }],
|
||||||
champs_private_attributes: [{ id: champ_private.id, value: 'nouvelle valeur privee' }]
|
champs_private_attributes: [{ id: champ_private.id, value: 'nouvelle valeur privee' }]
|
||||||
)
|
)
|
||||||
|
|
||||||
perform_enqueued_jobs(only: DossierUpdateSearchTermsJob)
|
assert_enqueued_jobs(1, only: DossierIndexSearchTermsJob) do
|
||||||
dossier.reload
|
dossier.passer_en_construction
|
||||||
|
end
|
||||||
|
|
||||||
|
perform_enqueued_jobs(only: DossierIndexSearchTermsJob)
|
||||||
|
|
||||||
|
expect(result["search_terms"]).to include('nouvelle valeur publique')
|
||||||
|
expect(result["private_search_terms"]).to include('nouvelle valeur privee')
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(dossier.search_terms).to include('nouvelle valeur publique') }
|
it "debounce jobs" do
|
||||||
it { expect(dossier.private_search_terms).to include('nouvelle valeur privee') }
|
assert_enqueued_jobs(1, only: DossierIndexSearchTermsJob) do
|
||||||
|
3.times { dossier.index_search_terms_later }
|
||||||
|
end
|
||||||
|
|
||||||
|
# wait redis key expiration
|
||||||
|
sleep 1.01.seconds
|
||||||
|
|
||||||
|
assert_enqueued_jobs(1, only: DossierIndexSearchTermsJob) do
|
||||||
|
dossier.index_search_terms_later
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'mandataire' do
|
||||||
|
it "update columns" do
|
||||||
|
dossier.debounce_index_search_terms_flag.remove
|
||||||
|
|
||||||
|
assert_enqueued_jobs(1, only: DossierIndexSearchTermsJob) do
|
||||||
|
dossier.update!(mandataire_first_name: "Chris")
|
||||||
|
end
|
||||||
|
|
||||||
|
perform_enqueued_jobs(only: DossierIndexSearchTermsJob)
|
||||||
|
|
||||||
|
expect(result["search_terms"]).to include("Chris")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -899,7 +899,7 @@ describe Dossier, type: :model do
|
||||||
dossier.procedure.update_column(:web_hook_url, '/webhook.json')
|
dossier.procedure.update_column(:web_hook_url, '/webhook.json')
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
dossier.update_column(:search_terms, 'bonjour')
|
dossier.update_column(:conservation_extension, 'P1W')
|
||||||
}.to_not have_enqueued_job(WebHookJob)
|
}.to_not have_enqueued_job(WebHookJob)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
|
@ -907,7 +907,7 @@ describe Dossier, type: :model do
|
||||||
}.to have_enqueued_job(WebHookJob).with(dossier.procedure.id, dossier.id, 'en_construction', anything)
|
}.to have_enqueued_job(WebHookJob).with(dossier.procedure.id, dossier.id, 'en_construction', anything)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
dossier.update_column(:search_terms, 'bonjour2')
|
dossier.update_column(:conservation_extension, 'P2W')
|
||||||
}.to_not have_enqueued_job(WebHookJob)
|
}.to_not have_enqueued_job(WebHookJob)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
|
@ -995,28 +995,28 @@ describe Dossier, type: :model do
|
||||||
allow(NotificationMailer).to receive(:send_accepte_notification).and_return(double(deliver_later: true))
|
allow(NotificationMailer).to receive(:send_accepte_notification).and_return(double(deliver_later: true))
|
||||||
allow(dossier).to receive(:build_attestation).and_return(attestation)
|
allow(dossier).to receive(:build_attestation).and_return(attestation)
|
||||||
|
|
||||||
Timecop.freeze(now)
|
travel_to now
|
||||||
dossier.accepter!(instructeur: instructeur, motivation: 'motivation')
|
dossier.accepter!(instructeur: instructeur, motivation: 'motivation')
|
||||||
dossier.reload
|
dossier.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
after { Timecop.return }
|
it "update attributes" do
|
||||||
|
expect(dossier.traitements.last.motivation).to eq('motivation')
|
||||||
it { expect(dossier.traitements.last.motivation).to eq('motivation') }
|
expect(dossier.motivation).to eq('motivation')
|
||||||
it { expect(dossier.motivation).to eq('motivation') }
|
expect(dossier.traitements.last.instructeur_email).to eq(instructeur.email)
|
||||||
it { expect(dossier.traitements.last.instructeur_email).to eq(instructeur.email) }
|
expect(dossier.en_instruction_at).to eq(dossier.en_instruction_at)
|
||||||
it { expect(dossier.en_instruction_at).to eq(dossier.en_instruction_at) }
|
expect(dossier.traitements.last.processed_at).to eq(now)
|
||||||
it { expect(dossier.traitements.last.processed_at).to eq(now) }
|
expect(dossier.processed_at).to eq(now)
|
||||||
it { expect(dossier.processed_at).to eq(now) }
|
expect(dossier.state).to eq('accepte')
|
||||||
it { expect(dossier.state).to eq('accepte') }
|
expect(last_operation.operation).to eq('accepter')
|
||||||
it { expect(last_operation.operation).to eq('accepter') }
|
expect(last_operation.automatic_operation?).to be_falsey
|
||||||
it { expect(last_operation.automatic_operation?).to be_falsey }
|
expect(operation_serialized['operation']).to eq('accepter')
|
||||||
it { expect(operation_serialized['operation']).to eq('accepter') }
|
expect(operation_serialized['dossier_id']).to eq(dossier.id)
|
||||||
it { expect(operation_serialized['dossier_id']).to eq(dossier.id) }
|
expect(operation_serialized['executed_at']).to eq(last_operation.executed_at.iso8601)
|
||||||
it { expect(operation_serialized['executed_at']).to eq(last_operation.executed_at.iso8601) }
|
expect(NotificationMailer).to have_received(:send_accepte_notification).with(dossier)
|
||||||
it { expect(NotificationMailer).to have_received(:send_accepte_notification).with(dossier) }
|
expect(dossier.attestation).to eq(attestation)
|
||||||
it { expect(dossier.attestation).to eq(attestation) }
|
expect(dossier.commentaires.count).to eq(1)
|
||||||
it { expect(dossier.commentaires.count).to eq(1) }
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#accepter_automatiquement!' do
|
describe '#accepter_automatiquement!' do
|
||||||
|
@ -1642,24 +1642,24 @@ describe Dossier, type: :model do
|
||||||
let(:last_operation) { dossier.dossier_operation_logs.last }
|
let(:last_operation) { dossier.dossier_operation_logs.last }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
Timecop.freeze
|
freeze_time
|
||||||
allow(NotificationMailer).to receive(:send_repasser_en_instruction_notification).and_return(double(deliver_later: true))
|
allow(NotificationMailer).to receive(:send_repasser_en_instruction_notification).and_return(double(deliver_later: true))
|
||||||
dossier.repasser_en_instruction!(instructeur: instructeur)
|
dossier.repasser_en_instruction!(instructeur: instructeur)
|
||||||
dossier.reload
|
dossier.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(dossier.state).to eq('en_instruction') }
|
it "update attributes" do
|
||||||
it { expect(dossier.archived).to be_falsey }
|
expect(dossier.state).to eq('en_instruction')
|
||||||
it { expect(dossier.motivation).to be_nil }
|
expect(dossier.archived).to be_falsey
|
||||||
it { expect(dossier.justificatif_motivation.attached?).to be_falsey }
|
expect(dossier.motivation).to be_nil
|
||||||
it { expect(dossier.attestation).to be_nil }
|
expect(dossier.justificatif_motivation.attached?).to be_falsey
|
||||||
it { expect(dossier.sva_svr_decision_on).to be_nil }
|
expect(dossier.attestation).to be_nil
|
||||||
it { expect(dossier.termine_close_to_expiration_notice_sent_at).to be_nil }
|
expect(dossier.sva_svr_decision_on).to be_nil
|
||||||
it { expect(last_operation.operation).to eq('repasser_en_instruction') }
|
expect(dossier.termine_close_to_expiration_notice_sent_at).to be_nil
|
||||||
it { expect(last_operation.data['author']['email']).to eq(instructeur.email) }
|
expect(last_operation.operation).to eq('repasser_en_instruction')
|
||||||
it { expect(NotificationMailer).to have_received(:send_repasser_en_instruction_notification).with(dossier) }
|
expect(last_operation.data['author']['email']).to eq(instructeur.email)
|
||||||
|
expect(NotificationMailer).to have_received(:send_repasser_en_instruction_notification).with(dossier)
|
||||||
after { Timecop.return }
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#notify_draft_not_submitted' do
|
describe '#notify_draft_not_submitted' do
|
||||||
|
|
|
@ -115,6 +115,16 @@ describe Etablissement do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'update search terms' do
|
||||||
|
let(:etablissement) { create(:etablissement, dossier: build(:dossier)) }
|
||||||
|
|
||||||
|
it "schedule update search terms" do
|
||||||
|
assert_enqueued_jobs(1, only: DossierIndexSearchTermsJob) do
|
||||||
|
etablissement.update(entreprise_nom: "nom")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def csv_to_array_of_hash(lines)
|
def csv_to_array_of_hash(lines)
|
||||||
|
|
|
@ -7,41 +7,50 @@ describe Individual do
|
||||||
describe "#save" do
|
describe "#save" do
|
||||||
let(:individual) { build(:individual) }
|
let(:individual) { build(:individual) }
|
||||||
|
|
||||||
subject { individual.save }
|
subject do
|
||||||
|
individual.save
|
||||||
|
individual
|
||||||
|
end
|
||||||
|
|
||||||
context "with birthdate" do
|
context "with birthdate" do
|
||||||
before do
|
before do
|
||||||
individual.birthdate = birthdate_from_user
|
individual.birthdate = birthdate_from_user
|
||||||
subject
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "and the format is dd/mm/yyy " do
|
context "and the format is dd/mm/yyy " do
|
||||||
let(:birthdate_from_user) { "12/11/1980" }
|
let(:birthdate_from_user) { "12/11/1980" }
|
||||||
|
|
||||||
it { expect(individual.birthdate).to eq(Date.new(1980, 11, 12)) }
|
it { expect(subject.birthdate).to eq(Date.new(1980, 11, 12)) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "and the format is ISO" do
|
context "and the format is ISO" do
|
||||||
let(:birthdate_from_user) { "1980-11-12" }
|
let(:birthdate_from_user) { "1980-11-12" }
|
||||||
|
|
||||||
it { expect(individual.birthdate).to eq(Date.new(1980, 11, 12)) }
|
it { expect(subject.birthdate).to eq(Date.new(1980, 11, 12)) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "and the format is WTF" do
|
context "and the format is WTF" do
|
||||||
let(:birthdate_from_user) { "1980 1 12" }
|
let(:birthdate_from_user) { "1980 1 12" }
|
||||||
|
|
||||||
it { expect(individual.birthdate).to be_nil }
|
it { expect(subject.birthdate).to be_nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when an individual has an invalid notification_method' do
|
it "schedule index search terms" do
|
||||||
let(:invalid_individual) { build(:individual, notification_method: 'invalid_method') }
|
subject.dossier.debounce_index_search_terms_flag.remove
|
||||||
|
assert_enqueued_jobs(1, only: DossierIndexSearchTermsJob) do
|
||||||
it 'raises an ArgumentError' do
|
individual.update(nom: "new name")
|
||||||
expect {
|
|
||||||
invalid_individual.valid?
|
|
||||||
}.to raise_error(ArgumentError, "'invalid_method' is not a valid notification_method")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when an individual has an invalid notification_method' do
|
||||||
|
let(:invalid_individual) { build(:individual, notification_method: 'invalid_method') }
|
||||||
|
|
||||||
|
it 'raises an ArgumentError' do
|
||||||
|
expect {
|
||||||
|
invalid_individual.valid?
|
||||||
|
}.to raise_error(ArgumentError, "'invalid_method' is not a valid notification_method")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,23 +15,33 @@ describe DossierSearchService do
|
||||||
before do
|
before do
|
||||||
instructeur_1.assign_to_procedure(procedure_1)
|
instructeur_1.assign_to_procedure(procedure_1)
|
||||||
instructeur_2.assign_to_procedure(procedure_2)
|
instructeur_2.assign_to_procedure(procedure_2)
|
||||||
|
|
||||||
|
# create dossier before performing jobs
|
||||||
|
# because let!() syntax is executed after "before" callback
|
||||||
|
dossier_0
|
||||||
|
dossier_1
|
||||||
|
dossier_2
|
||||||
|
dossier_3
|
||||||
|
dossier_archived
|
||||||
|
|
||||||
|
perform_enqueued_jobs(only: DossierIndexSearchTermsJob)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:procedure_1) { create(:procedure, :published, administrateur: administrateur_1) }
|
let(:procedure_1) { create(:procedure, :published, administrateur: administrateur_1) }
|
||||||
let(:procedure_2) { create(:procedure, :published, administrateur: administrateur_2) }
|
let(:procedure_2) { create(:procedure, :published, administrateur: administrateur_2) }
|
||||||
|
|
||||||
let!(:dossier_0) { create(:dossier, state: Dossier.states.fetch(:brouillon), procedure: procedure_1, user: create(:user, email: 'brouillon@clap.fr')) }
|
let(:dossier_0) { create(:dossier, state: Dossier.states.fetch(:brouillon), procedure: procedure_1, user: create(:user, email: 'brouillon@clap.fr')) }
|
||||||
|
|
||||||
let!(:etablissement_1) { create(:etablissement, entreprise_raison_sociale: 'OCTO Academy', siret: '41636169600051') }
|
let(:etablissement_1) { create(:etablissement, entreprise_raison_sociale: 'OCTO Academy', siret: '41636169600051') }
|
||||||
let!(:dossier_1) { create(:dossier, :en_construction, procedure: procedure_1, user: create(:user, email: 'contact@test.com'), etablissement: etablissement_1) }
|
let(:dossier_1) { create(:dossier, :en_construction, procedure: procedure_1, user: create(:user, email: 'contact@test.com'), etablissement: etablissement_1) }
|
||||||
|
|
||||||
let!(:etablissement_2) { create(:etablissement, entreprise_raison_sociale: 'Plop octo', siret: '41816602300012') }
|
let(:etablissement_2) { create(:etablissement, entreprise_raison_sociale: 'Plop octo', siret: '41816602300012') }
|
||||||
let!(:dossier_2) { create(:dossier, :en_construction, procedure: procedure_1, user: create(:user, email: 'plop@gmail.com'), etablissement: etablissement_2) }
|
let(:dossier_2) { create(:dossier, :en_construction, procedure: procedure_1, user: create(:user, email: 'plop@gmail.com'), etablissement: etablissement_2) }
|
||||||
|
|
||||||
let!(:etablissement_3) { create(:etablissement, entreprise_raison_sociale: 'OCTO Technology', siret: '41816609600051') }
|
let(:etablissement_3) { create(:etablissement, entreprise_raison_sociale: 'OCTO Technology', siret: '41816609600051') }
|
||||||
let!(:dossier_3) { create(:dossier, :en_construction, procedure: procedure_2, user: create(:user, email: 'peace@clap.fr'), etablissement: etablissement_3) }
|
let(:dossier_3) { create(:dossier, :en_construction, procedure: procedure_2, user: create(:user, email: 'peace@clap.fr'), etablissement: etablissement_3) }
|
||||||
|
|
||||||
let!(:dossier_archived) { create(:dossier, :en_construction, procedure: procedure_1, archived: true, user: create(:user, email: 'archived@clap.fr')) }
|
let(:dossier_archived) { create(:dossier, :en_construction, procedure: procedure_1, archived: true, user: create(:user, email: 'archived@clap.fr')) }
|
||||||
|
|
||||||
describe 'search is empty' do
|
describe 'search is empty' do
|
||||||
let(:terms) { '' }
|
let(:terms) { '' }
|
||||||
|
@ -99,6 +109,16 @@ describe DossierSearchService do
|
||||||
describe '#matching_dossiers_for_user' do
|
describe '#matching_dossiers_for_user' do
|
||||||
subject { liste_dossiers }
|
subject { liste_dossiers }
|
||||||
|
|
||||||
|
before do
|
||||||
|
dossier_0
|
||||||
|
dossier_0b
|
||||||
|
dossier_1
|
||||||
|
dossier_2
|
||||||
|
dossier_3
|
||||||
|
dossier_archived
|
||||||
|
perform_enqueued_jobs(only: DossierIndexSearchTermsJob)
|
||||||
|
end
|
||||||
|
|
||||||
let(:liste_dossiers) do
|
let(:liste_dossiers) do
|
||||||
described_class.matching_dossiers_for_user(terms, user_1)
|
described_class.matching_dossiers_for_user(terms, user_1)
|
||||||
end
|
end
|
||||||
|
@ -109,19 +129,19 @@ describe DossierSearchService do
|
||||||
let(:procedure_1) { create(:procedure, :published) }
|
let(:procedure_1) { create(:procedure, :published) }
|
||||||
let(:procedure_2) { create(:procedure, :published) }
|
let(:procedure_2) { create(:procedure, :published) }
|
||||||
|
|
||||||
let!(:dossier_0) { create(:dossier, state: Dossier.states.fetch(:brouillon), procedure: procedure_1, user: user_1) }
|
let(:dossier_0) { create(:dossier, state: Dossier.states.fetch(:brouillon), procedure: procedure_1, user: user_1) }
|
||||||
let!(:dossier_0b) { create(:dossier, state: Dossier.states.fetch(:brouillon), procedure: procedure_1, user: user_2) }
|
let(:dossier_0b) { create(:dossier, state: Dossier.states.fetch(:brouillon), procedure: procedure_1, user: user_2) }
|
||||||
|
|
||||||
let!(:etablissement_1) { create(:etablissement, entreprise_raison_sociale: 'OCTO Academy', siret: '41636169600051') }
|
let(:etablissement_1) { create(:etablissement, entreprise_raison_sociale: 'OCTO Academy', siret: '41636169600051') }
|
||||||
let!(:dossier_1) { create(:dossier, state: Dossier.states.fetch(:en_construction), procedure: procedure_1, user: user_1, etablissement: etablissement_1) }
|
let(:dossier_1) { create(:dossier, state: Dossier.states.fetch(:en_construction), procedure: procedure_1, user: user_1, etablissement: etablissement_1) }
|
||||||
|
|
||||||
let!(:etablissement_2) { create(:etablissement, entreprise_raison_sociale: 'Plop octo', siret: '41816602300012') }
|
let(:etablissement_2) { create(:etablissement, entreprise_raison_sociale: 'Plop octo', siret: '41816602300012') }
|
||||||
let!(:dossier_2) { create(:dossier, state: Dossier.states.fetch(:en_construction), procedure: procedure_1, user: user_1, etablissement: etablissement_2) }
|
let(:dossier_2) { create(:dossier, state: Dossier.states.fetch(:en_construction), procedure: procedure_1, user: user_1, etablissement: etablissement_2) }
|
||||||
|
|
||||||
let!(:etablissement_3) { create(:etablissement, entreprise_raison_sociale: 'OCTO Technology', siret: '41816609600051') }
|
let(:etablissement_3) { create(:etablissement, entreprise_raison_sociale: 'OCTO Technology', siret: '41816609600051') }
|
||||||
let!(:dossier_3) { create(:dossier, state: Dossier.states.fetch(:en_construction), procedure: procedure_2, user: user_1, etablissement: etablissement_3) }
|
let(:dossier_3) { create(:dossier, state: Dossier.states.fetch(:en_construction), procedure: procedure_2, user: user_1, etablissement: etablissement_3) }
|
||||||
|
|
||||||
let!(:dossier_archived) { create(:dossier, state: Dossier.states.fetch(:en_construction), procedure: procedure_1, archived: true, user: user_1) }
|
let(:dossier_archived) { create(:dossier, state: Dossier.states.fetch(:en_construction), procedure: procedure_1, archived: true, user: user_1) }
|
||||||
|
|
||||||
describe 'search is empty' do
|
describe 'search is empty' do
|
||||||
let(:terms) { '' }
|
let(:terms) { '' }
|
||||||
|
|
|
@ -134,6 +134,8 @@ describe 'The routing with rules', js: true do
|
||||||
user_send_dossier(litteraire_user, 'littéraire')
|
user_send_dossier(litteraire_user, 'littéraire')
|
||||||
user_send_dossier(artistique_user, 'artistique')
|
user_send_dossier(artistique_user, 'artistique')
|
||||||
|
|
||||||
|
perform_enqueued_jobs(only: DossierIndexSearchTermsJob)
|
||||||
|
|
||||||
# the litteraires instructeurs only manage the litteraires dossiers
|
# the litteraires instructeurs only manage the litteraires dossiers
|
||||||
register_instructeur_and_log_in(victor.email)
|
register_instructeur_and_log_in(victor.email)
|
||||||
click_on procedure.libelle
|
click_on procedure.libelle
|
||||||
|
|
|
@ -162,6 +162,7 @@ describe 'Invitations' do
|
||||||
before do
|
before do
|
||||||
navigate_to_invited_dossier(invite)
|
navigate_to_invited_dossier(invite)
|
||||||
visit dossiers_path
|
visit dossiers_path
|
||||||
|
perform_enqueued_jobs(only: DossierIndexSearchTermsJob)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can search by id and it displays the dossier" do
|
it "can search by id and it displays the dossier" do
|
||||||
|
|
|
@ -268,6 +268,7 @@ describe 'user access to the list of their dossiers', js: true do
|
||||||
context 'when it matches multiple dossiers' do
|
context 'when it matches multiple dossiers' do
|
||||||
let!(:dossier_with_champs) { create(:dossier, :with_populated_champs, :en_construction, user: user) }
|
let!(:dossier_with_champs) { create(:dossier, :with_populated_champs, :en_construction, user: user) }
|
||||||
before do
|
before do
|
||||||
|
perform_enqueued_jobs(only: DossierIndexSearchTermsJob)
|
||||||
find('.fr-search-bar .fr-btn').click
|
find('.fr-search-bar .fr-btn').click
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue