def test_happy_path(self): user = SiaeStaffFactory() # Create enough fake job applications so that the dummy endpoint returns the first 25 of them. JobApplicationFactory.create_batch(30) url = reverse("v1:token-auth") data = {"username": user.email, "password": DEFAULT_PASSWORD} response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, 200) token = response.json()["token"] url = reverse("v1:dummy-employee-records-list") self.client.credentials(HTTP_AUTHORIZATION=f"Token {token}") response = self.client.get(url, format="json") self.assertEqual(response.status_code, 200) # The dummy endpoint always returns 25 records, first page is 20 of them. self.assertEqual(response.json()["count"], 25) self.assertEqual(len(response.json()["results"]), 20) employee_record_json = response.json()["results"][0] self.assertIn("mesure", employee_record_json) self.assertIn("siret", employee_record_json) self.assertIn("numeroAnnexe", employee_record_json) self.assertIn("personnePhysique", employee_record_json) self.assertIn("passIae", employee_record_json["personnePhysique"]) self.assertIn("adresse", employee_record_json) self.assertIn("situationSalarie", employee_record_json)
def test_order_by(self): """ Check SIAE results sorting. Don't test sorting by active members to avoid creating too much data. """ guerande = create_city_guerande() created_siaes = [] # Several job descriptions but no job application. siae = SiaeWithJobsFactory(department="44", coords=guerande.coords, post_code="44350") created_siaes.append(siae) # Many job descriptions and job applications. siae = SiaeWithJobsFactory(department="44", coords=guerande.coords, post_code="44350") JobApplicationFactory(to_siae=siae) created_siaes.append(siae) # Many job descriptions and more job applications than the first one. siae = SiaeWithJobsFactory(department="44", coords=guerande.coords, post_code="44350") JobApplicationFactory(to_siae=siae) JobApplicationFactory(to_siae=siae) created_siaes.append(siae) # No job description and a job application siae = SiaeFactory(department="44", coords=guerande.coords, post_code="44350") JobApplicationFactory(to_siae=siae) created_siaes.append(siae) # No job description, no job application. siae = SiaeFactory(department="44", coords=guerande.coords, post_code="44350") created_siaes.append(siae) # Does not want to receive any job application. siae = SiaeFactory(department="44", coords=guerande.coords, post_code="44350", block_job_applications=True) created_siaes.append(siae) response = self.client.get(self.url, {"city": guerande.slug}) siaes_results = response.context["siaes_page"] for i, siae in enumerate(siaes_results): self.assertEqual(siae.pk, created_siaes[i].pk)
def test_eligibility_diagnosis_by_siae_required(self): job_application = JobApplicationFactory( state=JobApplicationWorkflow.STATE_PROCESSING, to_siae__kind=Siae.KIND_GEIQ ) self.assertFalse(job_application.job_seeker.has_eligibility_diagnosis) self.assertFalse(job_application.eligibility_diagnosis_by_siae_required) job_application = JobApplicationFactory( state=JobApplicationWorkflow.STATE_PROCESSING, to_siae__kind=Siae.KIND_EI ) self.assertFalse(job_application.job_seeker.has_eligibility_diagnosis) self.assertTrue(job_application.eligibility_diagnosis_by_siae_required)
def setUp(self): self.siae = SiaeWithMembershipFactory() self.job_application = JobApplicationFactory(to_siae=self.siae) self.notification = NewSpontaneousJobAppEmployersNotification( job_application=self.job_application) # Make sure notifications are empty self.siaemembership_set = self.siae.siaemembership_set self.membership = self.siaemembership_set.first() self.assertFalse(self.membership.notifications)
def test_mise_a_jour_pass_iae_success_with_approval_refused(self, mock_post): """ Nominal scenario: an approval is **refused** HTTP 200 + codeSortie = S001 is the only way mise_a_jour_pass_iae will return True""" job_application = JobApplicationFactory() result = mise_a_jour_pass_iae( job_application, POLE_EMPLOI_PASS_REFUSED, "some_valid_encrypted_identifier", "some_valid_token" ) mock_post.assert_called() self.assertTrue(result)
def test_refuse(self): user = JobSeekerFactory() kwargs = { "job_seeker": user, "sender": user, "sender_kind": JobApplication.SENDER_KIND_JOB_SEEKER, } JobApplicationFactory(state=JobApplicationWorkflow.STATE_PROCESSING, **kwargs) JobApplicationFactory(state=JobApplicationWorkflow.STATE_POSTPONED, **kwargs) self.assertEqual(user.job_applications.count(), 2) self.assertEqual(user.job_applications.pending().count(), 2) for job_application in user.job_applications.all(): job_application.refuse() # Check sent email. self.assertEqual(len(mail.outbox), 1) self.assertIn("Candidature déclinée", mail.outbox[0].subject) mail.outbox = []
def test_accept_job_application_sent_by_job_seeker(self): job_seeker = JobSeekerFactory() # A valid Pôle emploi ID should trigger an automatic approval delivery. self.assertNotEqual(job_seeker.pole_emploi_id, "") kwargs = { "job_seeker": job_seeker, "sender": job_seeker, "sender_kind": JobApplication.SENDER_KIND_JOB_SEEKER, } JobApplicationFactory(state=JobApplicationWorkflow.STATE_NEW, **kwargs) JobApplicationFactory(state=JobApplicationWorkflow.STATE_PROCESSING, **kwargs) JobApplicationFactory(state=JobApplicationWorkflow.STATE_POSTPONED, **kwargs) JobApplicationFactory(state=JobApplicationWorkflow.STATE_PROCESSING, **kwargs) self.assertEqual(job_seeker.job_applications.count(), 4) self.assertEqual(job_seeker.job_applications.pending().count(), 4) job_application = job_seeker.job_applications.filter( state=JobApplicationWorkflow.STATE_PROCESSING ).first() job_application.accept(user=job_application.to_siae.members.first()) self.assertEqual( job_seeker.job_applications.filter( state=JobApplicationWorkflow.STATE_ACCEPTED ).count(), 1, ) self.assertEqual( job_seeker.job_applications.filter( state=JobApplicationWorkflow.STATE_OBSOLETE ).count(), 3, ) # Check sent email. self.assertEqual(len(mail.outbox), 2) self.assertIn("Candidature acceptée", mail.outbox[0].subject) self.assertIn("Délivrance d'un PASS IAE pour", mail.outbox[1].subject)
def test_accept(self): user = JobSeekerFactory() kwargs = { "job_seeker": user, "sender": user, "sender_kind": JobApplication.SENDER_KIND_JOB_SEEKER, } JobApplicationFactory(state=JobApplicationWorkflow.STATE_NEW, **kwargs) JobApplicationFactory(state=JobApplicationWorkflow.STATE_PROCESSING, **kwargs) JobApplicationFactory(state=JobApplicationWorkflow.STATE_POSTPONED, **kwargs) JobApplicationFactory(state=JobApplicationWorkflow.STATE_PROCESSING, **kwargs) self.assertEqual(user.job_applications.count(), 4) self.assertEqual(user.job_applications.pending().count(), 4) job_application = user.job_applications.filter( state=JobApplicationWorkflow.STATE_PROCESSING ).first() job_application.accept(user=job_application.to_siae.members.first()) self.assertEqual( user.job_applications.filter( state=JobApplicationWorkflow.STATE_ACCEPTED ).count(), 1, ) self.assertEqual( user.job_applications.filter( state=JobApplicationWorkflow.STATE_OBSOLETE ).count(), 3, ) # Check sent email. self.assertEqual(len(mail.outbox), 2) self.assertIn("Candidature acceptée", mail.outbox[0].subject) self.assertIn("Numéro d'agrément requis sur Itou", mail.outbox[1].subject)
def test_impossible_download_when_approval_is_missing(self, *args, **kwargs): """ Given an existing job application without a PASS IAE, when trying to download it as PDF, then a 404 should be raised. """ job_application = JobApplicationFactory() 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}) ) # `can_download_approval_as_pdf` should fail and trigger a 404. self.assertEqual(response.status_code, 404)
def test_impossible_download_when_approval_is_missing( self, *args, **kwargs): """ The button to download an approval is show only when certain conditions are met. Nevertheless, don't trust the client. Make sure we raise an error if the same conditions are not met in this view. """ # Create a job application without an approval. job_application = JobApplicationFactory() 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, 404)
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_job_application_factory(self): create_test_romes_and_appellations(["M1805"], appellations_per_rome=2) job_application = JobApplicationFactory(selected_jobs=Appellation.objects.all()) self.assertEqual(job_application.selected_jobs.count(), 2)
def test_import_data_into_itou(self): developer = UserFactory( email=settings.AI_EMPLOYEES_STOCK_DEVELOPER_EMAIL) CommuneFactory(code=getattr(CleanedAiCsvFileMock, CITY_INSEE_COL)) command = self.command base_data = CleanedAiCsvFileMock() siae = SiaeFactory(siret=getattr(base_data, SIRET_COL), kind=Siae.KIND_AI) # User, approval and job application creation. input_df = pandas.DataFrame([base_data]) output_df = command.import_data_into_itou(df=input_df, to_be_imported_df=input_df) self.assertEqual(User.objects.count(), 2) self.assertEqual(Approval.objects.count(), 1) self.assertEqual(JobApplication.objects.count(), 1) job_seeker = User.objects.filter(is_job_seeker=True).get() self.assertEqual(job_seeker.job_applications.count(), 1) self.assertEqual(job_seeker.approvals.count(), 1) job_seeker.delete() # User, approval and job application retrieval. job_seeker = JobSeekerFactory(nir=getattr(base_data, NIR_COL)) ApprovalFactory(user=job_seeker) JobApplicationFactory( sender_kind=JobApplication.SENDER_KIND_SIAE_STAFF, sender_siae=siae, to_siae=siae, created_at=settings.AI_EMPLOYEES_STOCK_IMPORT_DATE, approval_manually_delivered_by=developer, approval_delivery_mode=JobApplication. APPROVAL_DELIVERY_MODE_MANUAL, job_seeker=job_seeker, hiring_start_at=getattr(base_data, CONTRACT_STARTDATE_COL), ) input_df = pandas.DataFrame([CleanedAiCsvFileMock()]) output_df = command.import_data_into_itou(df=input_df, to_be_imported_df=input_df) self.assertEqual(User.objects.filter(is_job_seeker=True).count(), 1) self.assertEqual(Approval.objects.count(), 1) self.assertEqual(JobApplication.objects.count(), 1) job_seeker.delete() # Only values to be imported are imported but the whole input data frame # is updated for logging purposes. input_df = pandas.DataFrame([ CleanedAiCsvFileMock(**{CONTRACT_ENDDATE_COL: "2020-05-11" }), # Ended contracts are ignored. CleanedAiCsvFileMock( **{SIRET_COL: "598742121322354"}), # Not existing SIAE. CleanedAiCsvFileMock( **{ NIR_COL: "141062a78200555", EMAIL_COL: "*****@*****.**", BIRTHDATE_COL: datetime.date(1997, 3, 12), }), # Different contract start date. CleanedAiCsvFileMock( **{CONTRACT_STARTDATE_COL: datetime.date(2020, 4, 12)}), CleanedAiCsvFileMock(), ]) input_df = command.add_columns_for_asp(input_df) input_df, to_be_imported_df = command.remove_ignored_rows(input_df) output_df = command.import_data_into_itou( df=input_df, to_be_imported_df=to_be_imported_df) self.assertEqual(User.objects.count(), 3) self.assertEqual(Approval.objects.count(), 2) self.assertEqual(JobApplication.objects.count(), 3) job_seeker = User.objects.get(email=getattr(base_data, EMAIL_COL)) self.assertEqual(job_seeker.job_applications.count(), 2) self.assertEqual(job_seeker.approvals.count(), 1) # Different contract start date. job_seeker = User.objects.get(email="*****@*****.**") self.assertEqual(job_seeker.job_applications.count(), 1) self.assertEqual(job_seeker.approvals.count(), 1) # Ignored rows. for _, row in output_df[:2].iterrows(): self.assertTrue(row[COMMENTS_COL]) self.assertFalse(row[PASS_IAE_NUMBER_COL]) self.assertFalse(row[USER_PK_COL]) for _, row in output_df[2:].iterrows(): job_seeker = User.objects.get(nir=row[NIR_COL]) approval = job_seeker.approvals.first() self.assertEqual(row[PASS_IAE_NUMBER_COL], approval.number) self.assertEqual(row[PASS_IAE_START_DATE_COL], approval.start_at.strftime(DATE_FORMAT)) self.assertEqual(row[PASS_IAE_END_DATE_COL], approval.end_at.strftime(DATE_FORMAT)) self.assertEqual(row[USER_PK_COL], job_seeker.jobseeker_hash_id) self.assertEqual(row[USER_ITOU_EMAIL_COL], job_seeker.email) # Clean job_seeker = User.objects.get(nir=getattr(base_data, NIR_COL)) job_seeker.delete() job_seeker = User.objects.get(email="*****@*****.**") job_seeker.delete() # If transaction: raise and pass. job_seeker = JobSeekerFactory( nir=getattr(CleanedAiCsvFileMock(), NIR_COL)) future_date = datetime.date.today() + relativedelta(months=2) ApprovalFactory(user=job_seeker, start_at=future_date) input_df = pandas.DataFrame([ base_data, CleanedAiCsvFileMock( **{ NIR_COL: "141062a78200555", EMAIL_COL: "*****@*****.**", BIRTHDATE_COL: datetime.date(1997, 3, 12), }), ]) output_df = None output_df = command.import_data_into_itou(df=input_df, to_be_imported_df=input_df) self.assertEqual(len(output_df), 2)
def test_find_or_create_job_application__create(self): developer = UserFactory( email=settings.AI_EMPLOYEES_STOCK_DEVELOPER_EMAIL) command = self.command # Employers canceled the job application we created, hence removing the PASS IAE we delivered. # Remove those job applications and deliver a new PASS IAE. nir = getattr(CleanedAiCsvFileMock(), NIR_COL) siret = getattr(CleanedAiCsvFileMock(), SIRET_COL) approval = ApprovalFactory(user__nir=nir) df = pandas.DataFrame([CleanedAiCsvFileMock(**{SIRET_COL: siret})]) job_application = JobApplicationSentBySiaeFactory( to_siae__kind=Siae.KIND_AI, to_siae__siret=siret, state=JobApplicationWorkflow.STATE_CANCELLED, job_seeker=approval.user, approval_delivery_mode=JobApplication. APPROVAL_DELIVERY_MODE_MANUAL, approval=None, approval_manually_delivered_by=developer, created_at=settings.AI_EMPLOYEES_STOCK_IMPORT_DATE, hiring_start_at=df.iloc[0][CONTRACT_STARTDATE_COL], ) created, new_job_application, cancelled_job_app_deleted = command.find_or_create_job_application( approval=approval, job_seeker=job_application.job_seeker, row=df.iloc[0], approval_manually_delivered_by=developer, ) self.assertTrue(created) self.assertTrue(cancelled_job_app_deleted) # Assert employee records creation is blocked. self.assertNotIn( new_job_application, JobApplication.objects.eligible_as_employee_record( new_job_application.to_siae)) self.assertEqual(new_job_application.approval.pk, approval.pk) self.assertFalse( JobApplication.objects.filter(pk=job_application.pk).exists()) self.assertFalse(new_job_application.can_be_cancelled) self.assertTrue(new_job_application.is_from_ai_stock) self.assertEqual(JobApplication.objects.count(), 1) job_application.job_seeker.delete() # Different contract starting date. nir = getattr(CleanedAiCsvFileMock(), NIR_COL) approval = ApprovalFactory(user__nir=nir) siae = SiaeFactory(kind=Siae.KIND_AI) job_application = JobApplicationFactory( to_siae=siae, sender_siae=siae, job_seeker=approval.user, approval=approval, state=JobApplicationWorkflow.STATE_ACCEPTED, approval_delivery_mode=JobApplication. APPROVAL_DELIVERY_MODE_MANUAL, approval_manually_delivered_by=developer, created_at=settings.AI_EMPLOYEES_STOCK_IMPORT_DATE, hiring_start_at=datetime.date(2021, 1, 1), ) df = pandas.DataFrame( [CleanedAiCsvFileMock(**{SIRET_COL: siae.siret})]) created, new_job_application, cancelled_job_app_deleted = command.find_or_create_job_application( approval=job_application.approval, job_seeker=job_application.job_seeker, row=df.iloc[0], approval_manually_delivered_by=developer, ) self.assertTrue(created) self.assertNotEqual(job_application.pk, new_job_application.pk) self.assertFalse(cancelled_job_app_deleted) job_application.job_seeker.delete()