This commit is contained in:
Kara Diaby 2024-07-04 10:47:53 +00:00
parent d6defce137
commit a4b8d816de
No known key found for this signature in database
GPG key ID: C4D1B0CF9F24D759
7 changed files with 439 additions and 50 deletions

View file

@ -89,23 +89,9 @@ describe FranceConnect::ParticulierController, type: :controller do
let(:fc_user) { nil }
context 'and no user with the same email exists' do
it 'creates an user with the same email and log in' do
expect { subject }.to change { User.count }.by(1)
user = User.last
expect(user.email).to eq(email.downcase)
expect(controller.current_user).to eq(user)
expect(response).to redirect_to(root_path)
end
context 'when invites are pending' do
let!(:invite) { create(:invite, email: email, user: nil) }
it 'links pending invites' do
expect(invite.reload.user).to eq(nil)
subject
expect(invite.reload.user).to eq(User.last)
end
it 'render the choose email template to select good email' do
expect { subject }.to change { User.count }.by(0)
expect(subject).to render_template(:choose_email)
end
end
@ -134,15 +120,7 @@ describe FranceConnect::ParticulierController, type: :controller do
context 'when france_connect_particulier_id does not exist in database' do
it { expect { subject }.to change { FranceConnectInformation.count }.by(1) }
describe 'FranceConnectInformation attributs' do
let(:stored_fci) { FranceConnectInformation.last }
before { subject }
it { expect(stored_fci).to have_attributes(user_info.merge(birthdate: Time.zone.parse(birthdate).to_datetime)) }
end
it { is_expected.to redirect_to(root_path) }
it { is_expected.to render_template(:choose_email) }
end
end
@ -158,6 +136,311 @@ describe FranceConnect::ParticulierController, type: :controller do
end
end
describe '#associate_user' do
subject { post :associate_user, params: { use_france_connect_email: use_france_connect_email, alternative_email: alternative_email, merge_token: merge_token } }
let(:fci) { FranceConnectInformation.new(user_info) }
let(:use_france_connect_email) { true }
let(:alternative_email) { 'alt@example.com' }
let(:merge_token) { 'valid_merge_token' }
before do
allow_any_instance_of(ApplicationController).to receive(:session).and_return({ merge_token: merge_token })
end
context 'when we are using france connect email' do
let(:fci) { instance_double('FranceConnectInformation') }
let(:email) { 'fc_email@example.com' }
let(:user) { instance_double('User') }
let(:destination_path) { '/some_path' }
let(:merge_token) { 'some_token' }
before do
allow(controller).to receive(:securely_retrieve_fci).and_return(fci)
controller.instance_variable_set(:@fci, fci)
allow(fci).to receive(:email_france_connect).and_return(email)
allow(fci).to receive(:associate_user!)
allow(fci).to receive(:user).and_return(user)
allow(fci).to receive(:delete_merge_token!)
allow(controller).to receive(:use_fc_email?).and_return(true)
allow(controller).to receive(:sign_only)
allow(controller).to receive(:destination_path).and_return(destination_path)
end
subject { post :associate_user, params: { merge_token: merge_token, use_france_connect_email: true } }
it 'renders the confirmation_sent template' do
subject
expect(response).to render_template(:confirmation_sent)
end
it 'performs all expected steps' do
expect(fci).to receive(:associate_user!).with(email)
expect(fci).to receive(:delete_merge_token!)
expect(controller).to receive(:sign_only).with(user)
expect(controller).to receive(:render).with(:confirmation_sent, locals: { email: email, destination_path: destination_path })
subject
end
end
context 'when france connect information is missing or invalid' do
let(:merge_token) { 'invalid_token' }
before do
allow(FranceConnectInformation).to receive(:find_by).with(merge_token: merge_token).and_return(nil)
allow(controller).to receive(:merge_token_params).and_return(merge_token)
end
it 'redirects to root_path with an alert' do
subject
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to eq("Le délai pour fusionner les comptes FranceConnect et demarches-simplifiees.fr est expiré. Veuillez recommencer la procédure pour vous fusionner les comptes.")
end
end
context 'when @fci is not valid for merge' do
before do
allow(FranceConnectInformation).to receive(:find_by).with(merge_token: merge_token).and_return(fci)
allow(fci).to receive(:valid_for_merge?).and_return(false)
end
it 'redirects to root_path with an alert' do
subject
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to eq('Le délai pour fusionner les comptes FranceConnect et demarches-simplifiees.fr est expiré. Veuillez recommencer la procédure pour vous fusionner les comptes.')
end
end
context 'when associating the user succeeds' do
let(:fci) { instance_double('FranceConnectInformation') }
let(:email) { 'user@example.com' }
let(:user) { instance_double('User', id: 1) }
let(:destination_path) { '/' }
before do
allow(FranceConnectInformation).to receive(:find_by).with(merge_token: merge_token).and_return(fci)
allow(fci).to receive(:valid_for_merge?).and_return(true)
allow(fci).to receive(:email_france_connect).and_return(email)
allow(fci).to receive(:associate_user!)
allow(fci).to receive(:user).and_return(user)
allow(fci).to receive(:delete_merge_token!)
allow(controller).to receive(:use_fc_email?).and_return(true)
allow(controller).to receive(:sign_only)
allow(controller).to receive(:destination_path).and_return(destination_path)
end
it 'calls associate_user! with the correct email' do
expect(fci).to receive(:associate_user!).with(email)
subject
end
it 'deletes the merge token' do
expect(fci).to receive(:delete_merge_token!)
subject
end
it 'signs in the user' do
expect(controller).to receive(:sign_only).with(user)
subject
end
it 'renders the confirmation sent template with correct locals' do
expect(controller).to receive(:render).with(
:confirmation_sent,
locals: { email: email, destination_path: destination_path }
)
subject
end
end
end
describe '#confirm_email' do
let!(:user) { create(:user) }
let!(:fci) { create(:france_connect_information, user: user) }
before do
sign_in(user)
fci.send_custom_confirmation_instructions(user)
user.reload
end
let!(:expired_user_confirmation) do
user = create(:user)
fci = create(:france_connect_information, user: user)
token = SecureRandom.hex(10)
user.update!(confirmation_token: token, confirmation_sent_at: 3.days.ago)
user
end
context 'when the confirmation token is valid' do
before do
get :confirm_email, params: { token: user.confirmation_token }
user.reload
end
it 'updates the email_verified_at and confirmation_token of the user' do
expect(user.email_verified_at).to be_present
expect(user.confirmation_token).to be_nil
end
it 'redirects to the stored location or root path with a success notice' do
expect(response).to redirect_to(root_path(user))
expect(flash[:notice]).to eq('Votre email est bien vérifié')
end
it 'calls after_confirmation on the user' do
expect(user).to receive(:after_confirmation).and_call_original
user.after_confirmation
end
end
context 'when invites are pending' do
let!(:invite) { create(:invite, email: user.email, user: nil) }
it 'links pending invites' do
get :confirm_email, params: { token: user.confirmation_token }
invite.reload
expect(invite.user).to eq(user)
end
end
context 'when the confirmation token is expired' do
before do
sign_in(expired_user_confirmation)
get :confirm_email, params: { token: expired_user_confirmation.confirmation_token }
end
it 'redirects to the resend confirmation path with an alert' do
expect(response).to redirect_to(france_connect_resend_confirmation_path(token: expired_user_confirmation.confirmation_token))
expect(flash[:alert]).to eq('Lien de confirmation expiré. Un nouveau lien de confirmation a été envoyé.')
end
end
context 'GET #resend_confirmation' do
let(:user) { create(:user, email: 'test@example.com', email_verified_at: nil, confirmation_token: 'valid_token') }
context 'when the token is valid' do
it 'assigns @user and renders the form' do
get :resend_confirmation, params: { token: user.confirmation_token }
expect(assigns(:user)).to eq(user)
expect(response).to render_template(:resend_confirmation)
end
end
context 'when the email is already confirmed' do
let(:confirmed_user) { create(:user, email: 'confirmed@example.com', email_verified_at: Time.zone.now, confirmation_token: nil) }
before do
allow(controller).to receive(:set_user_by_confirmation_token) do
controller.instance_variable_set(:@user, confirmed_user)
end
end
it 'redirects to root path with an alert about already confirmed email' do
get :resend_confirmation, params: { email: confirmed_user.email }
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to eq('Email déjà confirmé.')
end
end
context 'when the token is invalid' do
before do
get :resend_confirmation, params: { token: 'invalid_token' }
end
it 'sets @user to nil and redirects to root path with an alert' do
expect(assigns(:user)).to be_nil
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to eq('Utilisateur non trouvé')
end
end
context 'when the user is not found' do
before do
allow(User).to receive(:find_by).with(confirmation_token: 'invalid_token').and_return(nil)
end
it 'redirects to root_path with an alert' do
get :resend_confirmation, params: { token: 'invalid_token' }
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to eq('Utilisateur non trouvé')
end
end
context 'when a different user is signed in' do
let(:current_user) { create(:user) }
let(:target_user) { create(:user, confirmation_token: 'valid_token') }
before do
sign_in current_user
allow(User).to receive(:find_by).with(confirmation_token: 'valid_token').and_return(target_user)
end
it 'signs out the current user and redirects to new_user_session_path with an alert' do
get :resend_confirmation, params: { token: 'valid_token' }
expect(controller.current_user).to be_nil
expect(response).to redirect_to(new_user_session_path)
expect(flash[:alert]).to eq("Veuillez vous connecter avec le compte associé à ce lien de confirmation.")
end
end
end
context 'POST #post_resend_confirmation' do
let(:user) { create(:user, email: 'test@example.com', email_verified_at: nil, confirmation_token: 'valid_token') }
let(:fci) { create(:france_connect_information, user: user) }
before do
allow(FranceConnectInformation).to receive(:find_by).with(user: user).and_return(fci)
allow(controller).to receive(:set_user_by_confirmation_token).and_call_original
end
context 'when the user exists and email is not verified' do
before do
allow(controller).to receive(:set_user_by_confirmation_token) do
controller.instance_variable_set(:@user, user)
end
end
it 'sends custom confirmation instructions and redirects with notice' do
expect(fci).to receive(:send_custom_confirmation_instructions).with(user)
post :post_resend_confirmation, params: { token: user.confirmation_token }
expect(response).to redirect_to(root_path)
expect(flash[:notice]).to eq('Un nouveau lien de confirmation vous a été envoyé par mail.')
end
end
context 'when the user does not exist' do
before do
allow(User).to receive(:find_by).with(confirmation_token: 'non_existent_token').and_return(nil)
allow(controller).to receive(:set_user_by_confirmation_token).and_call_original
end
it 'redirects with alert for non-existent token' do
post :post_resend_confirmation, params: { token: 'non_existent_token' }
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to eq('Utilisateur non trouvé')
end
end
context 'when the email is already verified' do
let(:verified_user) { create(:user, email: 'verified@example.com', email_verified_at: Time.zone.now, confirmation_token: 'verified_token') }
before do
allow(controller).to receive(:set_user_by_confirmation_token) do
controller.instance_variable_set(:@user, verified_user)
end
end
it 'redirects with alert for already verified email' do
post :post_resend_confirmation, params: { token: verified_user.confirmation_token }
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to eq('Adresse email non trouvée ou déjà confirmée.')
end
end
end
end
RSpec.shared_examples "a method that needs a valid merge token" do
context 'when the merge token is invalid' do
before do

View file

@ -101,6 +101,31 @@ RSpec.describe UserMailer, type: :mailer do
end
end
describe '#custom_confirmation_instructions' do
let(:user) { create(:user, email: 'user@example.com') }
let(:token) { 'confirmation_token_123' }
let(:mail) { UserMailer.custom_confirmation_instructions(user, token) }
it 'renders the headers' do
expect(mail.subject).to eq('Confirmez votre email')
expect(mail.to).to eq([user.email])
expect(mail.from).to eq(['contact@demarches-simplifiees.fr'])
end
it 'renders the body' do
expect(mail.body.encoded).to match(user.email)
expect(mail.body.encoded).to match(token)
end
it 'assigns @user' do
expect(mail.body.encoded).to match(user.email)
end
it 'assigns @token' do
expect(mail.body.encoded).to include(token)
end
end
describe '.send_archive' do
let(:procedure) { create(:procedure) }
let(:archive) { create(:archive) }

View file

@ -10,18 +10,71 @@ describe FranceConnectInformation, type: :model do
end
describe 'associate_user!' do
context 'when there is no user with same email' do
let(:email) { 'A@email.com' }
let(:fci) { build(:france_connect_information) }
subject { fci.associate_user!(email) }
it { expect { subject }.to change(User, :count).by(1) }
context 'when there is no user with the same email' do
it 'creates a new user' do
expect { subject }.to change(User, :count).by(1)
end
it do
it 'sets the correct attributes on the user' do
subject
expect(fci.user.email).to eq('a@email.com')
expect(fci.user.email_verified_at).to be_present
user = User.find_by(email: email.downcase)
expect(user).not_to be_nil
expect(user.confirmed_at).to be_present
end
it 'sends custom confirmation instructions' do
expect(UserMailer).to receive(:custom_confirmation_instructions).and_call_original
subject
end
it 'associates the user with the FranceConnectInformation' do
subject
expect(fci.reload.user.email).to eq(email.downcase)
end
end
context 'when a user with the same email already exists due to race condition' do
let!(:existing_user) { create(:user, email: email.downcase) }
let!(:fci) { create(:france_connect_information) } # Assurez-vous que fci est créé et sauvegardé
before do
call_count = 0
allow(User).to receive(:create!).and_wrap_original do
call_count += 1
if call_count == 1
raise ActiveRecord::RecordNotUnique
else
existing_user
end
end
allow(fci).to receive(:send_custom_confirmation_instructions)
end
it 'raises an error' do
expect { fci.associate_user!(email) }.to raise_error(NoMethodError)
end
it 'does not create a new user' do
expect {
begin
fci.associate_user!(email)
rescue NoMethodError
end
}.to_not change(User, :count)
end
it 'does not associate with any user' do
expect(fci.user).to be_nil
begin
fci.associate_user!(email)
rescue NoMethodError
end
expect(fci.reload.user).to be_nil
end
end
end

View file

@ -18,12 +18,6 @@ describe User, type: :model do
user.confirm
expect(user.reload.invites.size).to eq(2)
end
it 'verifies its email' do
expect(user.email_verified_at).to be_nil
user.confirm
expect(user.email_verified_at).to be_present
end
end
describe '#owns?' do

View file

@ -43,9 +43,28 @@ describe 'France Connect Particulier Connexion' do
before { page.find('.fr-connect').click }
scenario 'he is redirected to user dossiers page' do
expect(page).to have_content('Dossiers')
expect(page).to have_content("Choisissez votre e-mail de contact")
expect(page).to have_selector("#use_france_connect_email_yes", visible: false, wait: 10)
find('#use_france_connect_email_yes').click
click_on 'Confirmer'
expect(page).to have_content("Confirmation envoyée")
click_on 'Continuer'
expect(User.find_by(email: email)).not_to be nil
end
scenario 'he can choose not to use FranceConnect email and input an alternative email' do
expect(page).to have_content("Choisissez votre e-mail de contact")
expect(page).to have_selector("#use_france_connect_email_no", visible: false, wait: 10)
find('#use_france_connect_email_no').click
expect(page).to have_selector("input[name='alternative_email']", visible: true, wait: 10)
fill_in 'alternative_email', with: 'alternative@example.com'
click_on 'Confirmer'
expect(page).to have_content("Confirmation envoyée")
click_on 'Continuer'
expect(User.find_by(email: 'alternative@example.com')).not_to be nil
end
end
context 'and an user exists with the same email' do
@ -79,17 +98,18 @@ describe 'France Connect Particulier Connexion' do
let!(:another_user) { create(:user, email: 'an_existing_email@a.com', password: SECURE_PASSWORD) }
scenario 'it uses another email that belongs to another account' do
page.find('#it-is-not-mine').click
find('label[for="it-is-not-mine"]').click
expect(page).to have_css('.new-account', visible: true)
within '.new-account' do
fill_in 'email', with: 'an_existing_email@a.com'
click_on 'Utiliser ce mail'
expect(page).to have_css('#password-for-another-account', visible: true)
within '#new-account-password-confirmation' do
fill_in 'password', with: SECURE_PASSWORD
click_on 'Fusionner les comptes'
end
fill_in 'password-for-another-account', with: SECURE_PASSWORD
last_button = all('input[type="submit"][value="Fusionner les comptes"]').last.click
puts last_button.click
expect(page).to have_content('Dossiers')
end
end

View file

@ -191,7 +191,15 @@ describe 'Prefilling a dossier (with a GET request):', js: true do
page.find('.fr-connect').click
click_on "Poursuivre mon dossier prérempli"
expect(page).to have_content("Choisissez votre e-mail de contact")
expect(page).to have_selector("#use_france_connect_email_yes", visible: false, wait: 10)
page.execute_script('document.getElementById("use_france_connect_email_yes").click()')
click_on 'Confirmer'
expect(page).to have_content("Confirmation envoyée")
click_on 'Continuer'
expect(page).to have_content('Vous avez un dossier prérempli')
find('.fr-btn.fr-mb-2w', text: 'Poursuivre mon dossier prérempli', wait: 10).click
end
end
end

View file

@ -142,9 +142,15 @@ describe 'Prefilling a dossier (with a POST request):', js: true do
allow(FranceConnectService).to receive(:retrieve_user_informations_particulier).and_return(build(:france_connect_information))
page.find('.fr-connect').click
expect(page).to have_content("Choisissez votre e-mail de contact")
expect(page).to have_selector("#use_france_connect_email_yes", visible: false, wait: 10)
page.execute_script('document.getElementById("use_france_connect_email_yes").click()')
click_on 'Confirmer'
expect(page).to have_content("Confirmation envoyée")
click_on 'Continuer'
expect(page).to have_content('Vous avez un dossier prérempli')
click_on 'Poursuivre mon dossier prérempli'
find('.fr-btn.fr-mb-2w', text: 'Poursuivre mon dossier prérempli', wait: 10).click
end
end
end