From 51e3002e27f5d32cd60991dded0bef303613d0cf Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 17 Dec 2024 21:15:28 +0100 Subject: [PATCH] fix: an invite cannot submit a fork (server side) --- app/controllers/users/dossiers_controller.rb | 2 +- .../users/dossiers_controller_spec.rb | 195 ++++++++++-------- 2 files changed, 107 insertions(+), 90 deletions(-) diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 4103122f6ac..e0b8e4d69b7 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -9,7 +9,7 @@ class DossiersController < UserController layout 'procedure_context', only: [:identite, :update_identite, :siret, :update_siret] ACTIONS_ALLOWED_TO_ANY_USER = [:index, :new, :transferer_all, :deleted_dossiers] - ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :destroy, :demande, :messagerie, :brouillon, :submit_en_construction, :modifier, :update, :create_commentaire, :papertrail, :restore, :champ] + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :destroy, :demande, :messagerie, :brouillon, :modifier, :update, :create_commentaire, :papertrail, :restore, :champ] before_action :ensure_ownership!, except: ACTIONS_ALLOWED_TO_ANY_USER + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE before_action :ensure_ownership_or_invitation!, only: ACTIONS_ALLOWED_TO_OWNER_OR_INVITE diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index 1c5390bb238..cddac2d1fac 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -501,10 +501,10 @@ end describe '#submit_en_construction' do - before { sign_in(user) } + let(:owner) { create(:user) } let(:procedure) { create(:procedure, :published, types_de_champ_public:) } let(:types_de_champ_public) { [{ type: :text, mandatory: false }] } - let(:dossier) { create(:dossier, :en_construction, procedure:, user:) } + let(:dossier) { create(:dossier, :en_construction, procedure:, user: owner) } let(:first_champ) { dossier.owner_editing_fork.project_champs_public.first } let(:anchor_to_first_champ) { controller.helpers.link_to I18n.t('views.users.dossiers.fix_champ'), modifier_dossier_path(anchor: first_champ.labelledby_id), class: 'error-anchor' } let(:value) { 'beautiful value' } @@ -519,132 +519,149 @@ end end - context 'when the dossier cannot be updated by the user' do - let!(:dossier) { create(:dossier, :en_instruction, user: user) } - - it 'redirects to the dossiers list' do - subject + context 'when the owner signs in' do + before { sign_in(owner) } - expect(response).to redirect_to(dossier_path(dossier)) - expect(flash.alert).to eq('Votre dossier ne peut plus être modifié') - end - end + context 'when the dossier cannot be updated by the owner' do + let!(:dossier) { create(:dossier, :en_instruction, user: owner) } - context 'when the update fails' do - render_views - - before do - allow_any_instance_of(Dossier).to receive(:validate).and_return(false) - allow_any_instance_of(Dossier).to receive(:errors).and_return( - [double(inner_error: double(base: first_champ), message: 'nop')] - ) + it 'redirects to the dossiers list' do + subject - subject + expect(response).to redirect_to(dossier_path(dossier)) + expect(flash.alert).to eq('Votre dossier ne peut plus être modifié') + end end - it { expect(response).to render_template(:modifier) } - end + context 'when the update fails' do + render_views - context 'when a mandatory champ is missing' do - let(:value) { nil } - render_views - let(:types_de_champ_public) { [{ type: :text, mandatory: true, libelle: 'l' }] } - before { subject } + before do + allow_any_instance_of(Dossier).to receive(:validate).and_return(false) + allow_any_instance_of(Dossier).to receive(:errors).and_return( + [double(inner_error: double(base: first_champ), message: 'nop')] + ) - it { expect(response).to render_template(:modifier) } - it { expect(response.body).to have_content("doit être rempli") } - it { expect(response.body).to have_link(first_champ.libelle, href: "##{first_champ.labelledby_id}") } - end + subject + end - context 'when dossier has no champ' do - let(:submit_payload) { { id: dossier.id } } + it { expect(response).to render_template(:modifier) } + end - it 'does not raise any errors' do - subject + context 'when a mandatory champ is missing' do + let(:value) { nil } + render_views + let(:types_de_champ_public) { [{ type: :text, mandatory: true, libelle: 'l' }] } + before { subject } - expect(response).to redirect_to(dossier_path(dossier)) + it { expect(response).to render_template(:modifier) } + it { expect(response.body).to have_content("doit être rempli") } + it { expect(response.body).to have_link(first_champ.libelle, href: "##{first_champ.labelledby_id}") } end - end - context 'when dossier repetition had been removed in newer version' do - let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:, user:) } - let(:types_de_champ_public) { [{ type: :repetition, libelle: 'repetition', children: [{ type: :text, libelle: 'child' }] }] } - let(:editing_fork) { dossier.owner_editing_fork } - let(:champ_repetition) { editing_fork.project_champs_public.find(&:repetition?) } - before do - editing_fork + context 'when dossier has no champ' do + let(:submit_payload) { { id: dossier.id } } - procedure.draft_revision.remove_type_de_champ(champ_repetition.stable_id) - procedure.publish_revision! + it 'does not raise any errors' do + subject - editing_fork.reload - editing_fork.rebase! + expect(response).to redirect_to(dossier_path(dossier)) + end end - let(:submit_payload) { { id: dossier.id } } - it { expect { subject }.not_to raise_error } - end + context 'when dossier repetition had been removed in newer version' do + let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:, user:) } + let(:types_de_champ_public) { [{ type: :repetition, libelle: 'repetition', children: [{ type: :text, libelle: 'child' }] }] } + let(:editing_fork) { dossier.owner_editing_fork } + let(:champ_repetition) { editing_fork.project_champs_public.find(&:repetition?) } + before do + editing_fork - context 'when dossier was already submitted' do - before do - expect_any_instance_of(Dossier).to receive(:remove_not_visible_or_empty_champs!) - post :submit_en_construction, params: payload - end + procedure.draft_revision.remove_type_de_champ(champ_repetition.stable_id) + procedure.publish_revision! - it 'redirects to the dossier' do - subject + editing_fork.reload + editing_fork.rebase! + end + let(:submit_payload) { { id: dossier.id } } - expect(response).to redirect_to(dossier_path(dossier)) - expect(flash.alert).to eq("Les modifications ont déjà été déposées") + it { expect { subject }.not_to raise_error } end - end - context "when there are pending correction" do - let!(:correction) { create(:dossier_correction, dossier: dossier) } + context 'when dossier was already submitted' do + before do + expect_any_instance_of(Dossier).to receive(:remove_not_visible_or_empty_champs!) + post :submit_en_construction, params: payload + end - subject { post :submit_en_construction, params: { id: dossier.id } } + it 'redirects to the dossier' do + subject - it "resolves correction automatically" do - expect { subject }.to change { correction.reload.resolved_at }.to be_truthy + expect(response).to redirect_to(dossier_path(dossier)) + expect(flash.alert).to eq("Les modifications ont déjà été déposées") + end end - context 'when procedure has sva enabled' do - let(:procedure) { create(:procedure, :sva) } - let(:dossier) { create(:dossier, :en_construction, procedure:, user:) } + context "when there are pending correction" do let!(:correction) { create(:dossier_correction, dossier: dossier) } - subject { post :submit_en_construction, params: { id: dossier.id, dossier: { pending_correction: pending_correction_value } } } + subject { post :submit_en_construction, params: { id: dossier.id } } - context 'when resolving correction' do - let(:pending_correction_value) { "1" } - it 'passe automatiquement en instruction' do - expect(dossier.pending_correction?).to be_truthy + it "resolves correction automatically" do + expect { subject }.to change { correction.reload.resolved_at }.to be_truthy + end - subject - dossier.reload + context 'when procedure has sva enabled' do + let(:procedure) { create(:procedure, :sva) } + let(:dossier) { create(:dossier, :en_construction, procedure:, user: owner) } + let!(:correction) { create(:dossier_correction, dossier: dossier) } - expect(dossier).to be_en_instruction - expect(dossier.pending_correction?).to be_falsey - expect(dossier.en_instruction_at).to within(5.seconds).of(Time.current) + subject { post :submit_en_construction, params: { id: dossier.id, dossier: { pending_correction: pending_correction_value } } } + + context 'when resolving correction' do + let(:pending_correction_value) { "1" } + it 'passe automatiquement en instruction' do + expect(dossier.pending_correction?).to be_truthy + + subject + dossier.reload + + expect(dossier).to be_en_instruction + expect(dossier.pending_correction?).to be_falsey + expect(dossier.en_instruction_at).to within(5.seconds).of(Time.current) + end end - end - context 'when not resolving correction' do - render_views + context 'when not resolving correction' do + render_views - let(:pending_correction_value) { "" } - it 'does not passe automatiquement en instruction' do - subject - dossier.reload + let(:pending_correction_value) { "" } + it 'does not passe automatiquement en instruction' do + subject + dossier.reload - expect(dossier).to be_en_construction - expect(dossier.pending_correction?).to be_truthy + expect(dossier).to be_en_construction + expect(dossier.pending_correction?).to be_truthy - expect(response.body).to include("Cochez la case") + expect(response.body).to include("Cochez la case") + end end end end end + + context 'when a invite signs in' do + let(:invite_user) { create(:user) } + let!(:invite) { create(:invite, dossier:, user: invite_user) } + + before { sign_in(invite_user) } + context 'and the invite tries to submit the dossier' do + before { subject } + + it { expect(response).to redirect_to(root_path) } + it { expect(flash.alert).to include("Vous n’avez pas accès à ce dossier") } + end + end end describe '#update brouillon' do