From 977eef8dfc323b775feb4ebcca13f1640d272d56 Mon Sep 17 00:00:00 2001 From: Ewen Corre Date: Mon, 13 Jan 2025 16:44:24 +0100 Subject: [PATCH] job_seekers_start: use a `Start` view for the get/create job seeker The `GetOrCreateJobSeekerForSenderStartView`'s role is to initialize a session before redirecting to `CheckNIRForSenderView`, which is the first step in the get-or-create job seeker process. We define 3 tunnels: - sender (a regular apply process) - hire - gps (which is still being worked on) --- itou/www/apply/views/submit_views.py | 40 +++- itou/www/job_seekers_views/urls.py | 5 + itou/www/job_seekers_views/views.py | 82 +++++++- migrations.md | 5 + tests/gps/test_create_beneficiary.py | 72 ++++++- tests/www/apply/test_submit.py | 115 ++++++++--- .../test_create_or_update.py | 190 +++++++++++++++++- 7 files changed, 456 insertions(+), 53 deletions(-) diff --git a/itou/www/apply/views/submit_views.py b/itou/www/apply/views/submit_views.py index 16b5b2d53a..2adc8827cd 100644 --- a/itou/www/apply/views/submit_views.py +++ b/itou/www/apply/views/submit_views.py @@ -145,7 +145,8 @@ def init_job_seeker_session(self, request): request.session, data={ "config": { - "reset_url": self.get_reset_url(), + "from_url": self.get_reset_url(), + "session_kind": "job-seeker-get-or-create-job-seeker", }, "apply": {"company_pk": self.company.pk}, }, @@ -296,13 +297,27 @@ def get(self, request, *args, **kwargs): reverse("apply:pending_authorization_for_sender", kwargs={"company_pk": self.company.pk}) ) - # Init a job_seeker_session needed for job_seekers_views - job_seeker_session = self.init_job_seeker_session(request) + if tunnel == "job_seeker": + # Init a job_seeker_session needed for job_seekers_views + job_seeker_session = self.init_job_seeker_session(request) - return HttpResponseRedirect( - reverse(f"job_seekers_views:check_nir_for_{tunnel}", kwargs={"session_uuid": job_seeker_session.name}) - + ("?gps=true" if self.is_gps else "") - ) + return HttpResponseRedirect( + reverse("job_seekers_views:check_nir_for_job_seeker", kwargs={"session_uuid": job_seeker_session.name}) + ) + + # TODO(ewen): get rid of GPS in apply + if self.is_gps: + tunnel = "gps" + + params = { + "tunnel": tunnel, + "company": self.company.pk, + "from_url": self.get_reset_url(), + "gps": "true" if self.is_gps else None, + } + + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) + return HttpResponseRedirect(next_url) class PendingAuthorizationForSender(ApplyStepForSenderBaseView): @@ -310,10 +325,13 @@ class PendingAuthorizationForSender(ApplyStepForSenderBaseView): def setup(self, request, *args, **kwargs): super().setup(request, *args, **kwargs) - self.job_seeker_session = self.init_job_seeker_session(request) - self.next_url = reverse( - "job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": self.job_seeker_session.name} - ) + params = { + "tunnel": "sender", + "company": self.company.pk, + "from_url": self.get_reset_url(), + } + + self.next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) def get_context_data(self, **kwargs): return super().get_context_data(**kwargs) | {"next_url": self.next_url} diff --git a/itou/www/job_seekers_views/urls.py b/itou/www/job_seekers_views/urls.py index 30d6f506bb..09b461f596 100644 --- a/itou/www/job_seekers_views/urls.py +++ b/itou/www/job_seekers_views/urls.py @@ -8,6 +8,11 @@ urlpatterns = [ path("details/", views.JobSeekerDetailView.as_view(), name="details"), path("list", views.JobSeekerListView.as_view(), name="list"), + path( + "start", + views.GetOrCreateJobSeekerForSenderStartView.as_view(), + name="get_or_create_start", + ), # For sender path("/sender/check-nir", views.CheckNIRForSenderView.as_view(), name="check_nir_for_sender"), path( diff --git a/itou/www/job_seekers_views/views.py b/itou/www/job_seekers_views/views.py index 3f6f093c88..50c6407ee8 100644 --- a/itou/www/job_seekers_views/views.py +++ b/itou/www/job_seekers_views/views.py @@ -27,7 +27,7 @@ from itou.utils.emails import redact_email_address from itou.utils.pagination import ItouPaginator from itou.utils.session import SessionNamespace, SessionNamespaceRequiredMixin -from itou.utils.urls import get_safe_url +from itou.utils.urls import add_url_params, get_safe_url from itou.www.apply.views.submit_views import ApplicationBaseView, ApplyStepBaseView from .forms import ( @@ -184,8 +184,74 @@ def get_queryset(self): return query +class GetOrCreateJobSeekerForSenderStartView(View): + def setup(self, request, *args, **kwargs): + super().setup(request, *args, **kwargs) + + tunnel = request.GET.get("tunnel") + self.is_gps = "gps" in request.GET and request.GET["gps"] == "true" + self.from_url = get_safe_url(request, "from_url") + if not self.from_url: + raise Http404 + + company = None + if tunnel == "gps": + company = Company.unfiltered_objects.get(siret=companies_enums.POLE_EMPLOI_SIRET) + else: + try: + company = ( + get_object_or_404(Company.objects.with_has_active_members(), pk=request.GET.get("company")) + if not self.is_gps + else Company.unfiltered_objects.get(siret=companies_enums.POLE_EMPLOI_SIRET) + ) + except ValueError: + raise Http404("Aucune entreprise n'a été trouvée") + + self.session_kind = None + match tunnel: + case "sender": + self.session_kind = "job-seeker-get-or-create-sender" + case "hire": + self.session_kind = "job-seeker-get-or-create-hire" + case "gps": + self.session_kind = "job-seeker-get-or-create-gps" + case _: + raise Http404 + + data = { + "config": { + "from_url": self.from_url, + "session_kind": self.session_kind, + }, + "apply": {"company_pk": company.pk} if company else {}, + } + self.job_seeker_session = SessionNamespace.create_uuid_namespace(request.session, data) + + def get(self, request, *args, **kwargs): + match self.session_kind: + case "job-seeker-get-or-create-sender": + view_name = "job_seekers_views:check_nir_for_sender" + case "job-seeker-get-or-create-hire": + view_name = "job_seekers_views:check_nir_for_hire" + case "job-seeker-get-or-create-gps": + # TODO(ewen): create a proper GPS tunnel instead of this redirection + params = { + "tunnel": "sender", + "gps": "true", + "from_url": self.from_url, + } + # This session won't be used + self.job_seeker_session.delete() + return HttpResponseRedirect(add_url_params(reverse("job_seekers_views:get_or_create_start"), params)) + + return HttpResponseRedirect( + reverse(view_name, kwargs={"session_uuid": self.job_seeker_session.name}) + + ("?gps=true" if self.is_gps else "") + ) + + class JobSeekerBaseView(TemplateView): - EXPECTED_SESSION_KIND = None + EXPECTED_SESSION_KINDS = None def __init__(self): super().__init__() @@ -203,7 +269,7 @@ def setup(self, request, *args, session_uuid, hire_process=False, **kwargs): # Ensure we are performing the action (update, create…) the session was created for. if ( session_kind := self.job_seeker_session.get("config").get("session_kind") - ) and session_kind != self.EXPECTED_SESSION_KIND: + ) and session_kind not in self.EXPECTED_SESSION_KINDS: raise Http404 self.is_gps = "gps" in request.GET and request.GET["gps"] == "true" if company_pk := self.job_seeker_session.get("apply", {}).get("company_pk"): @@ -244,7 +310,7 @@ def get_back_url(self): return None def get_reset_url(self): - return self.job_seeker_session.get("config", {}).get("reset_url") or reverse("dashboard:index") + return self.job_seeker_session.get("config", {}).get("from_url") or reverse("dashboard:index") def get_context_data(self, **kwargs): return super().get_context_data(**kwargs) | { @@ -276,7 +342,7 @@ def dispatch(self, request, *args, **kwargs): class CheckNIRForJobSeekerView(JobSeekerBaseView): template_name = "job_seekers_views/step_check_job_seeker_nir.html" - EXPECTED_SESSION_KIND = "job-seeker-get-or-create-job-seeker" + EXPECTED_SESSION_KINDS = ["job-seeker-get-or-create-job-seeker"] def __init__(self): super().__init__() @@ -336,7 +402,7 @@ def get_context_data(self, **kwargs): class CheckNIRForSenderView(JobSeekerForSenderBaseView): template_name = "job_seekers_views/step_check_job_seeker_nir.html" - EXPECTED_SESSION_KIND = "job-seeker-get-or-create-sender" + EXPECTED_SESSION_KINDS = ["job-seeker-get-or-create-sender", "job-seeker-get-or-create-hire"] def __init__(self): super().__init__() @@ -398,6 +464,7 @@ def get_context_data(self, **kwargs): class SearchByEmailForSenderView(JobSeekerForSenderBaseView): template_name = "job_seekers_views/step_search_job_seeker_by_email.html" + EXPECTED_SESSION_KINDS = ["job-seeker-get-or-create-sender", "job-seeker-get-or-create-hire"] def __init__(self): super().__init__() @@ -496,6 +563,7 @@ def get_context_data(self, **kwargs): class CreateJobSeekerForSenderBaseView(JobSeekerForSenderBaseView): required_session_namespaces = ["job_seeker_session"] + EXPECTED_SESSION_KINDS = ["job-seeker-get-or-create-sender", "job-seeker-get-or-create-hire"] def __init__(self): super().__init__() @@ -799,7 +867,7 @@ def get(self, request, *args, **kwargs): class UpdateJobSeekerBaseView(JobSeekerBaseView): - EXPECTED_SESSION_KIND = "job-seeker-update" + EXPECTED_SESSION_KINDS = ["job-seeker-update"] def __init__(self): super().__init__() diff --git a/migrations.md b/migrations.md index 44d5f0d37a..c5491d6620 100644 --- a/migrations.md +++ b/migrations.md @@ -33,3 +33,8 @@ Cette migration est problématique quand la modale est liée à un `{% include % car on ne peut pas avoir des `{% include %}` et des `{% block %}`, mais devrait éviter des problèmes de `z-index` lorsque le *markup* de la modale est dans un élément avec `z-index` différent. + +## Extraction du parcours de recherche/création/modification de compte candidat +Historiquement, la création de compte candidat par un prescripteur était rattachée au processus de candidature. +Nous voulons à présent déconnecter les deux processus. +Trois applications sont impactées : `job_seekers_views`, `apply` et `gps`. diff --git a/tests/gps/test_create_beneficiary.py b/tests/gps/test_create_beneficiary.py index 4855098eb8..5b64875262 100644 --- a/tests/gps/test_create_beneficiary.py +++ b/tests/gps/test_create_beneficiary.py @@ -16,6 +16,7 @@ from itou.users.models import User from itou.utils.mocks.address_format import mock_get_geocoding_data_by_ban_api_resolved from itou.utils.models import InclusiveDateRange +from itou.utils.urls import add_url_params from tests.cities.factories import create_city_geispolsheim, create_test_cities from tests.companies.factories import CompanyWithMembershipAndJobsFactory from tests.prescribers.factories import PrescriberOrganizationWithMembershipFactory @@ -53,6 +54,22 @@ def test_create_job_seeker(_mock, client): apply_start_url = reverse("apply:start", kwargs={"company_pk": singleton.pk}) + "?gps=true" response = client.get(apply_start_url) + params = { + "tunnel": "gps", + "company": singleton.pk, + "from_url": reverse("companies_views:card", kwargs={"siae_id": singleton.pk}), + "gps": "true", + } + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) + assert response.url == next_url + + # TODO(ewen): remove this redirection + response = client.get(next_url) + new_params = {"tunnel": "sender", "gps": "true", "from_url": params["from_url"]} + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), new_params) + assert response.url == next_url + + response = client.get(next_url) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] next_url = ( reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) @@ -79,7 +96,8 @@ def test_create_job_seeker(_mock, client): expected_job_seeker_session = { "config": { - "reset_url": reverse("companies_views:card", kwargs={"siae_id": singleton.pk}), + "from_url": reverse("companies_views:card", kwargs={"siae_id": singleton.pk}), + "session_kind": "job-seeker-get-or-create-sender", }, "apply": {"company_pk": singleton.pk}, "profile": { @@ -238,6 +256,22 @@ def test_gps_bypass(client): apply_start_url = reverse("apply:start", kwargs={"company_pk": singleton.pk}) + "?gps=true" response = client.get(apply_start_url) + params = { + "tunnel": "gps", + "company": singleton.pk, + "from_url": reverse("companies_views:card", kwargs={"siae_id": singleton.pk}), + "gps": "true", + } + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) + assert response.url == next_url + + # TODO(ewen): remove this redirection + response = client.get(next_url) + new_params = {"tunnel": "sender", "gps": "true", "from_url": params["from_url"]} + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), new_params) + assert response.url == next_url + + response = client.get(next_url) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] next_url = ( reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) @@ -258,6 +292,23 @@ def test_gps_bypass(client): apply_start_url = reverse("apply:start", kwargs={"company_pk": singleton.pk}) + "?gps=true" response = client.get(apply_start_url) + params = { + "tunnel": "gps", + "company": singleton.pk, + "from_url": reverse("companies_views:card", kwargs={"siae_id": singleton.pk}), + "gps": "true", + } + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) + assert response.url == next_url + + # TODO(ewen): remove this redirection + response = client.get(next_url) + new_params = {"tunnel": "sender", "gps": "true", "from_url": params["from_url"]} + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), new_params) + assert response.url == next_url + + response = client.get(next_url) + [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] next_url = ( reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) @@ -308,7 +359,8 @@ def test_existing_user_with_email(client): ) expected_job_seeker_session = { "config": { - "reset_url": reverse("companies_views:card", kwargs={"siae_id": singleton.pk}), + "from_url": reverse("companies_views:card", kwargs={"siae_id": singleton.pk}), + "session_kind": "job-seeker-get-or-create-sender", }, "apply": {"company_pk": singleton.pk}, "profile": { @@ -411,6 +463,22 @@ def test_creation_by_user_kind(client, UserFactory, factory_args, expected_acces assert response.status_code == 403 response = client.get(create_beneficiary_url) + params = { + "tunnel": "gps", + "company": singleton.pk, + "from_url": reverse("companies_views:card", kwargs={"siae_id": singleton.pk}), + "gps": "true", + } + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) + assert response.url == next_url + + # TODO(ewen): remove this redirection + response = client.get(next_url) + new_params = {"tunnel": "sender", "gps": "true", "from_url": params["from_url"]} + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), new_params) + assert response.url == next_url + + response = client.get(next_url) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] assert response.status_code == 302 assert ( diff --git a/tests/www/apply/test_submit.py b/tests/www/apply/test_submit.py index bfb2a9491d..432a187b2c 100644 --- a/tests/www/apply/test_submit.py +++ b/tests/www/apply/test_submit.py @@ -856,17 +856,31 @@ def test_apply_as_prescriber_with_pending_authorization(self, client, pdf_file): # ---------------------------------------------------------------------- response = client.get(next_url) - [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] - next_url = reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) + params = { + "tunnel": "sender", + "company": company.pk, + "from_url": reverse("companies_views:card", kwargs={"siae_id": company.pk}), + } + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) assertContains(response, "Statut de prescripteur habilité non vérifié") - assertContains(response, next_url) + assertContains( + response, + f""" + + Suivant + + """, + html=True, + ) # Step determine the job seeker with a NIR. # ---------------------------------------------------------------------- response = client.get(next_url) - assert response.status_code == 200 + [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] + next_url = reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) + assertRedirects(response, next_url) response = client.post(next_url, data={"nir": dummy_job_seeker.jobseeker_profile.nir, "confirm": 1}) assert response.status_code == 302 @@ -876,9 +890,7 @@ def test_apply_as_prescriber_with_pending_authorization(self, client, pdf_file): kwargs={"session_uuid": job_seeker_session_name}, ) expected_job_seeker_session = { - "config": { - "reset_url": reverse("companies_views:card", kwargs={"siae_id": company.pk}), - }, + "config": {"from_url": params["from_url"], "session_kind": "job-seeker-get-or-create-sender"}, "apply": {"company_pk": company.pk}, "profile": { "nir": dummy_job_seeker.jobseeker_profile.nir, @@ -1128,6 +1140,15 @@ def test_apply_as_authorized_prescriber(self, client, pdf_file): response = client.get(reverse("apply:start", kwargs={"company_pk": company.pk})) assert response.status_code == 302 + params = { + "tunnel": "sender", + "company": company.pk, + "from_url": reset_url_company, + } + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) + assert response.url == next_url + + response = client.get(next_url) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] next_url = reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) assert response.url == next_url @@ -1160,7 +1181,8 @@ def test_apply_as_authorized_prescriber(self, client, pdf_file): expected_job_seeker_session = { "config": { - "reset_url": reset_url_company, + "from_url": reset_url_company, + "session_kind": "job-seeker-get-or-create-sender", }, "apply": {"company_pk": company.pk}, "user": { @@ -1399,7 +1421,7 @@ def test_cannot_create_job_seeker_with_pole_emploi_email(self, client): # Init session start_url = reverse("apply:start", kwargs={"company_pk": company.pk}) - client.get(start_url) + client.get(start_url, follow=True) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] nir_url = reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) response = client.get(nir_url) @@ -1459,6 +1481,15 @@ def test_apply_with_temporary_nir(self, client): response = client.get(reverse("apply:start", kwargs={"company_pk": company.pk})) assert response.status_code == 302 + params = { + "tunnel": "sender", + "company": company.pk, + "from_url": reverse("companies_views:card", kwargs={"siae_id": company.pk}), + } + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) + assert response.url == next_url + + response = client.get(next_url) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] assertRedirects( response, @@ -1509,7 +1540,7 @@ def test_apply_as_prescriber_with_suspension_sanction(self, client): user = PrescriberFactory() client.force_login(user) - response = client.get(reverse("apply:start", kwargs={"company_pk": company.pk})) + response = client.get(reverse("apply:start", kwargs={"company_pk": company.pk}), follow=True) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] # The suspension does not prevent the access to the process @@ -1542,7 +1573,15 @@ def test_apply_as_prescriber(self, client, pdf_file): response = client.get(reverse("apply:start", kwargs={"company_pk": company.pk})) assert response.status_code == 302 + params = { + "tunnel": "sender", + "company": company.pk, + "from_url": reset_url_company, + } + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) + assert response.url == next_url + response = client.get(next_url) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] next_url = reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) assert response.url == next_url @@ -1565,7 +1604,8 @@ def test_apply_as_prescriber(self, client, pdf_file): expected_job_seeker_session = { "config": { - "reset_url": reset_url_company, + "from_url": reset_url_company, + "session_kind": "job-seeker-get-or-create-sender", }, "apply": {"company_pk": company.pk}, "profile": {"nir": dummy_job_seeker.jobseeker_profile.nir}, @@ -1960,7 +2000,8 @@ def test_one_account_no_nir(self, client): ) expected_job_seeker_session = { "config": { - "reset_url": reverse("companies_views:card", kwargs={"siae_id": company.pk}), + "from_url": reverse("companies_views:card", kwargs={"siae_id": company.pk}), + "session_kind": "job-seeker-get-or-create-sender", }, "apply": {"company_pk": company.pk}, "profile": { @@ -2045,7 +2086,8 @@ def test_one_account_lack_of_nir_reason(self, client): ) expected_job_seeker_session = { "config": { - "reset_url": reverse("companies_views:card", kwargs={"siae_id": siae.pk}), + "from_url": reverse("companies_views:card", kwargs={"siae_id": siae.pk}), + "session_kind": "job-seeker-get-or-create-sender", }, "apply": {"company_pk": siae.pk}, "profile": { @@ -2098,7 +2140,7 @@ def test_perms_for_company(self, client): user = company_1.members.first() client.force_login(user) - response = client.get(reverse("apply:start", kwargs={"company_pk": company_2.pk})) + response = client.get(reverse("apply:start", kwargs={"company_pk": company_2.pk}), follow=True) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] assertRedirects( response, @@ -2139,6 +2181,15 @@ def _test_apply_as_company(self, client, user, company, dummy_job_seeker, pdf_fi response = client.get(reverse("apply:start", kwargs={"company_pk": company.pk})) assert response.status_code == 302 + params = { + "tunnel": "sender", + "company": company.pk, + "from_url": reset_url, + } + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) + assert response.url == next_url + + response = client.get(next_url) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] next_url = reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) assert response.url == next_url @@ -2158,7 +2209,7 @@ def _test_apply_as_company(self, client, user, company, dummy_job_seeker, pdf_fi kwargs={"session_uuid": job_seeker_session_name}, ) expected_job_seeker_session = { - "config": {"reset_url": reset_url}, + "config": {"from_url": reset_url, "session_kind": "job-seeker-get-or-create-sender"}, "apply": {"company_pk": company.pk}, "profile": { "nir": dummy_job_seeker.jobseeker_profile.nir, @@ -2499,7 +2550,7 @@ def test_cannot_create_job_seeker_with_pole_emploi_email(self, client): # Init session start_url = reverse("apply:start", kwargs={"company_pk": company.pk}) - client.get(start_url) + client.get(start_url, follow=True) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] nir_url = reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) response = client.get(nir_url) @@ -2598,7 +2649,7 @@ def test_hire_as_company(self, client): # Step determine the job seeker with a NIR. # ---------------------------------------------------------------------- # Init session - response = client.get(reverse("apply:start_hire", kwargs={"company_pk": company.pk})) + response = client.get(reverse("apply:start_hire", kwargs={"company_pk": company.pk}), follow=True) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] check_nir_url = reverse( @@ -2628,7 +2679,7 @@ def test_hire_as_company(self, client): assert response.status_code == 302 expected_job_seeker_session = { - "config": {"reset_url": reset_url_dashboard}, + "config": {"from_url": reset_url_dashboard, "session_kind": "job-seeker-get-or-create-hire"}, "apply": {"company_pk": company.pk}, "profile": { "nir": dummy_job_seeker.jobseeker_profile.nir, @@ -2908,7 +2959,7 @@ def test_hire_as_geiq(self, client): # Step determine the job seeker with a NIR. # ---------------------------------------------------------------------- # Init session - response = client.get(reverse("apply:start_hire", kwargs={"company_pk": company.pk})) + response = client.get(reverse("apply:start_hire", kwargs={"company_pk": company.pk}), follow=True) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] check_nir_url = reverse( @@ -3137,7 +3188,7 @@ def test_application_start_with_invalid_job_description_id(self, client): company = CompanyFactory(subject_to_eligibility=True, with_membership=True, with_jobs=True) client.force_login(company.members.get()) response = client.get( - reverse("apply:start", kwargs={"company_pk": company.pk}), {"job_description_id": "invalid"} + reverse("apply:start", kwargs={"company_pk": company.pk}), {"job_description_id": "invalid"}, follow=True ) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] assertRedirects( @@ -4683,13 +4734,21 @@ def test_detect_existing_job_seeker(client): response = client.get(reverse("apply:start", kwargs={"company_pk": company.pk})) assert response.status_code == 302 - [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] - next_url = reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) + params = { + "tunnel": "sender", + "company": company.pk, + "from_url": reverse("companies_views:card", kwargs={"siae_id": company.pk}), + } + next_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) assert response.url == next_url # Step determine the job seeker with a NIR. # ---------------------------------------------------------------------- + response = client.get(next_url) + [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] + next_url = reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) + response = client.get(next_url) assert response.status_code == 200 @@ -4702,7 +4761,8 @@ def test_detect_existing_job_seeker(client): ) expected_job_seeker_session = { "config": { - "reset_url": reverse("companies_views:card", kwargs={"siae_id": company.pk}), + "session_kind": "job-seeker-get-or-create-sender", + "from_url": reverse("companies_views:card", kwargs={"siae_id": company.pk}), }, "apply": {"company_pk": company.pk}, "profile": { @@ -5273,7 +5333,7 @@ def setup_method(self): def get_check_nir_url(self, client): # Init session start_url = reverse("apply:start_hire", kwargs={"company_pk": self.company.pk}) - client.get(start_url) + client.get(start_url, follow=True) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] return reverse("job_seekers_views:check_nir_for_hire", kwargs={"session_uuid": job_seeker_session_name}) @@ -5392,7 +5452,8 @@ def test_no_job_seeker_redirect_to_create(self, client): expected_job_seeker_session = { "config": { - "reset_url": reverse("dashboard:index"), # Hire: reset_url = dashboard + "from_url": reverse("dashboard:index"), # Hire: reset_url = dashboard + "session_kind": "job-seeker-get-or-create-hire", }, "apply": {"company_pk": self.company.pk}, "user": { @@ -5822,7 +5883,7 @@ def test_as_employer(self, client): client.force_login(self.company.members.first()) # Init session - response = client.get(reverse("apply:start_hire", kwargs={"company_pk": self.company.pk})) + response = client.get(reverse("apply:start_hire", kwargs={"company_pk": self.company.pk}), follow=True) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] response = client.get( reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name}) @@ -5838,7 +5899,7 @@ def test_as_employer(self, client): client.force_login(self.geiq.members.first()) # Init session - response = client.get(reverse("apply:start_hire", kwargs={"company_pk": self.geiq.pk})) + response = client.get(reverse("apply:start_hire", kwargs={"company_pk": self.geiq.pk}), follow=True) [job_seeker_session_name_geiq] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] response = client.get( reverse("job_seekers_views:check_nir_for_sender", kwargs={"session_uuid": job_seeker_session_name_geiq}) diff --git a/tests/www/job_seekers_views/test_create_or_update.py b/tests/www/job_seekers_views/test_create_or_update.py index d91051cacf..f79cd324f9 100644 --- a/tests/www/job_seekers_views/test_create_or_update.py +++ b/tests/www/job_seekers_views/test_create_or_update.py @@ -68,13 +68,100 @@ def test_cannot_check_nir_if_already_set(self, client): class TestCreateForSender: + @pytest.mark.parametrize( + "company_value, tunnel_value, from_url_value, expected_status_code", + [ + # Valid parameters + pytest.param("valid", "valid", "valid", 302, id="valid_values"), + pytest.param("valid", "valid_hire", "valid", 302, id="valid_values_hire"), + # Invalid parameters + pytest.param(None, "valid", "valid", 404, id="missing_company"), + pytest.param(None, "valid", "valid_hire", 404, id="missing_company_hire"), + pytest.param("invalid", "valid", "valid", 404, id="invalid_company"), + pytest.param("valid", "invalid", "valid", 404, id="invalid_tunnel"), + pytest.param("valid", None, "valid", 404, id="missing_tunnel"), + pytest.param("valid", "valid", None, 404, id="missing_from_url"), + ], + ) + def test_start_get_or_create_sender( + self, + company_value, + tunnel_value, + from_url_value, + expected_status_code, + client, + ): + job_seeker = JobSeekerFactory() + company = CompanyFactory(with_membership=True) + user = company.members.get() + client.force_login(user) + + match company_value: + case "valid": + company_pk = company.pk + case "invalid": + company_pk = "invalid_pk" + case _: + company_pk = None + + match tunnel_value: + case "valid": + tunnel = "sender" + case "valid_hire": + tunnel = "hire" + case "invalid": + tunnel = "invalid-tunnel" + case _: + tunnel = None + + if from_url_value == "valid": + from_url = reverse( + "apply:application_jobs", + kwargs={"company_pk": company.pk, "job_seeker_public_id": job_seeker.public_id}, + ) + else: + from_url = None + + params = { + "tunnel": tunnel, + "company": company_pk, + "from_url": from_url, + } + start_url = add_url_params(reverse("job_seekers_views:get_or_create_start"), params) + + response = client.get(start_url) + assert response.status_code == expected_status_code + + if expected_status_code == 302: + [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] + next_url = reverse( + f"job_seekers_views:check_nir_for_{tunnel}", + kwargs={"session_uuid": job_seeker_session_name}, + ) + + assertRedirects(response, next_url) + assert client.session[job_seeker_session_name].get("config").get("from_url") == from_url + response = client.get(next_url) + assertContains( + response, + f""" + + + Annuler + + """, + html=True, + ) + def test_check_nir_with_session(self, client): company = CompanyFactory(with_membership=True) client.force_login(company.members.get()) # Init session start_url = reverse("apply:start", kwargs={"company_pk": company.pk}) - client.get(start_url) + client.get(start_url, follow=True) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] response = client.get( @@ -91,7 +178,7 @@ def test_create_step_1(self, born_in_france, client): client.force_login(company.members.get()) # Init session - client.get(reverse("apply:start", kwargs={"company_pk": company.pk})) + client.get(reverse("apply:start", kwargs={"company_pk": company.pk}), follow=True) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] birthdate = datetime.date(1911, 11, 1) @@ -129,7 +216,7 @@ def test_birth_country_not_france_and_birthplace(self, client): client.force_login(company.members.get()) # Init session - client.get(reverse("apply:start", kwargs={"company_pk": company.pk})) + client.get(reverse("apply:start", kwargs={"company_pk": company.pk}), follow=True) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] birthdate = datetime.date(1911, 11, 1) @@ -163,7 +250,7 @@ def test_birth_country_france_and_no_birthplace(self, client): client.force_login(company.members.get()) # Init session - client.get(reverse("apply:start", kwargs={"company_pk": company.pk})) + client.get(reverse("apply:start", kwargs={"company_pk": company.pk}), follow=True) [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] response = client.post( @@ -192,8 +279,99 @@ def test_birth_country_france_and_no_birthplace(self, client): ) -class TestUpdateJobSeeker: - def test_update_with_wrong_tunnel_in_session(self, client): +class TestUpdateForJobSeeker: + def test_start_update_job_seeker_forbidden(self, client): + job_seeker = JobSeekerFactory(jobseeker_profile__birthdate=None, jobseeker_profile__nir="") + company = CompanyFactory(with_membership=True) + client.force_login(job_seeker) + + company_pk = company.pk + + from_url = reverse( + "apply:application_jobs", + kwargs={"company_pk": company.pk, "job_seeker_public_id": job_seeker.public_id}, + ) + params = { + "job_seeker": job_seeker.public_id, + "company": company_pk, + "from_url": from_url, + } + start_url = add_url_params(reverse("job_seekers_views:update_job_seeker_start"), params) + + response = client.get(start_url) + assert response.status_code == 403 + + +class TestUpdateForSender: + @pytest.mark.parametrize( + "job_seeker_value, from_url_value, expected_status_code", + [ + # Valid parameters + pytest.param("valid", "valid", 302, id="valid_values"), + # Invalid parameters + pytest.param("invalid_uuid", "valid", 404, id="invalid_job_seeker_not_a_uuid"), + pytest.param("invalid", "valid", 404, id="invalid_job_seeker_not_found"), + pytest.param(None, "valid", 404, id="missing_job_seeker"), + pytest.param("valid", None, 404, id="missing_from_url"), + ], + ) + def test_start_update(self, job_seeker_value, from_url_value, expected_status_code, client): + job_seeker = JobSeekerFactory() + company = CompanyFactory(with_membership=True) + user = company.members.get() + client.force_login(user) + + match job_seeker_value: + case "valid": + job_seeker_public_id = job_seeker.public_id + case "invalid_uuid": + job_seeker_public_id = "invalid_uuid_value" + case "invalid": + job_seeker_public_id = uuid.uuid4() + case _: + job_seeker_public_id = None + + if from_url_value == "valid": + from_url = reverse( + "apply:application_jobs", + kwargs={"company_pk": company.pk, "job_seeker_public_id": job_seeker.public_id}, + ) + else: + from_url = None + + params = { + "job_seeker": job_seeker_public_id, + "from_url": from_url, + } + start_url = add_url_params(reverse("job_seekers_views:update_job_seeker_start"), params) + + response = client.get(start_url) + assert response.status_code == expected_status_code + + if expected_status_code == 302: + [job_seeker_session_name] = [k for k in client.session.keys() if k not in KNOWN_SESSION_KEYS] + step_1_url = reverse( + "job_seekers_views:update_job_seeker_step_1", + kwargs={"session_uuid": job_seeker_session_name}, + ) + + assertRedirects(response, step_1_url) + assert client.session[job_seeker_session_name].get("config").get("from_url") == from_url + response = client.get(step_1_url) + assertContains( + response, + f""" + + + Annuler + + """, + html=True, + ) + + def test_update_with_wrong_session(self, client): job_seeker = JobSeekerFactory() company = CompanyFactory(with_membership=True) prescriber = PrescriberOrganizationWithMembershipFactory(authorized=True).members.first()