def test_cancel(self): # Hiring date is today: cancellation should be possible. job_application = JobApplicationWithApprovalFactory( state=JobApplicationWorkflow.STATE_ACCEPTED) siae_user = job_application.to_siae.members.first() self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD) url = reverse("apply:cancel", kwargs={"job_application_id": job_application.pk}) response = self.client.get(url) self.assertEqual(response.status_code, 302) job_application.refresh_from_db() self.assertTrue(job_application.state.is_cancelled)
def test_can_be_cancelled(self): today = datetime.date.today() cancellation_days_in_future = relativedelta( days=JobApplication.CANCELLATION_DAYS_AFTER_HIRING_STARTED) cancellation_period_end = today - cancellation_days_in_future job_application_future_ok = JobApplicationWithApprovalFactory( hiring_start_at=cancellation_period_end) self.assertTrue(job_application_future_ok.can_be_cancelled) job_application_future_not_ok = JobApplicationWithApprovalFactory( hiring_start_at=(cancellation_period_end - relativedelta(days=365))) self.assertFalse(job_application_future_not_ok.can_be_cancelled)
def test_cannot_cancel(self): cancellation_period_end = datetime.date.today() - relativedelta( days=JobApplication.CANCELLATION_DAYS_AFTER_HIRING_STARTED) job_application = JobApplicationWithApprovalFactory( state=JobApplicationWorkflow.STATE_ACCEPTED, hiring_start_at=(cancellation_period_end - relativedelta(days=1)), ) siae_user = job_application.to_siae.members.first() self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD) url = reverse("apply:cancel", kwargs={"job_application_id": job_application.pk}) response = self.client.get(url) self.assertEqual(response.status_code, 302) job_application.refresh_from_db() self.assertFalse(job_application.state.is_cancelled)
def setUp(self): self.job_application = JobApplicationWithApprovalFactory(state=JobApplicationWorkflow.STATE_ACCEPTED) self.siae = self.job_application.to_siae self.siae_user = self.job_application.to_siae.members.first() self.approval = self.job_application.approval self.job_seeker = self.job_application.job_seeker self.pe_approval = PoleEmploiApprovalFactory()
def test_has_matching_pass_iae_that_belongs_to_another_siae(self): """ Make sure to NOT to redirect to job applications belonging to other SIAEs, as this would produce a 404. """ # Initial approvals (PE and PASS) self.set_up_pe_approval() # Create a job application with a PASS IAE created from a `PoleEmploiApproval` # that belongs to another siae. job_seeker = JobSeekerFactory() pe_approval = PoleEmploiApprovalFactory() job_application = JobApplicationWithApprovalFactory( state=JobApplicationWorkflow.STATE_ACCEPTED, approval__number=pe_approval.number, approval__user=job_seeker, job_seeker=job_seeker, ) another_siae = job_application.to_siae self.assertNotEqual(another_siae, self.siae) # This is the current user (NOT a member of `another_siae`). self.client.login(username=self.siae_user.email, password=DEFAULT_PASSWORD) # The current user should not be able to use the PASS IAE used by another SIAE. response = self.client.get(self.url, {"number": job_application.approval.number}) self.assertNotContains(response, "Continuer")
def test_download_approval_missing_diagnosis_ai(self, *args, **kwargs): """ Given an existing job application with an approval delivered by Itou when importing AI employees, when an AI tries to download it as PDF, it works. """ # On November 30th, 2021, AI were delivered approvals without a diagnosis. # See itou.users.management.commands.import_ai_employees. approval_created_at = settings.AI_EMPLOYEES_STOCK_IMPORT_DATE approval_created_by = UserFactory(email=settings.AI_EMPLOYEES_STOCK_DEVELOPER_EMAIL) job_application = JobApplicationWithApprovalFactory( eligibility_diagnosis=None, approval__created_at=approval_created_at, approval__created_by=approval_created_by, ) siae_member = job_application.to_siae.members.first() self.client.login(username=siae_member.email, password=DEFAULT_PASSWORD) response = self.client.get( reverse("approvals:approval_as_pdf", kwargs={"job_application_id": job_application.pk}) ) self.assertEqual(response.status_code, 200) self.assertIn("pdf", response.get("Content-Type"))
def test_cannot_cancel(self, *args, **kwargs): job_application = JobApplicationWithApprovalFactory( state=JobApplicationWorkflow.STATE_ACCEPTED, hiring_start_at=timezone.localdate() + relativedelta(days=1), ) siae_user = job_application.to_siae.members.first() # Add a blocking employee record EmployeeRecordFactory(job_application=job_application, status=EmployeeRecord.Status.PROCESSED) self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD) url = reverse("apply:cancel", kwargs={"job_application_id": job_application.pk}) response = self.client.get(url) next_url = reverse("apply:details_for_siae", kwargs={"job_application_id": job_application.pk}) self.assertRedirects(response, next_url) job_application.refresh_from_db() self.assertFalse(job_application.state.is_cancelled)
def test_mise_a_jour_pass_iae_invalid_token(self, mock_post): """If the API answers with a non-200 http code, mise_a_jour_pass_iae will return false""" job_application = JobApplicationWithApprovalFactory() with self.assertRaises(PoleEmploiMiseAJourPassIAEException): mise_a_jour_pass_iae( job_application, POLE_EMPLOI_PASS_APPROVED, "some_valid_encrypted_identifier", "some_valid_token" ) mock_post.assert_called()
def test_suspend_approval(self): """ Test the creation of a suspension. """ today = timezone.localdate() job_application = JobApplicationWithApprovalFactory( state=JobApplicationWorkflow.STATE_ACCEPTED, hiring_start_at=today - relativedelta(days=1), ) # Ensure that the job_application cannot be canceled. EmployeeRecordFactory(job_application=job_application, status=EmployeeRecord.Status.PROCESSED) approval = job_application.approval self.assertEqual(0, approval.suspension_set.count()) siae_user = job_application.to_siae.members.first() self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD) back_url = "/" params = urlencode({"back_url": back_url}) url = reverse("approvals:suspend", kwargs={"approval_id": approval.pk}) url = f"{url}?{params}" response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.context["preview"], False) start_at = today end_at = today + relativedelta(days=10) post_data = { "start_at": start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "end_at": end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "reason": Suspension.Reason.SUSPENDED_CONTRACT, "reason_explanation": "", # Preview. "preview": "1", } # Go to preview. response = self.client.post(url, data=post_data) self.assertEqual(response.status_code, 200) self.assertEqual(response.context["preview"], True) # Save to DB. del post_data["preview"] post_data["save"] = 1 response = self.client.post(url, data=post_data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, back_url) self.assertEqual(1, approval.suspension_set.count()) suspension = approval.suspension_set.first() self.assertEqual(suspension.created_by, siae_user)
def test_as_job_seeker_is_not_authorized(self): """ The search for PE approval screen as job seeker is not authorized """ job_application = JobApplicationWithApprovalFactory(state=JobApplicationWorkflow.STATE_ACCEPTED) self.client.login(username=job_application.job_seeker.email, password=DEFAULT_PASSWORD) response = self.client.get(self.url) self.assertEqual(response.status_code, 404)
def test_cancel(self, *args, **kwargs): # Hiring date is today: cancellation should be possible. job_application = JobApplicationWithApprovalFactory(state=JobApplicationWorkflow.STATE_ACCEPTED) siae_user = job_application.to_siae.members.first() self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD) url = reverse("apply:cancel", kwargs={"job_application_id": job_application.pk}) response = self.client.get(url) self.assertEqual(response.status_code, 200) post_data = { "confirm": "true", } response = self.client.post(url, data=post_data) next_url = reverse("apply:details_for_siae", kwargs={"job_application_id": job_application.pk}) self.assertRedirects(response, next_url) job_application.refresh_from_db() self.assertTrue(job_application.state.is_cancelled)
def test_mise_a_jour_pass_iae_success_with_approval_accepted(self, mock_post): """ Nominal scenario: an approval is **accepted** HTTP 200 + codeSortie = S001 is the only way mise_a_jour_pass_iae will return True""" job_application = JobApplicationWithApprovalFactory() result = mise_a_jour_pass_iae( job_application, POLE_EMPLOI_PASS_APPROVED, "some_valid_encrypted_identifier", "some_valid_token" ) mock_post.assert_called() self.assertTrue(result)
def test_mise_a_jour_pass_iae_failure(self, mock_post): """ If the API answers with a non-S001 codeSortie (this is something in the json output) mise_a_jour_pass_iae will return false """ job_application = JobApplicationWithApprovalFactory() with self.assertRaises(PoleEmploiMiseAJourPassIAEException): mise_a_jour_pass_iae( job_application, POLE_EMPLOI_PASS_APPROVED, "some_valid_encrypted_identifier", "some_valid_token" ) mock_post.assert_called()
def test_no_download_if_missing_diagnosis(self, *args, **kwargs): """ Given an existing job application with an approval delivered by Itou but no diagnosis, when trying to download it as PDF, then it raises an error. """ job_application = JobApplicationWithApprovalFactory(eligibility_diagnosis=None) siae_member = job_application.to_siae.members.first() self.client.login(username=siae_member.email, password=DEFAULT_PASSWORD) with self.assertRaises(ObjectDoesNotExist): self.client.get(reverse("approvals:approval_as_pdf", kwargs={"job_application_id": job_application.pk}))
def test_can_download_approval_as_pdf(self, mock_can_be_cancelled): """ A user can download an approval only when certain conditions are met: - the job_application.to_siae is subject to eligibility rules, - an approval exists (ie is not in the process of being delivered), - this approval is valid, - the job_application has been accepted. """ job_application = JobApplicationWithApprovalFactory() self.assertTrue(job_application.can_download_approval_as_pdf) # SIAE not subject to eligibility rules. not_eligible_kinds = [ choice[0] for choice in Siae.KIND_CHOICES if choice[0] not in Siae.ELIGIBILITY_REQUIRED_KINDS ] not_eligible_siae = SiaeFactory(kind=not_eligible_kinds[0]) job_application = JobApplicationWithApprovalFactory( to_siae=not_eligible_siae) self.assertFalse(job_application.can_download_approval_as_pdf) # Application is not accepted. job_application = JobApplicationWithApprovalFactory( state=JobApplicationWorkflow.STATE_OBSOLETE) self.assertFalse(job_application.can_download_approval_as_pdf) # Application accepted but without approval. job_application = JobApplicationFactory( state=JobApplicationWorkflow.STATE_ACCEPTED) self.assertFalse(job_application.can_download_approval_as_pdf) # Approval has ended start = datetime.date.today() - relativedelta(years=2) end = start + relativedelta(years=1) - relativedelta(days=1) ended_approval = ApprovalFactory(start_at=start, end_at=end) job_application = JobApplicationWithApprovalFactory( approval=ended_approval) self.assertFalse(job_application.can_download_approval_as_pdf)
def test_pdfshift_api_is_down(self, *args, **kwargs): job_application = JobApplicationWithApprovalFactory() siae_member = job_application.to_siae.members.first() job_seeker = job_application.job_seeker EligibilityDiagnosisFactory(job_seeker=job_seeker) self.client.login(username=siae_member.email, password=DEFAULT_PASSWORD) with self.assertRaises(ConnectionAbortedError): self.client.get( reverse("approvals:approval_as_pdf", kwargs={"job_application_id": job_application.pk}))
def test_no_download_if_missing_diagnosis(self, *args, **kwargs): job_application = JobApplicationWithApprovalFactory() siae_member = job_application.to_siae.members.first() # An approval has been delivered by Itou but there is no diagnosis. # It should raise an error. # EligibilityDiagnosisFactory(job_seeker=job_seeker) self.client.login(username=siae_member.email, password=DEFAULT_PASSWORD) with self.assertRaises(ObjectDoesNotExist): self.client.get( reverse("approvals:approval_as_pdf", kwargs={"job_application_id": job_application.pk}))
def test_approval_in_the_future(self): """ The search for PE approval screen should display that there is no results if a PE approval number was searched for but it is in the future """ today = timezone.now().date() pe_approval = PoleEmploiApprovalFactory(start_at=today + relativedelta(days=10)) job_application = JobApplicationWithApprovalFactory(state=JobApplicationWorkflow.STATE_ACCEPTED) siae_user = job_application.to_siae.members.first() self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD) response = self.client.get(self.url, {"number": pe_approval.number}) self.assertNotContains(response, "Continuer")
def test_download_job_app_approval_as_pdf(self, *args, **kwargs): job_application = JobApplicationWithApprovalFactory() siae_member = job_application.to_siae.members.first() job_seeker = job_application.job_seeker EligibilityDiagnosisFactory(job_seeker=job_seeker) self.client.login(username=siae_member.email, password=DEFAULT_PASSWORD) response = self.client.get( reverse("approvals:approval_as_pdf", kwargs={"job_application_id": job_application.pk})) self.assertEqual(response.status_code, 200) self.assertIn("pdf", response.get("Content-Type"))
def test_download_job_app_approval_as_pdf(self, *args, **kwargs): """ Given an existing job application with a PASS IAE and a diagnosis, when trying to download it as PDF, then it works. """ job_application = JobApplicationWithApprovalFactory() siae_member = job_application.to_siae.members.first() self.client.login(username=siae_member.email, password=DEFAULT_PASSWORD) response = self.client.get( reverse("approvals:approval_as_pdf", kwargs={"job_application_id": job_application.pk}) ) self.assertEqual(response.status_code, 200) self.assertIn("pdf", response.get("Content-Type"))
def test_update_suspension(self): """ Test the update of a suspension. """ today = timezone.localdate() job_application = JobApplicationWithApprovalFactory( state=JobApplicationWorkflow.STATE_ACCEPTED, # Ensure that the job_application cannot be canceled. hiring_start_at=today - relativedelta(days=1), ) approval = job_application.approval siae_user = job_application.to_siae.members.first() start_at = today end_at = today + relativedelta(days=10) suspension = SuspensionFactory(approval=approval, start_at=start_at, end_at=end_at, created_by=siae_user) self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD) back_url = "/" params = urlencode({"back_url": back_url}) url = reverse("approvals:suspension_update", kwargs={"suspension_id": suspension.pk}) url = f"{url}?{params}" response = self.client.get(url) self.assertEqual(response.status_code, 200) new_end_at = end_at + relativedelta(days=30) post_data = { "start_at": suspension.start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "end_at": new_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "reason": suspension.reason, "reason_explanation": suspension.reason_explanation, } response = self.client.post(url, data=post_data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, back_url) self.assertEqual(1, approval.suspension_set.count()) suspension = approval.suspension_set.first() self.assertEqual(suspension.updated_by, siae_user) self.assertEqual(suspension.end_at, new_end_at)
def test_cancellation_not_allowed(self): today = datetime.date.today() # Outside cancellation delay job_application = JobApplicationWithApprovalFactory( hiring_start_at=(today - relativedelta(days=365))) cancellation_user = job_application.to_siae.active_members.first() # xworkflows.base.AbortTransition with self.assertRaises(xwf_models.AbortTransition): job_application.cancel(user=cancellation_user) # Wrong state job_application = JobApplicationWithApprovalFactory( hiring_start_at=today, state=JobApplicationWorkflow.STATE_NEW) cancellation_user = job_application.to_siae.active_members.first() with self.assertRaises(xwf_models.AbortTransition): job_application.cancel(user=cancellation_user)
def set_up_pe_approval(self, with_job_application=True): # pylint: disable=attribute-defined-outside-init self.pe_approval = PoleEmploiApprovalFactory() self.siae = SiaeWithMembershipFactory() self.siae_user = self.siae.members.first() if with_job_application: self.job_application = JobApplicationWithApprovalFactory( to_siae=self.siae, state=JobApplicationWorkflow.STATE_ACCEPTED, approval__number=self.pe_approval.number, ) self.approval = self.job_application.approval self.job_seeker = self.job_application.job_seeker else: self.approval = None self.job_seeker = None
def test_download_approval_even_if_diagnosis_is_missing( self, *args, **kwargs): job_application = JobApplicationWithApprovalFactory() siae_member = job_application.to_siae.members.first() # An approval has been delivered but it does not come from Itou. # Therefore, the linked diagnosis exists but is not in our database. # Don't create a diagnosis. # EligibilityDiagnosisFactory(job_seeker=job_seeker) self.client.login(username=siae_member.email, password=DEFAULT_PASSWORD) response = self.client.get( reverse("approvals:approval_as_pdf", kwargs={"job_application_id": job_application.pk})) self.assertEqual(response.status_code, 200) self.assertIn("pdf", response.get("Content-Type"))
def test_cancel_delete_linked_approval(self): job_application = JobApplicationWithApprovalFactory() self.assertEqual(job_application.job_seeker.approvals.count(), 1) self.assertEqual( JobApplication.objects.filter( approval=job_application.approval).count(), 1) cancellation_user = job_application.to_siae.active_members.first() job_application.cancel(user=cancellation_user) self.assertEqual(job_application.state, JobApplicationWorkflow.STATE_CANCELLED) job_application.refresh_from_db() self.assertFalse(job_application.approval)
def test_download_approval_even_if_diagnosis_is_missing(self, *args, **kwargs): """ Given an existing job application with an approval delivered by Pôle emploi but no diagnosis, when trying to download it as PDF, then it works. """ # An approval has been delivered but it does not come from Itou. # Therefore, the linked diagnosis exists but is not in our database. job_application = JobApplicationWithApprovalFactory( eligibility_diagnosis=None, approval__number="625741810181" ) siae_member = job_application.to_siae.members.first() self.client.login(username=siae_member.email, password=DEFAULT_PASSWORD) response = self.client.get( reverse("approvals:approval_as_pdf", kwargs={"job_application_id": job_application.pk}) ) self.assertEqual(response.status_code, 200) self.assertIn("pdf", response.get("Content-Type"))
def test_when_pole_emploi_approval_has_already_been_imported(self): """ When the PoleEmploiApproval has already been imported, we are redirected to its page """ self.job_application = JobApplicationWithApprovalFactory( state=JobApplicationWorkflow.STATE_ACCEPTED, approval=ApprovalFactory(number=self.pe_approval.number[:12]) ) initial_approval_count = Approval.objects.count() job_seeker = JobSeekerFactory() self.client.login(username=self.siae_user.email, password=DEFAULT_PASSWORD) url = reverse("approvals:pe_approval_create", kwargs={"pe_approval_id": self.pe_approval.id}) params = {"email": job_seeker.email} response = self.client.post(url, params) self.assertEqual(response.status_code, 302) self.assertEqual(Approval.objects.count(), initial_approval_count) messages = list(get_messages(response.wsgi_request)) self.assertEqual(len(messages), 1) self.assertEqual(str(messages[0]), "Cet agrément a déjà été importé.")
def test_cancel_do_not_delete_linked_approval(self): # The approval is linked to two accepted job applications job_application = JobApplicationWithApprovalFactory() approval = job_application.approval JobApplicationWithApprovalFactory( approval=approval, job_seeker=job_application.job_seeker) self.assertEqual(job_application.job_seeker.approvals.count(), 1) self.assertEqual( JobApplication.objects.filter(approval=approval).count(), 2) cancellation_user = job_application.to_siae.active_members.first() job_application.cancel(user=cancellation_user) self.assertEqual(job_application.state, JobApplicationWorkflow.STATE_CANCELLED) job_application.refresh_from_db() self.assertTrue(job_application.approval)
def test_can_be_deleted(self): job_app = JobApplicationWithApprovalFactory(state=JobApplicationWorkflow.STATE_ACCEPTED) approval = job_app.approval self.assertTrue(approval.can_be_deleted) # An approval exists without a Job Application approval = ApprovalFactory() self.assertFalse(approval.can_be_deleted) job_app.state = JobApplicationWorkflow.STATE_REFUSED job_app.save() self.assertFalse(approval.can_be_deleted) JobApplicationWithApprovalFactory( state=JobApplicationWorkflow.STATE_ACCEPTED, job_seeker=job_app.job_seeker, approval=job_app.approval ) self.assertFalse(approval.can_be_deleted)
def test_delete_suspension(self): """ Test the deletion of a suspension. """ today = timezone.localdate() job_application = JobApplicationWithApprovalFactory( state=JobApplicationWorkflow.STATE_ACCEPTED, # Ensure that the job_application cannot be canceled. hiring_start_at=today - relativedelta(days=1), ) approval = job_application.approval siae_user = job_application.to_siae.members.first() start_at = today end_at = today + relativedelta(days=10) suspension = SuspensionFactory(approval=approval, start_at=start_at, end_at=end_at, created_by=siae_user) self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD) back_url = "/" params = urlencode({"back_url": back_url}) url = reverse("approvals:suspension_delete", kwargs={"suspension_id": suspension.pk}) url = f"{url}?{params}" response = self.client.get(url) self.assertEqual(response.status_code, 200) post_data = {"confirm": "true"} response = self.client.post(url, data=post_data) self.assertEqual(response.status_code, 302) self.assertRedirects(response, back_url) self.assertEqual(0, approval.suspension_set.count())