describe Procedure do describe 'mail templates' do subject { create(:procedure) } it { expect(subject.passer_en_construction_email_template).to be_a(Mails::InitiatedMail) } it { expect(subject.passer_en_instruction_email_template).to be_a(Mails::ReceivedMail) } it { expect(subject.accepter_email_template).to be_a(Mails::ClosedMail) } it { expect(subject.refuser_email_template).to be_a(Mails::RefusedMail) } it { expect(subject.classer_sans_suite_email_template).to be_a(Mails::WithoutContinuationMail) } it { expect(subject.repasser_en_instruction_email_template).to be_a(Mails::ReInstructedMail) } end describe 'compute_dossiers_count' do let(:procedure) { create(:procedure_with_dossiers, dossiers_count: 2, dossiers_count_computed_at: Time.zone.now - Procedure::DOSSIERS_COUNT_EXPIRING) } it 'caches estimated_dossiers_count' do procedure.dossiers.each(&:passer_en_construction!) expect { procedure.compute_dossiers_count }.to change(procedure, :estimated_dossiers_count).from(nil).to(2) expect { create(:dossier, procedure: procedure).passer_en_construction! }.not_to change(procedure, :estimated_dossiers_count) Timecop.freeze(Time.zone.now + Procedure::DOSSIERS_COUNT_EXPIRING) expect { procedure.compute_dossiers_count }.to change(procedure, :estimated_dossiers_count).from(2).to(3) Timecop.return end end describe 'initiated_mail' do let(:procedure) { create(:procedure) } subject { procedure } context 'when initiated_mail is not customize' do it { expect(subject.passer_en_construction_email_template.body).to eq(Mails::InitiatedMail.default_for_procedure(procedure).body) } end context 'when initiated_mail is customize' do before :each do subject.initiated_mail = Mails::InitiatedMail.new(body: 'sisi') subject.save subject.reload end it { expect(subject.passer_en_construction_email_template.body).to eq('sisi') } end context 'when initiated_mail is customize ... again' do before :each do subject.initiated_mail = Mails::InitiatedMail.new(body: 'toto') subject.save subject.reload end it { expect(subject.passer_en_construction_email_template.body).to eq('toto') } it { expect(Mails::InitiatedMail.count).to eq(1) } end end describe 'closed mail template body' do let(:procedure) { create(:procedure, attestation_template: attestation_template) } let(:attestation_template) { nil } subject { procedure.accepter_email_template.rich_body.body.to_html } context 'for procedures without an attestation' do it { is_expected.not_to include('lien attestation') } end context 'for procedures with an attestation' do let(:attestation_template) { build(:attestation_template, activated: activated) } context 'when the attestation is inactive' do let(:activated) { false } it { is_expected.not_to include('lien attestation') } end context 'when the attestation is inactive' do let(:activated) { true } it { is_expected.to include('lien attestation') } end end end describe '#closed_mail_template_attestation_inconsistency_state' do let(:procedure_without_attestation) { create(:procedure, closed_mail: closed_mail, attestation_template: nil) } let(:procedure_with_active_attestation) do create(:procedure, closed_mail: closed_mail, attestation_template: build(:attestation_template, activated: true)) end let(:procedure_with_inactive_attestation) do create(:procedure, closed_mail: closed_mail, attestation_template: build(:attestation_template, activated: false)) end subject { procedure.closed_mail_template_attestation_inconsistency_state } context 'with a custom mail template' do context 'that contains a lien attestation tag' do let(:closed_mail) { build(:closed_mail, body: '--lien attestation--') } context 'when the procedure doesn’t have an attestation' do let(:procedure) { procedure_without_attestation } it { is_expected.to eq(:extraneous_tag) } end context 'when the procedure has an active attestation' do let(:procedure) { procedure_with_active_attestation } it { is_expected.to be(nil) } end context 'when the procedure has an inactive attestation' do let(:procedure) { procedure_with_inactive_attestation } it { is_expected.to eq(:extraneous_tag) } end end context 'that doesn’t contain a lien attestation tag' do let(:closed_mail) { build(:closed_mail) } context 'when the procedure doesn’t have an attestation' do let(:procedure) { procedure_without_attestation } it { is_expected.to be(nil) } end context 'when the procedure has an active attestation' do let(:procedure) { procedure_with_active_attestation } it { is_expected.to eq(:missing_tag) } end context 'when the procedure has an inactive attestation' do let(:procedure) { procedure_with_inactive_attestation } it { is_expected.to be(nil) } end end end context 'with the default mail template' do let(:closed_mail) { nil } context 'when the procedure doesn’t have an attestation' do let(:procedure) { procedure_without_attestation } it { is_expected.to be(nil) } end context 'when the procedure has an active attestation' do let(:procedure) { procedure_with_active_attestation } it { is_expected.to be(nil) } end context 'when the procedure has an inactive attestation' do let(:procedure) { procedure_with_inactive_attestation } it { is_expected.to be(nil) } end end end describe 'scopes' do let!(:procedure) { create(:procedure) } let!(:discarded_procedure) { create(:procedure, :discarded) } describe 'default_scope' do subject { Procedure.all } it { is_expected.to match_array([procedure]) } end end describe 'validation' do context 'libelle' do it { is_expected.not_to allow_value(nil).for(:libelle) } it { is_expected.not_to allow_value('').for(:libelle) } it { is_expected.to allow_value('Demande de subvention').for(:libelle) } end context 'closing procedure' do context 'without replacing procedure in DS' do let(:procedure) { create(:procedure) } context 'valid' do before do procedure.update!(closing_details: "Bonjour,\nLa démarche est désormais hébergée sur une autre plateforme\nCordialement", closing_reason: Procedure.closing_reasons.fetch(:other)) end it { expect(procedure).to be_valid } end end end context 'description' do it { is_expected.not_to allow_value(nil).for(:description) } it { is_expected.not_to allow_value('').for(:description) } it { is_expected.to allow_value('Description Demande de subvention').for(:description) } end context 'organisation' do it { is_expected.to allow_value('URRSAF').for(:organisation) } end context 'administrateurs' do it { is_expected.not_to allow_value([]).for(:administrateurs) } end context 'juridique' do it { is_expected.not_to allow_value(nil).on(:publication).for(:cadre_juridique) } it { is_expected.to allow_value('text').on(:publication).for(:cadre_juridique) } context 'with deliberation' do let(:procedure) { build(:procedure, cadre_juridique: nil) } it { expect(procedure.valid?(:publication)).to eq(false) } context 'when the deliberation is uploaded ' do before do procedure.deliberation = fixture_file_upload('spec/fixtures/files/file.pdf', 'application/pdf') end it { expect(procedure.valid?(:publication)).to eq(true) } end context 'when the deliberation is uploaded with an unauthorized format' do before do procedure.deliberation = fixture_file_upload('spec/fixtures/files/french-flag.gif', 'image/gif') end it { expect(procedure.valid?(:publication)).to eq(false) } end end context 'when juridique_required is false' do let(:procedure) { build(:procedure, juridique_required: false, cadre_juridique: nil) } it { expect(procedure.valid?(:publication)).to eq(true) } end end context 'api_entreprise_token' do let(:valid_token) { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" } let(:invalid_token) { 'plouf' } it { is_expected.to allow_value(valid_token).for(:api_entreprise_token) } it { is_expected.not_to allow_value(invalid_token).for(:api_entreprise_token) } end context 'api_particulier_token' do let(:valid_token) { "3841b13fa8032ed3c31d160d3437a76a" } let(:invalid_token) { 'jet0n 1nvalide' } it { is_expected.to allow_value(valid_token).for(:api_particulier_token) } it { is_expected.not_to allow_value(invalid_token).for(:api_particulier_token) } end context 'monavis' do context 'nil is allowed' do it { is_expected.to allow_value(nil).for(:monavis_embed) } it { is_expected.to allow_value('').for(:monavis_embed) } end context 'random string is not allowed' do let(:procedure) { build(:procedure, monavis_embed: "plop") } it { expect(procedure.valid?).to eq(false) } end context 'random html is not allowed' do let(:procedure) { build(:procedure, monavis_embed: '') } it { expect(procedure.valid?).to eq(false) } end context 'Monavis embed code with white button is allowed' do monavis_blanc = <<-MSG Je donne mon avis MSG let(:procedure) { build(:procedure, monavis_embed: monavis_blanc) } it { expect(procedure.valid?).to eq(true) } end context 'Monavis embed code with blue button is allowed' do monavis_bleu = <<-MSG Je donne mon avis MSG let(:procedure) { build(:procedure, monavis_embed: monavis_bleu) } it { expect(procedure.valid?).to eq(true) } end context 'Monavis embed code with voxusages is allowed' do monavis_issue_phillipe = <<-MSG Je donne mon avis MSG let(:procedure) { build(:procedure, monavis_embed: monavis_issue_phillipe) } it { expect(procedure.valid?).to eq(true) } end context 'Monavis embed code without title allowed' do monavis_issue_bouchra = <<-MSG Je donne mon avis MSG let(:procedure) { build(:procedure, monavis_embed: monavis_issue_bouchra) } it { expect(procedure.valid?).to eq(true) } end context 'Monavis embed code with jedonnemonavis' do monavis_jedonnemonavis = <<-MSG Je donne mon avis MSG let(:procedure) { build(:procedure, monavis_embed: monavis_jedonnemonavis) } it { expect(procedure.valid?).to eq(true) } end context 'Monavis embed code with kthxbye' do monavis_jedonnemonavis = <<-MSG Je donne mon avis MSG let(:procedure) { build(:procedure, monavis_embed: monavis_jedonnemonavis) } it { expect(procedure.valid?).to eq(false) } end end describe 'duree de conservation dans ds' do let(:field_name) { :duree_conservation_dossiers_dans_ds } context 'by default is caped to 12' do subject { create(:procedure, duree_conservation_dossiers_dans_ds: 12, max_duree_conservation_dossiers_dans_ds: 12) } it { is_expected.not_to allow_value(nil).for(field_name) } it { is_expected.not_to allow_value('').for(field_name) } it { is_expected.not_to allow_value('trois').for(field_name) } it { is_expected.to allow_value(3).for(field_name) } it { is_expected.to validate_numericality_of(field_name).is_less_than_or_equal_to(12) } end context 'can be over riden' do subject { create(:procedure, duree_conservation_dossiers_dans_ds: 60, max_duree_conservation_dossiers_dans_ds: 60) } it { is_expected.not_to allow_value(nil).for(field_name) } it { is_expected.not_to allow_value('').for(field_name) } it { is_expected.not_to allow_value('trois').for(field_name) } it { is_expected.to allow_value(3).for(field_name) } it { is_expected.to allow_value(60).for(field_name) } it { is_expected.to validate_numericality_of(field_name).is_less_than_or_equal_to(60) } end end describe 'draft_types_de_champ validations' do let(:procedure) { create(:procedure, types_de_champ_public:, types_de_champ_private:) } context 'on a draft procedure' do let(:types_de_champ_private) { [] } let(:types_de_champ_public) { [{ type: :repetition, libelle: 'Enfants', children: [] }] } it 'doesn’t validate the types de champs' do procedure.validate expect(procedure.errors[:draft_types_de_champ_public]).not_to be_present end end context 'when validating for publication' do let(:types_de_champ_public) do [ { type: :repetition, libelle: 'Enfants', children: [] }, { type: :drop_down_list, libelle: 'Civilité', options: [] } ] end let(:types_de_champ_private) { [] } let(:invalid_repetition_error_message) { 'Le champ « Enfants » doit comporter au moins un champ répétable' } let(:invalid_drop_down_error_message) { 'Le champ « Civilité » doit comporter au moins un choix sélectionnable' } it 'validates that no repetition type de champ is empty' do procedure.validate(:publication) expect(procedure.errors.full_messages_for(:draft_types_de_champ_public)).to include(invalid_repetition_error_message) new_draft = procedure.draft_revision repetition = procedure.draft_revision.types_de_champ_public.find(&:repetition?) parent_coordinate = new_draft.revision_types_de_champ.find_by(type_de_champ: repetition) new_draft.revision_types_de_champ.create(type_de_champ: create(:type_de_champ), position: 0, parent: parent_coordinate) procedure.validate(:publication) expect(procedure.errors.full_messages_for(:draft_types_de_champ_public)).not_to include(invalid_repetition_error_message) end it 'validates that no drop-down type de champ is empty' do procedure.validate(:publication) expect(procedure.errors.full_messages_for(:draft_types_de_champ_public)).to include(invalid_drop_down_error_message) drop_down = procedure.draft_revision.types_de_champ_public.find(&:drop_down_list?) drop_down.update!(drop_down_list_value: "--title--\r\nsome value") procedure.reload.validate(:publication) expect(procedure.errors.full_messages_for(:draft_types_de_champ_public)).not_to include(invalid_drop_down_error_message) end end context 'when the champ is private' do let(:types_de_champ_private) do [ { type: :repetition, libelle: 'Enfants', children: [] }, { type: :drop_down_list, libelle: 'Civilité', options: [] } ] end let(:types_de_champ_public) { [] } let(:invalid_repetition_error_message) { 'L’annotation privée « Enfants » doit comporter au moins un champ répétable' } let(:invalid_drop_down_error_message) { 'L’annotation privée « Civilité » doit comporter au moins un choix sélectionnable' } it 'validates that no repetition type de champ is empty' do procedure.validate(:publication) expect(procedure.errors.full_messages_for(:draft_types_de_champ_private)).to include(invalid_repetition_error_message) end it 'validates that no drop-down type de champ is empty' do procedure.validate(:publication) expect(procedure.errors.full_messages_for(:draft_types_de_champ_private)).to include(invalid_drop_down_error_message) end it 'validates that types de champ private condition works types de champ public and private' do end end context 'when condition on champ private use public champ' do include Logic let(:types_de_champ_private) { [{ type: :text, condition: ds_eq(champ_value(1), constant(2)) }] } let(:types_de_champ_public) { [{ type: :number, stable_id: 1 }] } it 'validate without context' do expect(procedure.validate).to be_truthy end it 'validate with types_de_champ_private_editor' do expect(procedure.validate(:types_de_champ_private_editor)).to be_falsey end end end context 'with auto archive' do let(:procedure) { create(:procedure, auto_archive_on: 1.day.from_now) } it { expect(procedure).to be_valid } context 'when auto_archive_on is in the past' do it 'validates only when attribute is changed' do procedure.auto_archive_on = 1.day.ago expect(procedure).not_to be_valid procedure.save!(validate: false) expect(procedure).to be_valid end end end context 'with sva svr' do before { procedure.sva_svr["decision"] = "svr" } context 'when procedure is published with sva' do let(:procedure) { create(:procedure, :published, :sva) } it 'prevents changes to sva_svr' do expect(procedure).not_to be_valid expect(procedure.errors[:sva_svr].join).to include('ne peut plus être modifiée') end end context 'when procedure is published without sva' do let(:procedure) { create(:procedure, :published) } it 'allow activation' do expect(procedure).to be_valid end it 'allow activation from disabled value' do procedure.sva_svr["decision"] = "disabled" procedure.save! procedure.sva_svr["decision"] = "svr" expect(procedure).to be_valid end end context 'brouillon procedure' do let(:procedure) { create(:procedure, :sva) } it "can update sva config" do expect(procedure).to be_valid end end context "with declarative" do let(:procedure) { create(:procedure, declarative_with_state: "accepte") } it 'is not valid' do expect(procedure).not_to be_valid expect(procedure.errors[:sva_svr].join).to include('incompatible avec une démarche déclarative') end end end end describe 'opendata' do let(:procedure) { create(:procedure) } it 'is true by default' do expect(procedure.opendata).to be_truthy end end describe 'publiques' do let(:draft_procedure) { create(:procedure_with_dossiers, :draft, estimated_dossiers_count: 4, lien_site_web: 'https://monministere.gouv.fr/cparici') } let(:published_procedure) { create(:procedure_with_dossiers, :published, estimated_dossiers_count: 4, lien_site_web: 'https://monministere.gouv.fr/cparici') } let(:published_procedure_no_opendata) { create(:procedure_with_dossiers, :published, estimated_dossiers_count: 4, opendata: false) } let(:published_procedure_with_3_dossiers) { create(:procedure_with_dossiers, :published, estimated_dossiers_count: 3) } let(:published_procedure_with_mail) { create(:procedure_with_dossiers, :published, estimated_dossiers_count: 4, lien_site_web: 'par mail') } let(:published_procedure_with_intra) { create(:procedure_with_dossiers, :published, estimated_dossiers_count: 4, lien_site_web: 'https://intra.service-etat.gouv.fr') } it 'returns published procedure, with opendata flag, with accepted lien_site_web' do expect(Procedure.publiques).not_to include(published_procedure_no_opendata) end it "returns only published or closed procedures" do expect(Procedure.publiques).not_to include(draft_procedure) end it "returns only procedures with opendata flag" do expect(Procedure.publiques).not_to include(published_procedure_with_mail) end it "returns only procedures without mail in lien_site_web" do expect(Procedure.publiques).not_to include(published_procedure_with_mail) end it "returns only procedures without intra in lien_site_web" do expect(Procedure.publiques).not_to include(published_procedure_with_intra) end it "does not return procedures with less than 4 dossiers" do expect(Procedure.publiques).not_to include(published_procedure_with_3_dossiers) end end describe 'active' do let(:procedure) { create(:procedure) } subject { Procedure.active(procedure.id) } context 'when procedure is in draft status and not closed' do it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) } end context 'when procedure is published and not closed' do let(:procedure) { create(:procedure, :published) } it { is_expected.to be_truthy } end context 'when procedure is published and closed' do let(:procedure) { create(:procedure, :closed) } it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) } end end describe 'api_entreprise_token_expired?' do let(:token) { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" } let(:procedure) { create(:procedure, api_entreprise_token: token) } let(:payload) { [ { "exp" => expiration_time } ] } let(:subject) { procedure.api_entreprise_token_expired? } before do allow(JWT).to receive(:decode).with(token, nil, false).and_return(payload) end context "with token expired" do let(:expiration_time) { (1.day.ago).to_i } it { is_expected.to be_truthy } end context "with token not expired" do let(:expiration_time) { (1.day.from_now).to_i } it { is_expected.to be_falsey } end end describe 'clone' do let(:service) { create(:service) } let(:procedure) do create(:procedure, received_mail: received_mail, service: service, opendata: opendata, duree_conservation_etendue_par_ds: true, duree_conservation_dossiers_dans_ds: Procedure::OLD_MAX_DUREE_CONSERVATION, max_duree_conservation_dossiers_dans_ds: Procedure::OLD_MAX_DUREE_CONSERVATION, attestation_template: build(:attestation_template, logo: logo, signature: signature), types_de_champ_public: [{}, {}, { type: :drop_down_list }, { type: :piece_justificative }, { type: :repetition, children: [{}] }], types_de_champ_private: [{}, {}, { type: :drop_down_list }, { type: :repetition, children: [{}] }], api_particulier_token: '123456789012345', api_particulier_scopes: ['cnaf_famille'], estimated_dossiers_count: 4, template: true) end let(:type_de_champ_repetition) { procedure.draft_revision.types_de_champ_public.last } let(:type_de_champ_private_repetition) { procedure.draft_revision.types_de_champ_private.last } let(:received_mail) { build(:received_mail) } let(:from_library) { false } let(:opendata) { true } let(:administrateur) { procedure.administrateurs.first } let(:logo) { Rack::Test::UploadedFile.new('spec/fixtures/files/white.png', 'image/png') } let(:signature) { Rack::Test::UploadedFile.new('spec/fixtures/files/black.png', 'image/png') } let(:groupe_instructeur_1) { create(:groupe_instructeur, procedure: procedure, label: "groupe_1", contact_information: create(:contact_information)) } let(:instructeur_1) { create(:instructeur) } let(:instructeur_2) { create(:instructeur) } let!(:assign_to_1) { create(:assign_to, procedure: procedure, groupe_instructeur: groupe_instructeur_1, instructeur: instructeur_1) } let!(:assign_to_2) { create(:assign_to, procedure: procedure, groupe_instructeur: groupe_instructeur_1, instructeur: instructeur_2) } subject do @procedure = procedure.clone(administrateur, from_library) @procedure.save @procedure end it { expect(subject.parent_procedure).to eq(procedure) } it 'the cloned procedure should not be a template anymore' do expect(subject.template).to be_falsey end describe "should keep groupe instructeurs " do it "should clone groupe instructeurs" do expect(subject.groupe_instructeurs.size).to eq(2) expect(subject.groupe_instructeurs.size).to eq(procedure.groupe_instructeurs.size) expect(subject.groupe_instructeurs.where(label: "groupe_1").first).not_to be nil expect(subject.defaut_groupe_instructeur_id).to eq(subject.groupe_instructeurs.find_by(label: 'défaut').id) end it "should clone instructeurs in the groupe" do expect(subject.groupe_instructeurs.where(label: "groupe_1").first.instructeurs.map(&:email)).to eq(procedure.groupe_instructeurs.where(label: "groupe_1").first.instructeurs.map(&:email)) end it 'should clone with success a second group instructeur closed' do procedure.groupe_instructeurs.last.update(closed: true) expect { subject }.not_to raise_error end it 'should clone groupe instructeur services' do expect(procedure.groupe_instructeurs.last.contact_information).not_to eq nil expect(subject.groupe_instructeurs.last.contact_information).not_to eq nil end end it 'should reset duree_conservation_etendue_par_ds' do expect(subject.duree_conservation_etendue_par_ds).to eq(false) expect(subject.duree_conservation_dossiers_dans_ds).to eq(Expired::DEFAULT_DOSSIER_RENTENTION_IN_MONTH) end it 'should duplicate specific objects with different id' do expect(subject.id).not_to eq(procedure.id) expect(subject.draft_revision.types_de_champ_public.size).to eq(procedure.draft_revision.types_de_champ_public.size) expect(subject.draft_revision.types_de_champ_private.size).to eq(procedure.draft_revision.types_de_champ_private.size) procedure.draft_revision.types_de_champ_public.zip(subject.draft_revision.types_de_champ_public).each do |ptc, stc| expect(stc).to have_same_attributes_as(ptc) expect(stc.revision).to eq(subject.draft_revision) end public_repetition = type_de_champ_repetition cloned_public_repetition = subject.draft_revision.types_de_champ_public.repetition.first procedure.draft_revision.children_of(public_repetition).zip(subject.draft_revision.children_of(cloned_public_repetition)).each do |ptc, stc| expect(stc).to have_same_attributes_as(ptc) expect(stc.revision).to eq(subject.draft_revision) end procedure.draft_revision.types_de_champ_private.zip(subject.draft_revision.types_de_champ_private).each do |ptc, stc| expect(stc).to have_same_attributes_as(ptc) expect(stc.revision).to eq(subject.draft_revision) end private_repetition = type_de_champ_private_repetition cloned_private_repetition = subject.draft_revision.types_de_champ_private.repetition.first procedure.draft_revision.children_of(private_repetition).zip(subject.draft_revision.children_of(cloned_private_repetition)).each do |ptc, stc| expect(stc).to have_same_attributes_as(ptc) expect(stc.revision).to eq(subject.draft_revision) end expect(subject.attestation_template.title).to eq(procedure.attestation_template.title) expect(subject.cloned_from_library).to be(false) cloned_procedure = subject cloned_procedure.parent_procedure_id = nil expect(cloned_procedure).to have_same_attributes_as(procedure, except: [ "path", "draft_revision_id", "service_id", 'estimated_dossiers_count', "duree_conservation_etendue_par_ds", "duree_conservation_dossiers_dans_ds", 'max_duree_conservation_dossiers_dans_ds', "defaut_groupe_instructeur_id", "template" ]) end context 'which is opendata' do let(:opendata) { false } it 'should keep opendata for same admin' do expect(subject.opendata).to be_falsy end end context 'when the procedure is cloned from the library' do let(:from_library) { true } it 'should set cloned_from_library to true' do expect(subject.cloned_from_library).to be(true) end it 'should set service_id to nil' do expect(subject.service).to eq(nil) end it 'should discard old pj information' do subject.draft_revision.types_de_champ_public.each do |stc| expect(stc.old_pj).to be_nil end end it 'should have one administrateur' do expect(subject.administrateurs).to eq([administrateur]) end it 'should set ask_birthday to false' do expect(subject.ask_birthday?).to eq(false) end end context 'when the procedure is cloned from the library' do let(:procedure) { create(:procedure, received_mail: received_mail, service: service, ask_birthday: true) } it 'should set ask_birthday to false' do expect(subject.ask_birthday?).to eq(false) end end it 'should skips service_id' do expect(subject.service).to eq(nil) end context 'when the procedure is cloned to another administrateur' do let(:administrateur) { create(:administrateur) } let(:opendata) { false } context 'and the procedure does not have a groupe with the defaut label' do before do procedure.defaut_groupe_instructeur.update!(label: 'another label') end it "affects the first groupe as the defaut groupe" do expect(subject.defaut_groupe_instructeur).to eq(subject.groupe_instructeurs.first) end end it 'should not clone service' do expect(subject.service).to eq(nil) end context 'with groupe instructeur services' do it 'should not clone groupe instructeur services' do expect(procedure.groupe_instructeurs.last.contact_information).not_to eq nil expect(subject.groupe_instructeurs.last.contact_information).to eq nil end end it 'should discard old pj information' do subject.draft_revision.types_de_champ_public.each do |stc| expect(stc.old_pj).to be_nil end end it 'should discard specific api_entreprise_token' do expect(subject.read_attribute(:api_entreprise_token)).to be_nil end it 'should reset opendata to true' do expect(subject.opendata).to be_truthy end it 'should have one administrateur' do expect(subject.administrateurs).to eq([administrateur]) end it "should discard the existing groupe instructeurs" do expect(subject.groupe_instructeurs.size).not_to eq(procedure.groupe_instructeurs.size) expect(subject.groupe_instructeurs.where(label: "groupe_1").first).to be nil end it "should discard api_particulier_scopes and token" do expect(subject.encrypted_api_particulier_token).to be_nil expect(subject.api_particulier_scopes).to be_empty end it 'should not route the procedure' do expect(subject.routing_enabled).to eq(false) end it 'should have a default groupe instructeur' do expect(subject.groupe_instructeurs.size).to eq(1) expect(subject.groupe_instructeurs.first.label).to eq(GroupeInstructeur::DEFAUT_LABEL) expect(subject.groupe_instructeurs.first.instructeurs.size).to eq(0) end end it 'should duplicate existing mail_templates' do expect(subject.received_mail.attributes.except("id", "procedure_id", "created_at", "updated_at")).to eq procedure.received_mail.attributes.except("id", "procedure_id", "created_at", "updated_at") expect(subject.received_mail.id).not_to eq procedure.received_mail.id expect(subject.received_mail.id).not_to be nil expect(subject.received_mail.procedure_id).not_to eq procedure.received_mail.procedure_id expect(subject.received_mail.procedure_id).not_to be nil end it 'should not duplicate default mail_template' do expect(subject.passer_en_construction_email_template.attributes).to eq Mails::InitiatedMail.default_for_procedure(subject).attributes end it 'should not duplicate specific related objects' do expect(subject.dossiers).to eq([]) end it "should reset estimated_dossiers_count" do expect(subject.estimated_dossiers_count).to eq(0) end describe 'should not duplicate lien_notice' do let(:procedure) { create(:procedure, lien_notice: "http://toto.com") } it { expect(subject.lien_notice).to be_nil } end describe 'procedure status is reset' do let(:procedure) { create(:procedure, :closed, received_mail: received_mail, service: service, auto_archive_on: 3.weeks.from_now) } it 'Not published nor closed' do expect(subject.closed_at).to be_nil expect(subject.published_at).to be_nil expect(subject.unpublished_at).to be_nil expect(subject.auto_archive_on).to be_nil expect(subject.aasm_state).to eq "brouillon" expect(subject.path).not_to be_nil end end it 'should keep types_de_champ ids stable' do expect(subject.draft_revision.types_de_champ_public.first.id).not_to eq(procedure.draft_revision.types_de_champ_public.first.id) expect(subject.draft_revision.types_de_champ_public.first.stable_id).to eq(procedure.draft_revision.types_de_champ_public.first.id) end it 'should duplicate piece_justificative_template on a type_de_champ' do expect(subject.draft_revision.types_de_champ_public.where(type_champ: "piece_justificative").first.piece_justificative_template.attached?).to be_truthy end context 'with a notice attached' do let(:procedure) { create(:procedure, :with_notice, received_mail: received_mail, service: service) } it 'should duplicate notice' do expect(subject.notice.attached?).to be_truthy expect(subject.notice.attachment).not_to eq(procedure.notice.attachment) expect(subject.notice.attachment.blob).to eq(procedure.notice.attachment.blob) subject.notice.attach(logo) subject.reload procedure.reload expect(subject.notice.attached?).to be_truthy expect(subject.notice.attachment.blob).not_to eq(procedure.notice.attachment.blob) subject.notice.purge subject.reload procedure.reload expect(subject.notice.attached?).to be_falsey expect(procedure.notice.attached?).to be_truthy end end context 'with a deliberation attached' do let(:procedure) { create(:procedure, :with_deliberation, received_mail: received_mail, service: service) } it 'should duplicate deliberation' do expect(subject.deliberation.attached?).to be true end end context 'with canonical procedure' do let(:canonical_procedure) { create(:procedure) } let(:procedure) { create(:procedure, canonical_procedure: canonical_procedure, received_mail: received_mail, service: service) } it 'do not clone canonical procedure' do expect(subject.canonical_procedure).to be_nil end end describe 'feature flag' do context 'with a feature flag enabled' do before do Flipper.enable(:dossier_pdf_vide, procedure) end it 'should enable feature' do expect(subject.feature_enabled?(:dossier_pdf_vide)).to be true expect(Flipper.feature(:dossier_pdf_vide).enabled_gate_names).to include(:actor) end end context 'with feature flag is fully enabled' do before do Flipper.enable(:dossier_pdf_vide) end it 'should not clone feature for actor' do expect(subject.feature_enabled?(:dossier_pdf_vide)).to be true expect(Flipper.feature(:dossier_pdf_vide).enabled_gate_names).not_to include(:actor) end end context 'with a feature flag disabled' do before do Flipper.disable(:dossier_pdf_vide, procedure) end it 'should not enable feature' do expect(subject.feature_enabled?(:dossier_pdf_vide)).to be false end end end end describe '#publish!' do let(:procedure) { create(:procedure, path: 'example-path', zones: [create(:zone)]) } let(:now) { Time.zone.now.beginning_of_minute } context 'when publishing a new procedure' do before do Timecop.freeze(now) do procedure.publish! end end it 'no reference to the canonical procedure on the published procedure' do expect(procedure.canonical_procedure).to be_nil end it 'changes the procedure state to published' do expect(procedure.closed_at).to be_nil expect(procedure.published_at).to eq(now) expect(Procedure.find_by(path: "example-path")).to eq(procedure) expect(Procedure.find_by(path: "example-path").administrateurs).to eq(procedure.administrateurs) end it 'creates a new draft revision' do expect(procedure.published_revision).not_to be_nil expect(procedure.draft_revision).not_to be_nil expect(procedure.revisions.count).to eq(2) expect(procedure.revisions).to eq([procedure.published_revision, procedure.draft_revision]) end end context 'when publishing over a previous canonical procedure' do let(:canonical_procedure) { create(:procedure, :published) } before do Timecop.freeze(now) do procedure.publish!(canonical_procedure) end end it 'references the canonical procedure on the published procedure' do expect(procedure.canonical_procedure).to eq(canonical_procedure) end it 'changes the procedure state to published' do expect(procedure.closed_at).to be_nil expect(procedure.published_at).to eq(now) end end end describe "#publish_or_reopen!" do let(:canonical_procedure) { create(:procedure, :published) } let(:administrateur) { canonical_procedure.administrateurs.first } let(:procedure) { create(:procedure, administrateurs: [administrateur], zones: [create(:zone)]) } let(:now) { Time.zone.now.beginning_of_minute } context 'when publishing over a previous canonical procedure' do before do procedure.path = canonical_procedure.path Timecop.freeze(now) do procedure.publish_or_reopen!(administrateur) end procedure.reload canonical_procedure.reload end it 'references the canonical procedure on the published procedure' do expect(procedure.canonical_procedure).to eq(canonical_procedure) end it 'changes the procedure state to published' do expect(procedure.closed_at).to be_nil expect(procedure.published_at).to eq(now) end it 'unpublishes the canonical procedure' do expect(canonical_procedure.unpublished_at).to eq(now) end it 'creates a new draft revision' do expect(procedure.published_revision).not_to be_nil expect(procedure.draft_revision).not_to be_nil expect(procedure.revisions.count).to eq(2) expect(procedure.revisions).to eq([procedure.published_revision, procedure.draft_revision]) expect(procedure.published_revision.published_at).to eq(now) end end context 'when publishing over a previous procedure with canonical procedure' do let(:canonical_procedure) { create(:procedure, :closed) } let(:parent_procedure) { create(:procedure, :published, administrateurs: [administrateur]) } before do parent_procedure.update!(path: canonical_procedure.path, canonical_procedure: canonical_procedure) procedure.path = canonical_procedure.path Timecop.freeze(now) do procedure.publish_or_reopen!(administrateur) end parent_procedure.reload end it 'references the canonical procedure on the published procedure' do expect(procedure.canonical_procedure).to eq(canonical_procedure) end it 'changes the procedure state to published' do expect(procedure.canonical_procedure).to eq(canonical_procedure) expect(procedure.closed_at).to be_nil expect(procedure.published_at).to eq(now) expect(procedure.published_revision.published_at).to eq(now) end it 'unpublishes parent procedure' do expect(parent_procedure.unpublished_at).to eq(now) end end context 'when republishing a previously closed procedure' do let(:procedure) { create(:procedure, :published, administrateurs: [administrateur]) } before do procedure.close! Timecop.freeze(now) do procedure.publish_or_reopen!(administrateur) end end it 'changes the procedure state to published' do expect(procedure.closed_at).to be_nil expect(procedure.published_at).to eq(now) expect(procedure.published_revision.published_at).not_to eq(now) end it "doesn't create a new revision" do expect(procedure.published_revision).not_to be_nil expect(procedure.draft_revision).not_to be_nil expect(procedure.revisions.count).to eq(2) expect(procedure.revisions).to eq([procedure.published_revision, procedure.draft_revision]) end end end describe "#publish_revision!" do let(:procedure) { create(:procedure, :published) } let(:tdc_attributes) { { type_champ: :number, libelle: 'libelle 1' } } let(:publication_date) { Time.zone.local(2021, 1, 1, 12, 00, 00) } before do procedure.draft_revision.add_type_de_champ(tdc_attributes) end subject do Timecop.freeze(publication_date) do procedure.publish_revision! end end it 'publishes the new revision' do subject expect(procedure.published_revision).to be_present expect(procedure.published_revision.published_at).to eq(publication_date) expect(procedure.published_revision.types_de_champ_public.first.libelle).to eq('libelle 1') end it 'creates a new draft revision' do expect { subject }.to change(ProcedureRevision, :count).by(1) expect(procedure.draft_revision).to be_present expect(procedure.draft_revision.revision_types_de_champ_public).to be_present expect(procedure.draft_revision.types_de_champ_public).to be_present expect(procedure.draft_revision.types_de_champ_public.first.libelle).to eq('libelle 1') end context 'when the procedure has dossiers' do let(:dossier_draft) { create(:dossier, :brouillon, procedure: procedure) } let(:dossier_submitted) { create(:dossier, :en_construction, procedure: procedure) } let(:dossier_termine) { create(:dossier, :accepte, procedure: procedure) } before { [dossier_draft, dossier_submitted, dossier_termine] } it 'enqueues rebase jobs for draft dossiers' do subject expect(DossierRebaseJob).to have_been_enqueued.with(dossier_draft) expect(DossierRebaseJob).to have_been_enqueued.with(dossier_submitted) expect(DossierRebaseJob).not_to have_been_enqueued.with(dossier_termine) end end end describe "#reset_draft_revision!" do let(:procedure) { create(:procedure) } let(:tdc_attributes) { { type_champ: :number, libelle: 'libelle 1' } } let(:publication_date) { Time.zone.local(2021, 1, 1, 12, 00, 00) } context "brouillon procedure" do it "should not reset draft revision" do procedure.draft_revision.add_type_de_champ(tdc_attributes) previous_draft_revision = procedure.draft_revision procedure.reset_draft_revision! expect(procedure.draft_revision).to eq(previous_draft_revision) end end context "published procedure" do let(:procedure) do create( :procedure, :published, attestation_template: build(:attestation_template), dossier_submitted_message: create(:dossier_submitted_message), types_de_champ_public: [{ type: :text, libelle: 'published tdc' }] ) end it "should reset draft revision" do procedure.draft_revision.add_type_de_champ(tdc_attributes) previous_draft_revision = procedure.draft_revision previous_attestation_template = procedure.attestation_template previous_dossier_submitted_message = previous_draft_revision.dossier_submitted_message expect(procedure.draft_changed?).to be_truthy procedure.reset_draft_revision! expect(procedure.draft_changed?).to be_falsey expect(procedure.draft_revision).not_to eq(previous_draft_revision) expect { previous_draft_revision.reload }.to raise_error(ActiveRecord::RecordNotFound) expect(procedure.attestation_template).to eq(previous_attestation_template) expect(procedure.draft_revision.dossier_submitted_message).to eq(previous_dossier_submitted_message) end it "should erase orphan tdc" do published_tdc = procedure.published_revision.types_de_champ.first draft_tdc = procedure.draft_revision.add_type_de_champ(tdc_attributes) procedure.reset_draft_revision! expect { published_tdc.reload }.not_to raise_error expect { draft_tdc.reload }.to raise_error(ActiveRecord::RecordNotFound) end end end describe "#unpublish!" do let(:procedure) { create(:procedure, :published) } let(:now) { Time.zone.now.beginning_of_minute } before do Timecop.freeze(now) do procedure.unpublish! end end it { expect(procedure.closed_at).to eq(nil) expect(procedure.published_at).not_to be_nil expect(procedure.unpublished_at).to eq(now) } it 'sets published revision' do expect(procedure.published_revision).not_to be_nil expect(procedure.draft_revision).not_to be_nil expect(procedure.revisions.count).to eq(2) expect(procedure.revisions).to eq([procedure.published_revision, procedure.draft_revision]) end end describe "#brouillon?" do let(:procedure_brouillon) { build(:procedure) } let(:procedure_publiee) { build(:procedure, :published) } let(:procedure_close) { build(:procedure, :closed) } let(:procedure_depubliee) { build(:procedure, :unpublished) } it { expect(procedure_brouillon.brouillon?).to be_truthy } it { expect(procedure_publiee.brouillon?).to be_falsey } it { expect(procedure_close.brouillon?).to be_falsey } it { expect(procedure_depubliee.brouillon?).to be_falsey } end describe "#publiee?" do let(:procedure_brouillon) { build(:procedure) } let(:procedure_publiee) { build(:procedure, :published) } let(:procedure_close) { build(:procedure, :closed) } let(:procedure_depubliee) { build(:procedure, :unpublished) } it { expect(procedure_brouillon.publiee?).to be_falsey } it { expect(procedure_publiee.publiee?).to be_truthy } it { expect(procedure_close.publiee?).to be_falsey } it { expect(procedure_depubliee.publiee?).to be_falsey } end describe "#close?" do let(:procedure_brouillon) { build(:procedure) } let(:procedure_publiee) { build(:procedure, :published) } let(:procedure_close) { build(:procedure, :closed) } let(:procedure_depubliee) { build(:procedure, :unpublished) } it { expect(procedure_brouillon.close?).to be_falsey } it { expect(procedure_publiee.close?).to be_falsey } it { expect(procedure_close.close?).to be_truthy } it { expect(procedure_depubliee.close?).to be_falsey } end describe "#depubliee?" do let(:procedure_brouillon) { build(:procedure) } let(:procedure_publiee) { build(:procedure, :published) } let(:procedure_close) { build(:procedure, :closed) } let(:procedure_depubliee) { build(:procedure, :unpublished) } it { expect(procedure_brouillon.depubliee?).to be_falsey } it { expect(procedure_publiee.depubliee?).to be_falsey } it { expect(procedure_close.depubliee?).to be_falsey } it { expect(procedure_depubliee.depubliee?).to be_truthy } end describe "#locked?" do let(:procedure_brouillon) { build(:procedure) } let(:procedure_publiee) { build(:procedure, :published) } let(:procedure_close) { build(:procedure, :closed) } let(:procedure_depubliee) { build(:procedure, :unpublished) } it { expect(procedure_brouillon.locked?).to be_falsey } it { expect(procedure_publiee.locked?).to be_truthy } it { expect(procedure_close.locked?).to be_truthy } it { expect(procedure_depubliee.locked?).to be_truthy } end describe 'close' do let(:procedure) { create(:procedure, :published) } let(:now) { Time.zone.now.beginning_of_minute } before do Timecop.freeze(now) do procedure.close! end procedure.reload end it { expect(procedure.close?).to be_truthy } it { expect(procedure.closed_at).to eq(now) } it 'sets published revision' do expect(procedure.published_revision).not_to be_nil expect(procedure.draft_revision).not_to be_nil expect(procedure.revisions.count).to eq(2) expect(procedure.revisions).to eq([procedure.published_revision, procedure.draft_revision]) end end describe 'path_customized?' do let(:procedure) { create :procedure } subject { procedure.path_customized? } context 'when the path is still the default' do it { is_expected.to be_falsey } end context 'when the path has been changed' do before { procedure.path = 'custom_path' } it { is_expected.to be_truthy } end end describe 'total_dossier' do let(:procedure) { create :procedure } before do create :dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction) create :dossier, procedure: procedure, state: Dossier.states.fetch(:brouillon) create :dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction) end subject { procedure.total_dossier } it { is_expected.to eq 2 } end describe 'suggested_path' do let(:procedure) { create(:procedure, aasm_state: :publiee, libelle: 'Inscription au Collège', zones: [create(:zone)]) } subject { procedure.suggested_path(procedure.administrateurs.first) } context 'when the path has been customized' do before { procedure.path = 'custom_path' } it { is_expected.to eq 'custom_path' } end context 'when the suggestion does not conflict' do it { is_expected.to eq 'inscription-au-college' } end context 'when the suggestion conflicts with one procedure' do before do create(:procedure, aasm_state: :publiee, path: 'inscription-au-college', zones: [create(:zone)]) end it { is_expected.to eq 'inscription-au-college-2' } end context 'when the suggestion conflicts with several procedures' do before do create(:procedure, aasm_state: :publiee, path: 'inscription-au-college', zones: [create(:zone)]) create(:procedure, aasm_state: :publiee, path: 'inscription-au-college-2', zones: [create(:zone)]) end it { is_expected.to eq 'inscription-au-college-3' } end context 'when the suggestion conflicts with another procedure of the same admin' do before do create(:procedure, aasm_state: :publiee, path: 'inscription-au-college', administrateurs: procedure.administrateurs, zones: [create(:zone)]) end it { is_expected.to eq 'inscription-au-college-2' } end end describe ".default_scope" do let!(:procedure) { create(:procedure, hidden_at: hidden_at) } context "when hidden_at is nil" do let(:hidden_at) { nil } it { expect(Procedure.count).to eq(1) } it { expect(Procedure.all).to include(procedure) } end context "when hidden_at is not nil" do let(:hidden_at) { 2.days.ago } it { expect(Procedure.count).to eq(0) } it { expect { Procedure.find(procedure.id) }.to raise_error(ActiveRecord::RecordNotFound) } end end describe "#discard_and_keep_track!" do let(:super_admin) { create(:super_admin) } let(:procedure) { create(:procedure) } let!(:dossier) { create(:dossier, procedure: procedure) } let!(:dossier2) { create(:dossier, procedure: procedure) } let(:instructeur) { create(:instructeur) } it { expect(Dossier.count).to eq(2) } it { expect(Dossier.all).to include(dossier, dossier2) } context "when discarding procedure" do before do instructeur.followed_dossiers << dossier procedure.discard_and_keep_track!(super_admin) instructeur.reload end it { expect(procedure.dossiers.count).to eq(0) } it { expect(Dossier.count).to eq(0) } it { expect(instructeur.followed_dossiers).not_to include(dossier) } end end describe ".default_sort" do it { expect(Procedure.default_sort).to eq({ "table" => "self", "column" => "id", "order" => "desc" }) } end describe "#organisation_name" do subject { procedure.organisation_name } context 'when the procedure has a service (and no organization)' do let(:procedure) { create(:procedure, :with_service, organisation: nil) } it { is_expected.to eq procedure.service.nom } end context 'when the procedure has an organization (and no service)' do let(:procedure) { create(:procedure, organisation: 'DDT des Vosges', service: nil) } it { is_expected.to eq procedure.organisation } end end describe '#juridique_required' do it 'automatically jumps to true once cadre_juridique or deliberation have been set' do p = create( :procedure, juridique_required: false, cadre_juridique: nil ) expect(p.juridique_required).to be_falsey p.update(cadre_juridique: 'cadre') expect(p.juridique_required).to be_truthy p.update(cadre_juridique: nil) expect(p.juridique_required).to be_truthy p.update_columns(cadre_juridique: nil, juridique_required: false) p.reload expect(p.juridique_required).to be_falsey @deliberation = fixture_file_upload('spec/fixtures/files/file.pdf', 'application/pdf') p.update(deliberation: @deliberation) p.reload expect(p.juridique_required).to be_truthy end end describe '.ensure_a_groupe_instructeur_exists' do let(:procedure) { create(:procedure, groupe_instructeurs: []) } it do expect(procedure.groupe_instructeurs.count).to eq(1) expect(procedure.groupe_instructeurs.first.label).to eq(GroupeInstructeur::DEFAUT_LABEL) expect(procedure.defaut_groupe_instructeur_id).not_to be_nil end end describe '.missing_instructeurs?' do let!(:procedure) { create(:procedure) } subject { procedure.missing_instructeurs? } it { is_expected.to be true } context 'when an instructeur is assign to this procedure' do let!(:instructeur) { create(:instructeur) } before { instructeur.assign_to_procedure(procedure) } it { is_expected.to be false } end end describe '.missing_zones?' do before do Rails.application.config.ds_zonage_enabled = true end let(:procedure) { create(:procedure, zones: []) } subject { procedure.missing_zones? } it { is_expected.to be true } context 'when a procedure has zones' do let(:zone) { create(:zone) } before { procedure.zones << zone } it { is_expected.to be false } end end describe '.missing_steps' do before do Flipper.enable :zonage end subject { procedure.missing_steps.include?(step) } context 'without zone' do let(:procedure) { create(:procedure, zones: []) } let(:step) { :zones } it { is_expected.to be_truthy } end context 'with zone' do let(:procedure) { create(:procedure, zones: [create(:zone)]) } let(:step) { :zones } it { is_expected.to be_falsey } end context 'without service' do let(:procedure) { create(:procedure, service: nil) } let(:step) { :service } it { is_expected.to be_truthy } end context 'with service' do let(:procedure) { create(:procedure) } let(:step) { :service } it { is_expected.to be_truthy } end end describe "#destroy" do let(:procedure) { create(:procedure, :closed, :with_type_de_champ, :with_bulk_message) } before do procedure.discard! end it "can destroy procedure" do expect(procedure.revisions.count).to eq(2) expect(procedure.destroy).to be_truthy end end describe '#average_dossier_weight' do let(:procedure) { create(:procedure, :published) } before do create_dossier_with_pj_of_size(4, procedure) create_dossier_with_pj_of_size(5, procedure) create_dossier_with_pj_of_size(6, procedure) end it 'estimates average dossier weight' do expect(procedure.reload.average_dossier_weight).to eq(5 + Procedure::MIN_WEIGHT) end end describe 'lien_dpo' do it { expect(build(:procedure).valid?).to be(true) } it { expect(build(:procedure, lien_dpo: 'dpo@ministere.amere').valid?).to be(true) } it { expect(build(:procedure, lien_dpo: 'https://legal.fr/contact_dpo').valid?).to be(true) } it { expect(build(:procedure, lien_dpo: 'askjdlad l akdj asd ').valid?).to be(false) } end describe 'factory' do let(:types_de_champ) { [{ type: :yes_no }, { type: :integer_number }] } context 'create' do let(:types_de_champ) { [{ type: :yes_no }, { type: :repetition, children: [{ type: :integer_number }] }] } let(:procedure) { create(:procedure, types_de_champ_public: types_de_champ) } context 'with brouillon procedure' do it do expect(procedure.draft_revision.types_de_champ_public.count).to eq(2) expect(procedure.draft_revision.types_de_champ.count).to eq(3) end end context 'with published procedure' do let(:procedure) { create(:procedure, :published, types_de_champ_public: types_de_champ) } it do expect(procedure.draft_revision.types_de_champ_public.count).to eq(2) expect(procedure.draft_revision.types_de_champ.count).to eq(3) expect(procedure.published_revision.types_de_champ_public.count).to eq(2) expect(procedure.published_revision.types_de_champ.count).to eq(3) end end end context 'with bouillon procedure' do let(:procedure) { build(:procedure, types_de_champ_public: types_de_champ, types_de_champ_private: types_de_champ) } it do expect(procedure.revisions.size).to eq(1) expect(procedure.draft_revision.types_de_champ.size).to eq(4) expect(procedure.draft_revision.types_de_champ_public.size).to eq(2) expect(procedure.published_revision).to be_nil end end context 'with published procedure' do let(:procedure) { build(:procedure, :published, types_de_champ_public: types_de_champ, types_de_champ_private: types_de_champ) } it do expect(procedure.revisions.size).to eq(2) expect(procedure.draft_revision.types_de_champ.size).to eq(4) expect(procedure.draft_revision.types_de_champ_public.size).to eq(2) expect(procedure.published_revision.types_de_champ.size).to eq(4) expect(procedure.published_revision.types_de_champ_public.size).to eq(2) end end context 'repetition' do let(:types_de_champ) do [ { type: :yes_no }, { type: :repetition, children: [ { libelle: 'Nom', mandatory: true }, { libelle: 'Prénom', mandatory: true }, { libelle: 'Age', type: :integer_number } ] } ] end let(:revision) { procedure.draft_revision } let(:repetition) { revision.revision_types_de_champ_public.last } context 'with bouillon procedure' do let(:procedure) { build(:procedure, types_de_champ_public: types_de_champ) } it do expect(revision.types_de_champ.size).to eq(5) expect(revision.types_de_champ_public.size).to eq(2) expect(revision.types_de_champ_public.map(&:type_champ)).to eq(['yes_no', 'repetition']) expect(repetition.revision_types_de_champ.size).to eq(3) expect(repetition.revision_types_de_champ.map(&:type_champ)).to eq(['text', 'text', 'integer_number']) expect(repetition.revision_types_de_champ.map(&:mandatory?)).to eq([true, true, false]) end end context 'with published procedure' do let(:procedure) { build(:procedure, :published, types_de_champ_public: types_de_champ) } context 'draft revision' do it do expect(revision.types_de_champ.size).to eq(5) expect(revision.types_de_champ_public.size).to eq(2) expect(revision.types_de_champ_public.map(&:type_champ)).to eq(['yes_no', 'repetition']) expect(repetition.revision_types_de_champ.size).to eq(3) expect(repetition.revision_types_de_champ.map(&:type_champ)).to eq(['text', 'text', 'integer_number']) expect(repetition.revision_types_de_champ.map(&:mandatory?)).to eq([true, true, false]) end end context 'published revision' do let(:revision) { procedure.published_revision } it do expect(revision.types_de_champ.size).to eq(5) expect(revision.types_de_champ_public.size).to eq(2) expect(revision.types_de_champ_public.map(&:type_champ)).to eq(['yes_no', 'repetition']) expect(repetition.revision_types_de_champ.size).to eq(3) expect(repetition.revision_types_de_champ.map(&:type_champ)).to eq(['text', 'text', 'integer_number']) expect(repetition.revision_types_de_champ.map(&:mandatory?)).to eq([true, true, false]) end end end end end describe 'lien_notice' do let(:procedure) { build(:procedure, lien_notice:) } context 'when empty' do let(:lien_notice) { '' } it { expect(procedure.valid?).to be_truthy } end context 'when valid link' do let(:lien_notice) { 'https://www.demarches-simplifiees.fr' } it { expect(procedure.valid?).to be_truthy } end context 'when valid link with accents' do let(:lien_notice) { 'https://www.démarches-simplifiées.fr' } it { expect(procedure.valid?).to be_truthy } end context 'when not a valid link' do let(:lien_notice) { 'www.démarches-simplifiées.fr' } it { expect(procedure.valid?).to be_falsey } end context 'when an email' do let(:lien_notice) { 'test@demarches-simplifiees.fr' } it { expect(procedure.valid?).to be_falsey } end end describe 'lien_dpo' do let(:procedure) { build(:procedure, lien_dpo:) } context 'when empty' do let(:lien_dpo) { '' } it { expect(procedure.valid?).to be_truthy } end context 'when valid link' do let(:lien_dpo) { 'https://www.demarches-simplifiees.fr' } it { expect(procedure.valid?).to be_truthy } end context 'when valid link with accents' do let(:lien_dpo) { 'https://www.démarches-simplifiées.fr' } it { expect(procedure.valid?).to be_truthy } end context 'when valid email' do let(:lien_dpo) { 'test@demarches-simplifiees.fr' } it { expect(procedure.valid?).to be_truthy } end context 'when valid email with accents' do let(:lien_dpo) { 'test@démarches-simplifiées.fr' } it { expect(procedure.valid?).to be_truthy } end context 'when not a valid link' do let(:lien_dpo) { 'www.démarches-simplifiées.fr' } it { expect(procedure.valid?).to be_falsey } end end describe 'extend_conservation_for_dossiers' do let(:duree_conservation_dossiers_dans_ds) { 2 } let(:procedure) { create(:procedure, duree_conservation_dossiers_dans_ds:) } let(:expiring_dossier_brouillon) { create(:dossier, :brouillon, procedure: procedure, brouillon_close_to_expiration_notice_sent_at: duree_conservation_dossiers_dans_ds.months.ago) } let(:expiring_dossier_en_construction) { create(:dossier, :en_construction, procedure: procedure, en_construction_close_to_expiration_notice_sent_at: duree_conservation_dossiers_dans_ds.months.ago) } let(:expiring_dossier_en_termine) { create(:dossier, :accepte, procedure: procedure, termine_close_to_expiration_notice_sent_at: duree_conservation_dossiers_dans_ds.months.ago) } let(:not_expiring_dossie) { create(:dossier, :accepte, procedure: procedure, created_at: duree_conservation_dossiers_dans_ds.months.ago) } before do procedure expiring_dossier_brouillon expiring_dossier_en_construction expiring_dossier_en_termine not_expiring_dossie end context 'when duree_conservation_dossiers_dans_ds does not changes' do it 'does not enqueues any job' do expect(ResetExpiringDossiersJob).not_to receive(:perform_later) procedure.update!(libelle: 'does not change duree_conservation_dossiers_dans_ds') end end context 'when duree_conservation_dossiers_dans_ds decreases' do it 'calls extend_conservation_for_dossiers' do expect(ResetExpiringDossiersJob).not_to receive(:perform_later) procedure.update(duree_conservation_dossiers_dans_ds: duree_conservation_dossiers_dans_ds - 1) end end context 'when duree_conservation_dossiers_dans_ds increases' do it 'calls extend_conservation_for_dossiers' do expect(ResetExpiringDossiersJob).to receive(:perform_later) procedure.update(duree_conservation_dossiers_dans_ds: duree_conservation_dossiers_dans_ds + 1) end end end describe "#attestation_template" do let(:procedure) { create(:procedure) } context "when there is a v2 created after v1" do before do create(:attestation_template, procedure: procedure) create(:attestation_template, :v2, procedure: procedure) end it { expect(procedure.attestation_template.version).to eq(1) } end context "when there is a v2 created before v1" do before do create(:attestation_template, :v2, procedure: procedure) create(:attestation_template, procedure: procedure, activated: true) end it { expect(procedure.attestation_template.version).to eq(1) } end context "when there is only a v2" do before do create(:attestation_template, :v2, procedure: procedure) end it { expect(procedure.attestation_template.version).to eq(2) } end end private def create_dossier_with_pj_of_size(size, procedure) dossier = create(:dossier, :accepte, procedure: procedure) create(:champ_piece_justificative, size: size, dossier: dossier) end end