demarches-normaliennes/spec/models/dossier_spec.rb

1263 lines
50 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

describe Dossier do
include ActiveJob::TestHelper
let(:user) { create(:user) }
describe 'scopes' do
describe '.default_scope' do
let!(:dossier) { create(:dossier) }
let!(:discarded_dossier) { create(:dossier, :discarded) }
subject { Dossier.all }
it { is_expected.to match_array([dossier]) }
end
describe '.without_followers' do
let!(:dossier_with_follower) { create(:dossier, :followed, :with_entreprise, user: user) }
let!(:dossier_without_follower) { create(:dossier, :with_entreprise, user: user) }
it { expect(Dossier.without_followers.to_a).to eq([dossier_without_follower]) }
end
end
describe 'validations' do
let(:procedure) { create(:procedure, :for_individual) }
subject(:dossier) { create(:dossier, procedure: procedure) }
it { is_expected.to validate_presence_of(:individual) }
end
describe 'with_champs' do
let(:procedure) { create(:procedure) }
let(:dossier) { Dossier.create(user: create(:user), groupe_instructeur: procedure.defaut_groupe_instructeur) }
before do
create(:type_de_champ, libelle: 'l1', order_place: 1, procedure: procedure)
create(:type_de_champ, libelle: 'l3', order_place: 3, procedure: procedure)
create(:type_de_champ, libelle: 'l2', order_place: 2, procedure: procedure)
end
it do
expect(Dossier.with_champs.find(dossier.id).champs.map(&:libelle)).to match(['l1', 'l2', 'l3'])
end
end
describe 'brouillon_close_to_expiration' do
let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6) }
let!(:young_dossier) { create(:dossier, :en_construction, procedure: procedure) }
let!(:expiring_dossier) { create(:dossier, created_at: 170.days.ago, procedure: procedure) }
let!(:just_expired_dossier) { create(:dossier, created_at: (6.months + 1.hour + 10.seconds).ago, procedure: procedure) }
let!(:long_expired_dossier) { create(:dossier, created_at: 1.year.ago, procedure: procedure) }
subject { Dossier.brouillon_close_to_expiration }
it do
is_expected.not_to include(young_dossier)
is_expected.to include(expiring_dossier)
is_expected.to include(just_expired_dossier)
is_expected.to include(long_expired_dossier)
end
end
describe 'en_construction_close_to_expiration' do
let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6) }
let!(:young_dossier) { create(:dossier, procedure: procedure) }
let!(:expiring_dossier) { create(:dossier, :en_construction, en_construction_at: 170.days.ago, procedure: procedure) }
let!(:just_expired_dossier) { create(:dossier, :en_construction, en_construction_at: (6.months + 1.hour + 10.seconds).ago, procedure: procedure) }
let!(:long_expired_dossier) { create(:dossier, :en_construction, en_construction_at: 1.year.ago, procedure: procedure) }
subject { Dossier.en_construction_close_to_expiration }
it do
is_expected.not_to include(young_dossier)
is_expected.to include(expiring_dossier)
is_expected.to include(just_expired_dossier)
is_expected.to include(long_expired_dossier)
end
context 'does not include an expiring dossier that has been postponed' do
before do
expiring_dossier.update(en_construction_conservation_extension: 1.month)
expiring_dossier.reload
end
it { is_expected.not_to include(expiring_dossier) }
end
end
describe 'en_instruction_close_to_expiration' do
let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6) }
let!(:young_dossier) { create(:dossier, procedure: procedure) }
let!(:expiring_dossier) { create(:dossier, :en_instruction, en_instruction_at: 170.days.ago, procedure: procedure) }
let!(:just_expired_dossier) { create(:dossier, :en_instruction, en_instruction_at: (6.months + 1.hour + 10.seconds).ago, procedure: procedure) }
let!(:long_expired_dossier) { create(:dossier, :en_instruction, en_instruction_at: 1.year.ago, procedure: procedure) }
subject { Dossier.en_instruction_close_to_expiration }
it do
is_expected.not_to include(young_dossier)
is_expected.to include(expiring_dossier)
is_expected.to include(just_expired_dossier)
is_expected.to include(long_expired_dossier)
end
end
describe 'with_notifications' do
let(:dossier) { create(:dossier) }
let(:instructeur) { create(:instructeur) }
before do
create(:follow, dossier: dossier, instructeur: instructeur, messagerie_seen_at: 2.hours.ago)
end
subject { instructeur.followed_dossiers.with_notifications }
context('without changes') do
it { is_expected.to eq [] }
end
context('with changes') do
before { dossier.commentaires << create(:commentaire, email: 'test@exemple.fr') }
it { is_expected.to match([dossier]) }
end
end
describe 'methods' do
let(:dossier) { create(:dossier, :with_entreprise, user: user) }
let(:etablissement) { dossier.etablissement }
subject { dossier }
describe '#update_search_terms' do
let(:etablissement) { build(:etablissement, entreprise_nom: 'Dupont', entreprise_prenom: 'Thomas', association_rna: '12345', association_titre: 'asso de test', association_objet: 'tests unitaires') }
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) }
let(:dossier) { create(:dossier, etablissement: etablissement, user: user, procedure: procedure) }
let(:france_connect_information) { build(:france_connect_information, given_name: 'Chris', family_name: 'Harrisson') }
let(:user) { build(:user, france_connect_information: france_connect_information) }
let(:champ_public) { dossier.champs.first }
let(:champ_private) { dossier.champs_private.first }
before do
champ_public.update_attribute(:value, "champ public")
champ_private.update_attribute(:value, "champ privé")
dossier.update_search_terms
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 { expect(dossier.private_search_terms).to eq('champ privé') }
context 'with an update' do
before do
dossier.update(
champs_attributes: [{ id: champ_public.id, value: 'nouvelle valeur publique' }],
champs_private_attributes: [{ id: champ_private.id, value: 'nouvelle valeur privee' }]
)
end
it { expect(dossier.search_terms).to include('nouvelle valeur publique') }
it { expect(dossier.private_search_terms).to include('nouvelle valeur privee') }
end
end
describe '#create' do
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) }
let(:dossier) { create(:dossier, procedure: procedure, user: user) }
it 'builds public and private champs' do
expect(dossier.champs.count).to eq(1)
expect(dossier.champs_private.count).to eq(1)
end
context 'when the dossier belongs to a procedure for individuals' do
let(:procedure) { create(:procedure, :with_type_de_champ, for_individual: true) }
it 'creates a default individual' do
expect(dossier.individual).to be_present
expect(dossier.individual.nom).to be_nil
expect(dossier.individual.prenom).to be_nil
expect(dossier.individual.gender).to be_nil
end
context 'and the user signs-in using France Connect' do
let(:france_connect_information) { build(:france_connect_information) }
let(:user) { build(:user, france_connect_information: france_connect_information) }
it 'fills the individual with the informations from France Connect' do
expect(dossier.individual.nom).to eq('DUBOIS')
expect(dossier.individual.prenom).to eq('Angela Claire Louise')
expect(dossier.individual.gender).to eq(Individual::GENDER_FEMALE)
end
end
end
context 'when the dossier belongs to a procedure for moral personas' do
let(:procedure) { create(:procedure, :with_type_de_champ, for_individual: false) }
it 'doesnt create a individual' do
expect(dossier.individual).to be_nil
end
end
end
end
context 'when dossier is followed' do
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) }
let(:instructeur) { create(:instructeur) }
let(:date1) { 1.day.ago }
let(:date2) { 1.hour.ago }
let(:date3) { 1.minute.ago }
let(:dossier) { create(:dossier, :with_entreprise, user: user, procedure: procedure, en_construction_at: date1, en_instruction_at: date2, processed_at: date3, motivation: "Motivation") }
let!(:follow) { create(:follow, instructeur: instructeur, dossier: dossier) }
describe "followers_instructeurs" do
let(:non_following_instructeur) { create(:instructeur) }
subject { dossier.followers_instructeurs }
it { expect(subject).to eq [instructeur] }
it { expect(subject).not_to include(non_following_instructeur) }
end
end
describe '#reset!' do
let!(:dossier) { create :dossier, :with_entreprise, autorisation_donnees: true }
let!(:exercice) { create :exercice, etablissement: dossier.etablissement }
subject { dossier.reset! }
it { expect(dossier.etablissement).not_to be_nil }
it { expect(dossier.etablissement.exercices).not_to be_empty }
it { expect(dossier.etablissement.exercices.size).to eq 1 }
it { expect(dossier.autorisation_donnees).to be_truthy }
it { expect { subject }.to change(Exercice, :count).by(-1) }
it { expect { subject }.to change(Etablissement, :count).by(-1) }
context 'when method reset! is call' do
before do
subject
dossier.reload
end
it { expect(dossier.etablissement).to be_nil }
it { expect(dossier.autorisation_donnees).to be_falsey }
end
end
describe '#champs' do
let(:procedure) { create(:procedure) }
let(:dossier) { Dossier.create(user: create(:user), groupe_instructeur: procedure.defaut_groupe_instructeur) }
before do
create(:type_de_champ, libelle: 'l1', order_place: 1, procedure: procedure)
create(:type_de_champ, libelle: 'l3', order_place: 3, procedure: procedure)
create(:type_de_champ, libelle: 'l2', order_place: 2, procedure: procedure)
end
it { expect(dossier.champs.pluck(:libelle)).to match(['l1', 'l2', 'l3']) }
end
describe '#champs_private' do
let(:procedure) { create :procedure }
let(:dossier) { Dossier.create(user: create(:user), groupe_instructeur: procedure.defaut_groupe_instructeur) }
before do
create :type_de_champ, :private, libelle: 'l1', order_place: 1, procedure: procedure
create :type_de_champ, :private, libelle: 'l3', order_place: 3, procedure: procedure
create :type_de_champ, :private, libelle: 'l2', order_place: 2, procedure: procedure
end
it { expect(dossier.champs_private.pluck(:libelle)).to match(['l1', 'l2', 'l3']) }
end
describe "#text_summary" do
let(:service) { create(:service, nom: 'nom du service') }
let(:procedure) { create(:procedure, libelle: "Démarche", organisation: "Organisme", service: service) }
context 'when the dossier has been en_construction' do
let(:dossier) { create :dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction), en_construction_at: "31/12/2010".to_date }
subject { dossier.text_summary }
it { is_expected.to eq("Dossier déposé le 31/12/2010 sur la démarche Démarche gérée par l'organisme nom du service") }
end
context 'when the dossier has not been en_construction' do
let(:dossier) { create :dossier, procedure: procedure, state: Dossier.states.fetch(:brouillon) }
subject { dossier.text_summary }
it { is_expected.to eq("Dossier en brouillon répondant à la démarche Démarche gérée par l'organisme nom du service") }
end
end
describe '#avis_for' do
let!(:instructeur) { create(:instructeur) }
let!(:procedure) { create(:procedure, :published, instructeurs: [instructeur]) }
let!(:dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction)) }
let!(:expert_1) { create(:instructeur) }
let!(:expert_2) { create(:instructeur) }
context 'when there is a public advice asked from the dossiers instructeur' do
let!(:avis) { Avis.create(dossier: dossier, claimant: instructeur, instructeur: expert_1, confidentiel: false) }
it { expect(dossier.avis_for(instructeur)).to match([avis]) }
it { expect(dossier.avis_for(expert_1)).to match([avis]) }
it { expect(dossier.avis_for(expert_2)).to match([avis]) }
end
context 'when there is a private advice asked from the dossiers instructeur' do
let!(:avis) { Avis.create(dossier: dossier, claimant: instructeur, instructeur: expert_1, confidentiel: true) }
it { expect(dossier.avis_for(instructeur)).to match([avis]) }
it { expect(dossier.avis_for(expert_1)).to match([avis]) }
it { expect(dossier.avis_for(expert_2)).to match([]) }
end
context 'when there is a public advice asked from one expert to another' do
let!(:avis) { Avis.create(dossier: dossier, claimant: expert_1, instructeur: expert_2, confidentiel: false) }
it { expect(dossier.avis_for(instructeur)).to match([avis]) }
it { expect(dossier.avis_for(expert_1)).to match([avis]) }
it { expect(dossier.avis_for(expert_2)).to match([avis]) }
end
context 'when there is a private advice asked from one expert to another' do
let!(:avis) { Avis.create(dossier: dossier, claimant: expert_1, instructeur: expert_2, confidentiel: true) }
it { expect(dossier.avis_for(instructeur)).to match([avis]) }
it { expect(dossier.avis_for(expert_1)).to match([avis]) }
it { expect(dossier.avis_for(expert_2)).to match([avis]) }
end
context 'when they are a lot of advice' do
let!(:avis_1) { Avis.create(dossier: dossier, claimant: expert_1, instructeur: expert_2, confidentiel: false, created_at: Time.zone.parse('10/01/2010')) }
let!(:avis_2) { Avis.create(dossier: dossier, claimant: expert_1, instructeur: expert_2, confidentiel: false, created_at: Time.zone.parse('9/01/2010')) }
let!(:avis_3) { Avis.create(dossier: dossier, claimant: expert_1, instructeur: expert_2, confidentiel: false, created_at: Time.zone.parse('11/01/2010')) }
it { expect(dossier.avis_for(instructeur)).to match([avis_2, avis_1, avis_3]) }
it { expect(dossier.avis_for(expert_1)).to match([avis_2, avis_1, avis_3]) }
end
end
describe '#update_state_dates' do
let(:state) { Dossier.states.fetch(:brouillon) }
let(:dossier) { create(:dossier, state: state) }
let(:beginning_of_day) { Time.zone.now.beginning_of_day }
before { Timecop.freeze(beginning_of_day) }
after { Timecop.return }
context 'when dossier is en_construction' do
before do
dossier.en_construction!
dossier.reload
end
it { expect(dossier.state).to eq(Dossier.states.fetch(:en_construction)) }
it { expect(dossier.en_construction_at).to eq(beginning_of_day) }
it 'should keep first en_construction_at date' do
Timecop.return
dossier.en_instruction!
dossier.en_construction!
expect(dossier.en_construction_at).to eq(beginning_of_day)
end
end
context 'when dossier is en_instruction' do
let(:state) { Dossier.states.fetch(:en_construction) }
before do
dossier.en_instruction!
dossier.reload
end
it { expect(dossier.state).to eq(Dossier.states.fetch(:en_instruction)) }
it { expect(dossier.en_instruction_at).to eq(beginning_of_day) }
it 'should keep first en_instruction_at date if dossier is set to en_construction again' do
Timecop.return
dossier.en_construction!
dossier.en_instruction!
expect(dossier.en_instruction_at).to eq(beginning_of_day)
end
end
shared_examples 'dossier is processed' do |new_state|
before do
dossier.update(state: new_state)
dossier.reload
end
it { expect(dossier.state).to eq(new_state) }
it { expect(dossier.processed_at).to eq(beginning_of_day) }
end
context 'when dossier is accepte' do
let(:state) { Dossier.states.fetch(:en_instruction) }
it_behaves_like 'dossier is processed', Dossier.states.fetch(:accepte)
end
context 'when dossier is refuse' do
let(:state) { Dossier.states.fetch(:en_instruction) }
it_behaves_like 'dossier is processed', Dossier.states.fetch(:refuse)
end
context 'when dossier is sans_suite' do
let(:state) { Dossier.states.fetch(:en_instruction) }
it_behaves_like 'dossier is processed', Dossier.states.fetch(:sans_suite)
end
end
describe '.downloadable_sorted' do
let(:procedure) { create(:procedure) }
let!(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: Dossier.states.fetch(:brouillon)) }
let!(:dossier2) { create(:dossier, :with_entreprise, procedure: procedure, state: Dossier.states.fetch(:en_construction), en_construction_at: Time.zone.parse('03/01/2010')) }
let!(:dossier3) { create(:dossier, :with_entreprise, procedure: procedure, state: Dossier.states.fetch(:en_instruction), en_construction_at: Time.zone.parse('01/01/2010')) }
let!(:dossier4) { create(:dossier, :with_entreprise, procedure: procedure, state: Dossier.states.fetch(:en_instruction), archived: true, en_construction_at: Time.zone.parse('02/01/2010')) }
subject { procedure.dossiers.downloadable_sorted }
it { is_expected.to match([dossier3, dossier4, dossier2]) }
end
describe "#assign_to_groupe_instructeur" do
let(:procedure) { create(:procedure) }
let(:new_groupe_instructeur_new_procedure) { create(:groupe_instructeur) }
let(:new_groupe_instructeur) { create(:groupe_instructeur, procedure: procedure) }
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
it "can change groupe instructeur" do
expect(dossier.assign_to_groupe_instructeur(new_groupe_instructeur_new_procedure)).to be_falsey
expect(dossier.groupe_instructeur).not_to eq(new_groupe_instructeur_new_procedure)
end
it "can not change groupe instructeur if new groupe is from another procedure" do
expect(dossier.assign_to_groupe_instructeur(new_groupe_instructeur)).to be_truthy
expect(dossier.groupe_instructeur).to eq(new_groupe_instructeur)
end
end
describe "#unfollow_stale_instructeurs" do
let(:procedure) { create(:procedure, :published) }
let(:instructeur) { create(:instructeur) }
let(:new_groupe_instructeur) { create(:groupe_instructeur, procedure: procedure) }
let(:instructeur2) { create(:instructeur, groupe_instructeurs: [procedure.defaut_groupe_instructeur, new_groupe_instructeur]) }
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
let(:last_operation) { DossierOperationLog.last }
before do
allow(DossierMailer).to receive(:notify_groupe_instructeur_changed).and_return(double(deliver_later: nil))
end
it "unfollows stale instructeurs when groupe instructeur change" do
instructeur.follow(dossier)
instructeur2.follow(dossier)
dossier.reload.assign_to_groupe_instructeur(new_groupe_instructeur, procedure.administrateurs.first)
expect(dossier.reload.followers_instructeurs).not_to include(instructeur)
expect(dossier.reload.followers_instructeurs).to include(instructeur2)
expect(DossierMailer).to have_received(:notify_groupe_instructeur_changed).with(instructeur, dossier)
expect(DossierMailer).not_to have_received(:notify_groupe_instructeur_changed).with(instructeur2, dossier)
expect(last_operation.operation).to eq("changer_groupe_instructeur")
expect(last_operation.dossier).to eq(dossier)
expect(last_operation.automatic_operation?).to be_falsey
end
end
describe "#send_dossier_received" do
let(:procedure) { create(:procedure) }
let(:dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction)) }
before do
allow(NotificationMailer).to receive(:send_dossier_received).and_return(double(deliver_later: nil))
end
it "sends an email when the dossier becomes en_instruction" do
dossier.en_instruction!
expect(NotificationMailer).to have_received(:send_dossier_received).with(dossier)
end
it "does not an email when the dossier becomes accepte" do
dossier.accepte!
expect(NotificationMailer).to_not have_received(:send_dossier_received)
end
end
describe "#send_draft_notification_email" do
include Rails.application.routes.url_helpers
let(:procedure) { create(:procedure) }
let(:user) { create(:user) }
it "send an email when the dossier is created for the very first time" do
dossier = nil
expect do
perform_enqueued_jobs do
dossier = Dossier.create(groupe_instructeur: procedure.defaut_groupe_instructeur, state: Dossier.states.fetch(:brouillon), user: user)
end
end.to change(ActionMailer::Base.deliveries, :size).from(0).to(1)
mail = ActionMailer::Base.deliveries.last
expect(mail.subject).to eq("Retrouvez votre brouillon pour la démarche « #{procedure.libelle} »")
expect(mail.html_part.body).to include(dossier_url(dossier))
end
it "does not send an email when the dossier is created with a non brouillon state" do
expect { Dossier.create(groupe_instructeur: procedure.defaut_groupe_instructeur, state: Dossier.states.fetch(:en_construction), user: user) }.not_to change(ActionMailer::Base.deliveries, :size)
expect { Dossier.create(groupe_instructeur: procedure.defaut_groupe_instructeur, state: Dossier.states.fetch(:en_instruction), user: user) }.not_to change(ActionMailer::Base.deliveries, :size)
expect { Dossier.create(groupe_instructeur: procedure.defaut_groupe_instructeur, state: Dossier.states.fetch(:accepte), user: user) }.not_to change(ActionMailer::Base.deliveries, :size)
expect { Dossier.create(groupe_instructeur: procedure.defaut_groupe_instructeur, state: Dossier.states.fetch(:refuse), user: user) }.not_to change(ActionMailer::Base.deliveries, :size)
expect { Dossier.create(groupe_instructeur: procedure.defaut_groupe_instructeur, state: Dossier.states.fetch(:sans_suite), user: user) }.not_to change(ActionMailer::Base.deliveries, :size)
end
end
describe "#unspecified_attestation_champs" do
let(:procedure) { create(:procedure, attestation_template: attestation_template) }
let(:dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction)) }
subject { dossier.unspecified_attestation_champs.map(&:libelle) }
context "without attestation template" do
let(:attestation_template) { nil }
it { is_expected.to eq([]) }
end
context "with attestation template" do
# Test all combinations:
# - with tag specified and unspecified
# - with tag in body and tag in title
# - with tag correponsing to a champ and an annotation privée
# - with a dash in the champ libelle / tag
let(:title) { "voici --specified champ-in-title-- un --unspecified champ-in-title-- beau --specified annotation privée-in-title-- titre --unspecified annotation privée-in-title-- non --numéro du dossier--" }
let(:body) { "voici --specified champ-in-body-- un --unspecified champ-in-body-- beau --specified annotation privée-in-body-- body --unspecified annotation privée-in-body-- non ?" }
let(:attestation_template) { create(:attestation_template, title: title, body: body, activated: activated) }
context "which is disabled" do
let(:activated) { false }
it { is_expected.to eq([]) }
end
context "wich is enabled" do
let(:activated) { true }
let!(:tdc_1) { create(:type_de_champ, libelle: "specified champ-in-title", procedure: procedure) }
let!(:tdc_2) { create(:type_de_champ, libelle: "unspecified champ-in-title", procedure: procedure) }
let!(:tdc_3) { create(:type_de_champ, libelle: "specified champ-in-body", procedure: procedure) }
let!(:tdc_4) { create(:type_de_champ, libelle: "unspecified champ-in-body", procedure: procedure) }
let!(:tdc_5) { create(:type_de_champ, private: true, libelle: "specified annotation privée-in-title", procedure: procedure) }
let!(:tdc_6) { create(:type_de_champ, private: true, libelle: "unspecified annotation privée-in-title", procedure: procedure) }
let!(:tdc_7) { create(:type_de_champ, private: true, libelle: "specified annotation privée-in-body", procedure: procedure) }
let!(:tdc_8) { create(:type_de_champ, private: true, libelle: "unspecified annotation privée-in-body", procedure: procedure) }
before do
(dossier.champs + dossier.champs_private)
.filter { |c| c.libelle.match?(/^specified/) }
.each { |c| c.update_attribute(:value, "specified") }
end
it do
is_expected.to eq([
"unspecified champ-in-title",
"unspecified annotation privée-in-title",
"unspecified champ-in-body",
"unspecified annotation privée-in-body"
])
end
end
end
end
describe '#build_attestation' do
let(:attestation_template) { nil }
let(:procedure) { create(:procedure, attestation_template: attestation_template) }
before :each do
dossier.attestation = dossier.build_attestation
dossier.reload
end
context 'when the dossier is in en_instruction state ' do
let!(:dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction)) }
context 'when the procedure has no attestation' do
it { expect(dossier.attestation).to be_nil }
end
context 'when the procedure has an unactivated attestation' do
let(:attestation_template) { AttestationTemplate.new(activated: false) }
it { expect(dossier.attestation).to be_nil }
end
context 'when the procedure attached has an activated attestation' do
let(:attestation_template) { AttestationTemplate.new(activated: true) }
it { expect(dossier.attestation).not_to be_nil }
end
end
end
describe 'updated_at' do
let!(:dossier) { create(:dossier) }
let(:modif_date) { Time.zone.parse('01/01/2100') }
before { Timecop.freeze(modif_date) }
after { Timecop.return }
subject do
dossier.reload
dossier.updated_at
end
it { is_expected.not_to eq(modif_date) }
context 'when a champ is modified' do
before { dossier.champs.first.update_attribute('value', 'yop') }
it { is_expected.to eq(modif_date) }
end
context 'when a commentaire is modified' do
before { dossier.commentaires << create(:commentaire) }
it { is_expected.to eq(modif_date) }
end
context 'when an avis is modified' do
before { dossier.avis << create(:avis) }
it { is_expected.to eq(modif_date) }
end
end
describe '#owner_name' do
let(:procedure) { create(:procedure) }
subject { dossier.owner_name }
context 'when there is no entreprise or individual' do
let(:dossier) { create(:dossier, individual: nil, procedure: procedure) }
it { is_expected.to be_nil }
end
context 'when there is entreprise' do
let(:dossier) { create(:dossier, :with_entreprise, procedure: procedure) }
it { is_expected.to eq(dossier.etablissement.entreprise_raison_sociale) }
end
context 'when there is an individual' do
let(:procedure) { create(:procedure, :for_individual) }
let(:dossier) { create(:dossier, :with_individual, procedure: procedure) }
it { is_expected.to eq("#{dossier.individual.nom} #{dossier.individual.prenom}") }
end
end
describe "#discard_and_keep_track!" do
let(:dossier) { create(:dossier, :en_construction) }
let(:deleted_dossier) { DeletedDossier.find_by(dossier_id: dossier.id) }
let(:last_operation) { dossier.dossier_operation_logs.last }
let(:reason) { :user_request }
before do
allow(DossierMailer).to receive(:notify_deletion_to_user).and_return(double(deliver_later: nil))
allow(DossierMailer).to receive(:notify_deletion_to_administration).and_return(double(deliver_later: nil))
end
subject! { dossier.discard_and_keep_track!(dossier.user, reason) }
context 'brouillon' do
let(:dossier) { create(:dossier) }
it 'hides the dossier' do
expect(dossier.discarded?).to be_truthy
end
it 'do not creates a DeletedDossier record' do
expect(deleted_dossier).to be_nil
end
it 'do not records the operation in the log' do
expect(last_operation).to be_nil
end
end
context 'en_construction' do
it 'hides the dossier' do
expect(dossier.hidden_at).to be_present
end
it 'creates a DeletedDossier record' do
expect(deleted_dossier.reason).to eq DeletedDossier.reasons.fetch(reason)
expect(deleted_dossier.dossier_id).to eq dossier.id
expect(deleted_dossier.procedure).to eq dossier.procedure
expect(deleted_dossier.state).to eq dossier.state
expect(deleted_dossier.deleted_at).to be_present
end
it 'notifies the user' do
expect(DossierMailer).to have_received(:notify_deletion_to_user).with(deleted_dossier, dossier.user.email)
end
it 'records the operation in the log' do
expect(last_operation.operation).to eq("supprimer")
expect(last_operation.automatic_operation?).to be_falsey
end
context 'where instructeurs are following the dossier' do
let(:dossier) { create(:dossier, :en_construction, :followed) }
let!(:non_following_instructeur) do
non_following_instructeur = create(:instructeur)
non_following_instructeur.groupe_instructeurs << dossier.procedure.defaut_groupe_instructeur
non_following_instructeur
end
it 'notifies the following instructeurs' do
expect(DossierMailer).to have_received(:notify_deletion_to_administration).once
expect(DossierMailer).to have_received(:notify_deletion_to_administration).with(deleted_dossier, dossier.followers_instructeurs.first.email)
end
end
context 'when there are no following instructeurs' do
let(:dossier) { create(:dossier, :en_construction) }
it 'notifies the procedure administrateur' do
expect(DossierMailer).to have_received(:notify_deletion_to_administration).once
expect(DossierMailer).to have_received(:notify_deletion_to_administration).with(deleted_dossier, dossier.procedure.administrateurs.first.email)
end
end
context 'when dossier is brouillon' do
let(:dossier) { create(:dossier) }
it 'do not notifies the procedure administrateur' do
expect(DossierMailer).not_to have_received(:notify_deletion_to_administration)
end
end
context 'with reason: manager_request' do
let(:reason) { :manager_request }
it 'hides the dossier' do
expect(dossier.discarded?).to be_truthy
end
it 'records the operation in the log' do
expect(last_operation.operation).to eq("supprimer")
expect(last_operation.automatic_operation?).to be_falsey
end
end
context 'with reason: user_removed' do
let(:reason) { :user_removed }
it 'hides the dossier' do
expect(dossier.discarded?).to be_truthy
end
it 'records the operation in the log' do
expect(last_operation.operation).to eq("supprimer")
expect(last_operation.automatic_operation?).to be_falsey
end
end
end
end
describe 'webhook' do
let(:dossier) { create(:dossier) }
it 'should not call webhook' do
expect {
dossier.accepte!
}.to_not have_enqueued_job(WebHookJob)
end
it 'should call webhook' do
dossier.procedure.update_column(:web_hook_url, '/webhook.json')
expect {
dossier.update_column(:motivation, 'bonjour')
}.to_not have_enqueued_job(WebHookJob)
expect {
dossier.en_construction!
}.to have_enqueued_job(WebHookJob)
expect {
dossier.update_column(:motivation, 'bonjour2')
}.to_not have_enqueued_job(WebHookJob)
expect {
dossier.en_instruction!
}.to have_enqueued_job(WebHookJob)
end
end
describe "#can_transition_to_en_construction?" do
let(:procedure) { create(:procedure, :published) }
let(:dossier) { create(:dossier, state: state, procedure: procedure) }
subject { dossier.can_transition_to_en_construction? }
context "dossier state is brouillon" do
let(:state) { Dossier.states.fetch(:brouillon) }
it { is_expected.to be true }
context "procedure is closed" do
before { procedure.close! }
it { is_expected.to be false }
end
end
context "dossier state is en_construction" do
let(:state) { Dossier.states.fetch(:en_construction) }
it { is_expected.to be false }
end
context "dossier state is en_instruction" do
let(:state) { Dossier.states.fetch(:en_instruction) }
it { is_expected.to be false }
end
context "dossier state is en_instruction" do
let(:state) { Dossier.states.fetch(:accepte) }
it { is_expected.to be false }
end
context "dossier state is en_instruction" do
let(:state) { Dossier.states.fetch(:refuse) }
it { is_expected.to be false }
end
context "dossier state is en_instruction" do
let(:state) { Dossier.states.fetch(:sans_suite) }
it { is_expected.to be false }
end
end
describe "#messagerie_available?" do
let(:procedure) { create(:procedure) }
let(:dossier) { create(:dossier, procedure: procedure) }
subject { dossier.messagerie_available? }
context "dossier is brouillon" do
before { dossier.state = Dossier.states.fetch(:brouillon) }
it { is_expected.to be false }
end
context "dossier is submitted" do
before { dossier.state = Dossier.states.fetch(:en_instruction) }
it { is_expected.to be true }
end
context "dossier is archived" do
before { dossier.archived = true }
it { is_expected.to be false }
end
end
context "retention date" do
let(:procedure) { create(:procedure, duree_conservation_dossiers_dans_ds: 6) }
let(:uninstructed_dossier) { create(:dossier, :en_construction, procedure: procedure) }
let(:young_dossier) { create(:dossier, :en_instruction, en_instruction_at: Time.zone.now, procedure: procedure) }
let(:just_expired_dossier) { create(:dossier, :en_instruction, en_instruction_at: 6.months.ago, procedure: procedure) }
let(:long_expired_dossier) { create(:dossier, :en_instruction, en_instruction_at: 1.year.ago, procedure: procedure) }
let(:modif_date) { Time.zone.parse('01/01/2100') }
before { Timecop.freeze(modif_date) }
after { Timecop.return }
describe "#retention_end_date" do
it { expect(uninstructed_dossier.retention_end_date).to be_nil }
it { expect(young_dossier.retention_end_date).to eq(6.months.from_now) }
it { expect(just_expired_dossier.retention_end_date).to eq(Time.zone.now) }
it { expect(long_expired_dossier.retention_end_date).to eq(6.months.ago) }
end
describe "#retention_expired?" do
it { expect(uninstructed_dossier).not_to be_retention_expired }
it { expect(young_dossier).not_to be_retention_expired }
it { expect(just_expired_dossier).to be_retention_expired }
it { expect(long_expired_dossier).to be_retention_expired }
end
end
describe '#accepter!' do
let(:dossier) { create(:dossier, :en_instruction) }
let(:last_operation) { dossier.dossier_operation_logs.last }
let(:operation_serialized) { JSON.parse(last_operation.serialized.download) }
let!(:instructeur) { create(:instructeur) }
let!(:now) { Time.zone.parse('01/01/2100') }
let(:attestation) { Attestation.new }
before do
allow(NotificationMailer).to receive(:send_closed_notification).and_return(double(deliver_later: true))
allow(dossier).to receive(:build_attestation).and_return(attestation)
Timecop.freeze(now)
dossier.accepter!(instructeur, 'motivation')
dossier.reload
end
after { Timecop.return }
it { expect(dossier.motivation).to eq('motivation') }
it { expect(dossier.en_instruction_at).to eq(dossier.en_instruction_at) }
it { expect(dossier.processed_at).to eq(now) }
it { expect(dossier.state).to eq('accepte') }
it { expect(last_operation.operation).to eq('accepter') }
it { expect(last_operation.automatic_operation?).to be_falsey }
it { expect(operation_serialized['operation']).to eq('accepter') }
it { expect(operation_serialized['dossier_id']).to eq(dossier.id) }
it { expect(operation_serialized['executed_at']).to eq(last_operation.executed_at.iso8601) }
it { expect(NotificationMailer).to have_received(:send_closed_notification).with(dossier) }
it { expect(dossier.attestation).to eq(attestation) }
end
describe '#accepter_automatiquement!' do
let(:dossier) { create(:dossier, :en_construction) }
let(:last_operation) { dossier.dossier_operation_logs.last }
let!(:now) { Time.zone.parse('01/01/2100') }
let(:attestation) { Attestation.new }
before do
allow(NotificationMailer).to receive(:send_closed_notification).and_return(double(deliver_later: true))
allow(dossier).to receive(:build_attestation).and_return(attestation)
Timecop.freeze(now)
dossier.accepter_automatiquement!
dossier.reload
end
after { Timecop.return }
it { expect(dossier.motivation).to eq(nil) }
it { expect(dossier.en_instruction_at).to eq(now) }
it { expect(dossier.processed_at).to eq(now) }
it { expect(dossier.state).to eq('accepte') }
it { expect(last_operation.operation).to eq('accepter') }
it { expect(last_operation.automatic_operation?).to be_truthy }
it { expect(NotificationMailer).to have_received(:send_closed_notification).with(dossier) }
it { expect(dossier.attestation).to eq(attestation) }
end
describe '#passer_en_instruction!' do
let(:dossier) { create(:dossier, :en_construction) }
let(:last_operation) { dossier.dossier_operation_logs.last }
let(:operation_serialized) { JSON.parse(last_operation.serialized.download) }
let(:instructeur) { create(:instructeur) }
before { dossier.passer_en_instruction!(instructeur) }
it { expect(dossier.state).to eq('en_instruction') }
it { expect(dossier.followers_instructeurs).to include(instructeur) }
it { expect(last_operation.operation).to eq('passer_en_instruction') }
it { expect(last_operation.automatic_operation?).to be_falsey }
it { expect(operation_serialized['operation']).to eq('passer_en_instruction') }
it { expect(operation_serialized['dossier_id']).to eq(dossier.id) }
it { expect(operation_serialized['executed_at']).to eq(last_operation.executed_at.iso8601) }
end
describe '#passer_automatiquement_en_instruction!' do
let(:dossier) { create(:dossier, :en_construction) }
let(:last_operation) { dossier.dossier_operation_logs.last }
let(:operation_serialized) { JSON.parse(last_operation.serialized.download) }
let(:instructeur) { create(:instructeur) }
before { dossier.passer_automatiquement_en_instruction! }
it { expect(dossier.followers_instructeurs).not_to include(instructeur) }
it { expect(last_operation.operation).to eq('passer_en_instruction') }
it { expect(last_operation.automatic_operation?).to be_truthy }
it { expect(operation_serialized['operation']).to eq('passer_en_instruction') }
it { expect(operation_serialized['dossier_id']).to eq(dossier.id) }
it { expect(operation_serialized['executed_at']).to eq(last_operation.executed_at.iso8601) }
end
describe "#check_mandatory_champs" do
let(:procedure) { create(:procedure, :with_type_de_champ) }
let(:dossier) { create(:dossier, procedure: procedure) }
it 'no mandatory champs' do
expect(dossier.check_mandatory_champs).to be_empty
end
context "with mandatory champs" do
let(:procedure) { create(:procedure, :with_type_de_champ_mandatory) }
let(:champ_with_error) { dossier.champs.first }
before do
champ_with_error.value = nil
champ_with_error.save
end
it 'should have errors' do
errors = dossier.check_mandatory_champs
expect(errors).not_to be_empty
expect(errors.first).to eq("Le champ #{champ_with_error.libelle} doit être rempli.")
end
end
context "with mandatory SIRET champ" do
let(:type_de_champ) { create(:type_de_champ_siret, mandatory: true) }
let(:champ_siret) { create(:champ_siret, type_de_champ: type_de_champ) }
before do
dossier.champs << champ_siret
end
it 'should not have errors' do
errors = dossier.check_mandatory_champs
expect(errors).to be_empty
end
context "and invalid SIRET" do
before do
champ_siret.update(value: "1234")
dossier.reload
end
it 'should have errors' do
errors = dossier.check_mandatory_champs
expect(errors).not_to be_empty
expect(errors.first).to eq("Le champ #{champ_siret.libelle} doit être rempli.")
end
end
end
context "with champ repetition" do
let(:procedure) { create(:procedure) }
let(:type_de_champ_repetition) { create(:type_de_champ_repetition, mandatory: true) }
before do
procedure.types_de_champ << type_de_champ_repetition
type_de_champ_repetition.types_de_champ << create(:type_de_champ_text, mandatory: true)
end
context "when no champs" do
let(:champ_with_error) do
repetition_champ = dossier.champs.first
text_champ = repetition_champ.rows.first.first
text_champ
end
it 'should have errors' do
errors = dossier.check_mandatory_champs
expect(errors).not_to be_empty
expect(errors.first).to eq("Le champ #{champ_with_error.libelle} doit être rempli.")
end
end
context "when mandatory champ inside repetition" do
let(:champ_with_error) { dossier.champs.first.champs.first }
before do
dossier.champs.first.add_row
end
it 'should have errors' do
errors = dossier.check_mandatory_champs
expect(errors).not_to be_empty
expect(errors.first).to eq("Le champ #{champ_with_error.libelle} doit être rempli.")
end
end
end
end
describe '#repasser_en_instruction!' do
let(:dossier) { create(:dossier, :refuse, :with_attestation) }
let!(:instructeur) { create(:instructeur) }
let(:last_operation) { dossier.dossier_operation_logs.last }
before do
Timecop.freeze
allow(DossierMailer).to receive(:notify_revert_to_instruction)
.and_return(double(deliver_later: true))
dossier.repasser_en_instruction!(instructeur)
dossier.reload
end
it { expect(dossier.state).to eq('en_instruction') }
it { expect(dossier.processed_at).to be_nil }
it { expect(dossier.motivation).to be_nil }
it { expect(dossier.attestation).to be_nil }
it { expect(last_operation.operation).to eq('repasser_en_instruction') }
it { expect(JSON.parse(last_operation.serialized.download)['author']['email']).to eq(instructeur.email) }
it { expect(DossierMailer).to have_received(:notify_revert_to_instruction).with(dossier) }
after { Timecop.return }
end
describe '#attachments_downloadable?' do
let(:dossier) { create(:dossier, user: user) }
# subject { dossier.attachments_downloadable? }
context "no attachments" do
it {
expect(PiecesJustificativesService).to receive(:liste_pieces_justificatives).and_return([])
expect(dossier.attachments_downloadable?).to be false
}
end
context "with a small attachment" do
it {
expect(PiecesJustificativesService).to receive(:liste_pieces_justificatives).and_return([Champ.new])
expect(PiecesJustificativesService).to receive(:pieces_justificatives_total_size).and_return(4.megabytes)
expect(dossier.attachments_downloadable?).to be true
}
end
context "with a too large attachment" do
it {
expect(PiecesJustificativesService).to receive(:liste_pieces_justificatives).and_return([Champ.new])
expect(PiecesJustificativesService).to receive(:pieces_justificatives_total_size).and_return(100.megabytes)
expect(dossier.attachments_downloadable?).to be false
}
end
end
describe '#for_procedure' do
let!(:procedure_1) { create(:procedure) }
let!(:procedure_2) { create(:procedure) }
let!(:dossier_1_1) { create(:dossier, procedure: procedure_1) }
let!(:dossier_1_2) { create(:dossier, procedure: procedure_1) }
let!(:dossier_2_1) { create(:dossier, procedure: procedure_2) }
before do
gi_1_2 = procedure_1.groupe_instructeurs.create(label: 2)
gi_1_2.dossiers << dossier_1_2
end
it { expect(Dossier.for_procedure(procedure_1)).to contain_exactly(dossier_1_1, dossier_1_2) }
it { expect(Dossier.for_procedure(procedure_2)).to contain_exactly(dossier_2_1) }
end
describe '#notify_draft_not_submitted' do
let!(:user1) { create(:user) }
let!(:user2) { create(:user) }
let!(:procedure_near_closing) { create(:procedure, auto_archive_on: Time.zone.today + Dossier::REMAINING_DAYS_BEFORE_CLOSING.days) }
let!(:procedure_closed_later) { create(:procedure, auto_archive_on: Time.zone.today + Dossier::REMAINING_DAYS_BEFORE_CLOSING.days + 1.day) }
let!(:procedure_closed_before) { create(:procedure, auto_archive_on: Time.zone.today + Dossier::REMAINING_DAYS_BEFORE_CLOSING.days - 1.day) }
# user 1 has three draft dossiers where one is for procedure that closes in two days ==> should trigger one mail
let!(:draft_near_closing) { create(:dossier, user: user1, procedure: procedure_near_closing) }
let!(:draft_before) { create(:dossier, user: user1, procedure: procedure_closed_before) }
let!(:draft_later) { create(:dossier, user: user1, procedure: procedure_closed_later) }
# user 2 submitted a draft and en_construction dossier for the same procedure ==> should not trigger the mail
let!(:draft_near_closing_2) { create(:dossier, :en_construction, user: user2, procedure: procedure_near_closing) }
let!(:submitted_near_closing_2) { create(:dossier, user: user2, procedure: procedure_near_closing) }
before do
allow(DossierMailer).to receive(:notify_brouillon_not_submitted).and_return(double(deliver_later: nil))
Dossier.notify_draft_not_submitted
end
it 'notifies draft is not submitted' do
expect(DossierMailer).to have_received(:notify_brouillon_not_submitted).once
expect(DossierMailer).to have_received(:notify_brouillon_not_submitted).with(draft_near_closing)
end
end
describe '#geo_position' do
let(:lat) { "46.538192" }
let(:lon) { "2.428462" }
let(:zoom) { "13" }
let(:etablissement_geo_adresse_lat) { "40.7143528" }
let(:etablissement_geo_adresse_lon) { "-74.0059731" }
let(:result) { { lat: lat, lon: lon, zoom: zoom } }
let(:dossier) { create(:dossier) }
it 'should geolocate' do
expect(dossier.geo_position).to eq(result)
end
context 'with etablissement' do
before do
Geocoder::Lookup::Test.add_stub(
dossier.etablissement.geo_adresse, [
{
'coordinates' => [etablissement_geo_adresse_lat.to_f, etablissement_geo_adresse_lon.to_f]
}
]
)
end
let(:dossier) { create(:dossier, :with_entreprise) }
let(:result) { { lat: etablissement_geo_adresse_lat, lon: etablissement_geo_adresse_lon, zoom: zoom } }
it 'should geolocate' do
expect(dossier.geo_position).to eq(result)
end
end
end
describe 'dossier_operation_log after dossier deletion' do
let(:dossier) { create(:dossier) }
let(:dossier_operation_log) { create(:dossier_operation_log, dossier: dossier) }
it 'should nullify dossier link' do
expect(dossier_operation_log.dossier).to eq(dossier)
expect(DossierOperationLog.count).to eq(1)
dossier.destroy
expect(dossier_operation_log.reload.dossier).to be_nil
expect(DossierOperationLog.count).to eq(1)
end
end
describe 'discarded_brouillon_expired and discarded_en_construction_expired' do
let(:administration) { create(:administration) }
before do
create(:dossier)
create(:dossier, :en_construction)
create(:dossier).discard!
create(:dossier, :en_construction).discard!
Timecop.travel(2.months.ago) do
create(:dossier).discard!
create(:dossier, :en_construction).discard!
create(:dossier).procedure.discard_and_keep_track!(administration)
create(:dossier, :en_construction).procedure.discard_and_keep_track!(administration)
end
Timecop.travel(1.week.ago) do
create(:dossier).discard!
create(:dossier, :en_construction).discard!
end
end
it { expect(Dossier.discarded_brouillon_expired.count).to eq(2) }
it { expect(Dossier.discarded_en_construction_expired.count).to eq(2) }
end
describe "discarded procedure dossier should be able to access it's procedure" do
let(:dossier) { create(:dossier) }
let(:procedure) { dossier.reload.procedure }
before { dossier.procedure.discard! }
it { expect(procedure).not_to be_nil }
it { expect(procedure.discarded?).to be_truthy }
end
end