def test_accept_after_cancel(self, *args, **kwargs): # A canceled job application is not linked to an approval # unless the job seeker has an accepted job application. create_test_cities(["54", "57"], num_per_department=2) city = City.objects.first() job_seeker = JobSeekerWithAddressFactory(city=city.name) job_application = JobApplicationSentByJobSeekerFactory( state=JobApplicationWorkflow.STATE_CANCELLED, job_seeker=job_seeker ) siae_user = job_application.to_siae.members.first() self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD) url_accept = reverse("apply:accept", kwargs={"job_application_id": job_application.pk}) hiring_start_at = timezone.localdate() hiring_end_at = Approval.get_default_end_date(hiring_start_at) post_data = { "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "pole_emploi_id": job_application.job_seeker.pole_emploi_id, "answer": "", "address_line_1": job_seeker.address_line_1, "post_code": job_seeker.post_code, "city": city.name, "city_slug": city.slug, } response = self.client.post(url_accept, 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.assertEqual(job_seeker.approvals.count(), 1) approval = job_seeker.approvals.first() self.assertEqual(approval.start_at, job_application.hiring_start_at) self.assertTrue(job_application.state.is_accepted)
def test_fecth_city(self): """ The city name should be fetched from the slug. """ create_test_cities(["67"], num_per_department=1) city = City.objects.first() form_data = { "city_slug": city.slug, "city": "", "address_line_1": "11 rue des pixels cassés", "address_line_2": "", "post_code": "67000", } form = OptionalAddressFormMixin(data=form_data) with self.assertNumQueries(1): self.assertTrue(form.is_valid()) expected_cleaned_data = { "city_slug": city.slug, "city": city.name, "address_line_1": form_data["address_line_1"], "address_line_2": form_data["address_line_2"], "post_code": form_data["post_code"], } self.assertDictEqual(form.cleaned_data, expected_cleaned_data)
def test_accept_with_manual_approval_delivery(self, *args, **kwargs): """ Test the "manual approval delivery mode" path of the view. """ create_test_cities(["57"], num_per_department=1) city = City.objects.first() job_application = JobApplicationSentByJobSeekerFactory( state=JobApplicationWorkflow.STATE_PROCESSING, # The state of the 2 `pole_emploi_*` fields will trigger a manual delivery. job_seeker__pole_emploi_id="", job_seeker__lack_of_pole_emploi_id_reason=User.REASON_FORGOTTEN, ) siae_user = job_application.to_siae.members.first() self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD) url = reverse("apply:accept", kwargs={"job_application_id": job_application.pk}) response = self.client.get(url) self.assertEqual(response.status_code, 200) post_data = { # Data for `JobSeekerPoleEmploiStatusForm`. "pole_emploi_id": job_application.job_seeker.pole_emploi_id, "lack_of_pole_emploi_id_reason": job_application.job_seeker.lack_of_pole_emploi_id_reason, # Data for `UserAddressForm`. "address_line_1": "11 rue des Lilas", "post_code": "57000", "city": city.name, "city_slug": city.slug, # Data for `AcceptForm`. "hiring_start_at": timezone.localdate().strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "hiring_end_at": (timezone.localdate() + relativedelta(days=360)).strftime( DuetDatePickerWidget.INPUT_DATE_FORMAT ), "answer": "", } 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.assertEqual(job_application.approval_delivery_mode, job_application.APPROVAL_DELIVERY_MODE_MANUAL)
def test_autocomplete(self): create_test_cities(["67"], num_per_department=10) url = reverse("autocomplete:cities") response = self.client.get(url, {"term": "alte"}) self.assertEqual(response.status_code, 200) expected = [ { "value": "Altenheim (67)", "slug": "altenheim-67" }, { "value": "Altorf (67)", "slug": "altorf-67" }, { "value": "Alteckendorf (67)", "slug": "alteckendorf-67" }, { "value": "Albé (67)", "slug": "albe-67" }, { "value": "Altwiller (67)", "slug": "altwiller-67" }, ] self.assertEqual(json.loads(response.content), expected) response = self.client.get(url, {"term": " "}) self.assertEqual(response.status_code, 200) expected = b"[]" self.assertEqual(response.content, expected) response = self.client.get(url, {"term": "paris"}) self.assertEqual(response.status_code, 200) expected = b"[]" self.assertEqual(response.content, expected)
def test_with_instance(self): """ If an instance is passed, `city` and `city_slug` should be prepopulated. """ create_test_cities(["57"], num_per_department=1) city = City.objects.first() user = JobSeekerFactory() user.address_line_1 = "11 rue des pixels cassés" user.department = city.department user.post_code = city.post_codes[0] user.city = city.name with self.assertNumQueries(1): form = DummyUserModelForm(data={}, instance=user) self.assertEqual(form.initial["city_slug"], city.slug) self.assertEqual(form.initial["city"], city.name)
def setUp(self): create_test_cities(["57"], num_per_department=1) self.city = City.objects.first()
def setUp(self): create_test_cities(["67"], num_per_department=10)
def test_create_test_cities(self): create_test_cities(["62", "67", "93"], num_per_department=10) self.assertEqual(City.objects.count(), 30) self.assertEqual(City.objects.filter(department="62").count(), 10) self.assertEqual(City.objects.filter(department="67").count(), 10) self.assertEqual(City.objects.filter(department="93").count(), 10)
def test_accept_and_update_hiring_start_date_of_two_job_applications(self, *args, **kwargs): create_test_cities(["54", "57"], num_per_department=2) city = City.objects.first() job_seeker = JobSeekerWithAddressFactory() base_for_post_data = { "address_line_1": job_seeker.address_line_1, "post_code": job_seeker.post_code, "city": city.name, "city_slug": city.slug, "pole_emploi_id": job_seeker.pole_emploi_id, "answer": "", } hiring_start_at = timezone.localdate() + relativedelta(months=2) hiring_end_at = hiring_start_at + relativedelta(months=2) approval_default_ending = Approval.get_default_end_date(start_at=hiring_start_at) # Send 3 job applications to 3 different structures job_application = JobApplicationSentByJobSeekerFactory( job_seeker=job_seeker, state=JobApplicationWorkflow.STATE_PROCESSING ) job_app_starting_earlier = JobApplicationSentByJobSeekerFactory( job_seeker=job_seeker, state=JobApplicationWorkflow.STATE_PROCESSING ) job_app_starting_later = JobApplicationSentByJobSeekerFactory( job_seeker=job_seeker, state=JobApplicationWorkflow.STATE_PROCESSING ) # SIAE 1 logs in and accepts the first job application. # The delivered approval should start at the same time as the contract. user = job_application.to_siae.members.first() self.client.login(username=user.email, password=DEFAULT_PASSWORD) url_accept = reverse("apply:accept", kwargs={"job_application_id": job_application.pk}) post_data = { "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), **base_for_post_data, } response = self.client.post(url_accept, data=post_data) self.assertEqual(response.status_code, 302) # First job application has been accepted. # All other job applications are obsolete. job_application.refresh_from_db() self.assertTrue(job_application.state.is_accepted) self.assertEqual(job_application.approval.start_at, job_application.hiring_start_at) self.assertEqual(job_application.approval.end_at, approval_default_ending) self.client.logout() # SIAE 2 accepts the second job application # but its contract starts earlier than the approval delivered the first time. # Approval's starting date should be brought forward. user = job_app_starting_earlier.to_siae.members.first() hiring_start_at = hiring_start_at - relativedelta(months=1) hiring_end_at = hiring_start_at + relativedelta(months=2) approval_default_ending = Approval.get_default_end_date(start_at=hiring_start_at) job_app_starting_earlier.refresh_from_db() self.assertTrue(job_app_starting_earlier.state.is_obsolete) self.client.login(username=user.email, password=DEFAULT_PASSWORD) url_accept = reverse("apply:accept", kwargs={"job_application_id": job_app_starting_earlier.pk}) post_data = { "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), **base_for_post_data, } response = self.client.post(url_accept, data=post_data) job_app_starting_earlier.refresh_from_db() # Second job application has been accepted. # The job seeker has now two part-time jobs at the same time. self.assertEqual(response.status_code, 302) self.assertTrue(job_app_starting_earlier.state.is_accepted) self.assertEqual(job_app_starting_earlier.approval.start_at, job_app_starting_earlier.hiring_start_at) self.assertEqual(job_app_starting_earlier.approval.end_at, approval_default_ending) self.client.logout() # SIAE 3 accepts the third job application. # Its contract starts later than the corresponding approval. # Approval's starting date should not be updated. user = job_app_starting_later.to_siae.members.first() hiring_start_at = hiring_start_at + relativedelta(months=6) hiring_end_at = hiring_start_at + relativedelta(months=2) job_app_starting_later.refresh_from_db() self.assertTrue(job_app_starting_later.state.is_obsolete) self.client.login(username=user.email, password=DEFAULT_PASSWORD) url_accept = reverse("apply:accept", kwargs={"job_application_id": job_app_starting_later.pk}) post_data = { "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), **base_for_post_data, } response = self.client.post(url_accept, data=post_data) job_app_starting_later.refresh_from_db() # Third job application has been accepted. # The job seeker has now three part-time jobs at the same time. self.assertEqual(response.status_code, 302) self.assertTrue(job_app_starting_later.state.is_accepted) self.assertEqual(job_app_starting_later.approval.start_at, job_app_starting_earlier.hiring_start_at)
def test_accept_with_active_suspension(self, *args, **kwargs): """Test the `accept` transition with active suspension for active user""" create_test_cities(["54", "57"], num_per_department=2) city = City.objects.first() today = timezone.localdate() # the old job of job seeker job_seeker_user = JobSeekerWithAddressFactory() old_job_application = JobApplicationWithApprovalFactory( state=JobApplicationWorkflow.STATE_ACCEPTED, job_seeker=job_seeker_user, # Ensure that the old_job_application cannot be canceled. hiring_start_at=today - relativedelta(days=100), ) # create suspension for the job seeker approval_job_seeker = old_job_application.approval siae_user = old_job_application.to_siae.members.first() susension_start_at = today suspension_end_at = today + relativedelta(days=50) SuspensionFactory( approval=approval_job_seeker, start_at=susension_start_at, end_at=suspension_end_at, created_by=siae_user, reason=Suspension.Reason.BROKEN_CONTRACT.value, ) # Now, another Siae wants to hire the job seeker other_siae = SiaeWithMembershipFactory() job_application = JobApplicationSentByJobSeekerFactory( approval=approval_job_seeker, state=JobApplicationWorkflow.STATE_PROCESSING, job_seeker=job_seeker_user, to_siae=other_siae, ) other_siae_user = job_application.to_siae.members.first() # login with other siae self.client.login(username=other_siae_user.email, password=DEFAULT_PASSWORD) url = reverse("apply:accept", kwargs={"job_application_id": job_application.pk}) hiring_start_at = today + relativedelta(days=20) hiring_end_at = Approval.get_default_end_date(hiring_start_at) post_data = { # Data for `JobSeekerPoleEmploiStatusForm`. "pole_emploi_id": job_application.job_seeker.pole_emploi_id, # Data for `AcceptForm`. "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "answer": "", "address_line_1": job_seeker_user.address_line_1, "post_code": job_seeker_user.post_code, "city": city.name, "city_slug": city.slug, } response = self.client.post(url, data=post_data) self.assertEqual(response.status_code, 302) get_job_application = JobApplication.objects.get(pk=job_application.pk) g_suspension = get_job_application.approval.suspension_set.in_progress().last() # The end date of suspension is set to d-1 of hiring start day self.assertEqual(g_suspension.end_at, get_job_application.hiring_start_at - relativedelta(days=1)) # Check if the duration of approval was updated correctly self.assertEqual( get_job_application.approval.end_at, approval_job_seeker.end_at + relativedelta(days=(g_suspension.end_at - g_suspension.start_at).days), )
def test_accept(self, *args, **kwargs): """Test the `accept` transition.""" create_test_cities(["54", "57"], num_per_department=2) city = City.objects.first() today = timezone.localdate() job_seeker = JobSeekerWithAddressFactory(city=city.name) address = { "address_line_1": job_seeker.address_line_1, "post_code": job_seeker.post_code, "city": city.name, "city_slug": city.slug, } siae = SiaeWithMembershipFactory() siae_user = siae.members.first() for state in JobApplicationWorkflow.CAN_BE_ACCEPTED_STATES: job_application = JobApplicationSentByJobSeekerFactory(state=state, job_seeker=job_seeker, to_siae=siae) self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD) url = reverse("apply:accept", kwargs={"job_application_id": job_application.pk}) response = self.client.get(url) self.assertEqual(response.status_code, 200) # Good duration. hiring_start_at = today hiring_end_at = Approval.get_default_end_date(hiring_start_at) post_data = { # Data for `JobSeekerPoleEmploiStatusForm`. "pole_emploi_id": job_application.job_seeker.pole_emploi_id, # Data for `AcceptForm`. "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "answer": "", **address, } 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 = JobApplication.objects.get(pk=job_application.pk) self.assertEqual(job_application.hiring_start_at, hiring_start_at) self.assertEqual(job_application.hiring_end_at, hiring_end_at) self.assertTrue(job_application.state.is_accepted) ############## # Exceptions # ############## job_application = JobApplicationSentByJobSeekerFactory(state=state, job_seeker=job_seeker, to_siae=siae) url = reverse("apply:accept", kwargs={"job_application_id": job_application.pk}) # Wrong dates. hiring_start_at = today hiring_end_at = Approval.get_default_end_date(hiring_start_at) # Force `hiring_start_at` in past. hiring_start_at = hiring_start_at - relativedelta(days=1) post_data = { "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "answer": "", **address, } response = self.client.post(url, data=post_data) self.assertFormError(response, "form_accept", "hiring_start_at", JobApplication.ERROR_START_IN_PAST) # Wrong dates: end < start. hiring_start_at = today hiring_end_at = hiring_start_at - relativedelta(days=1) post_data = { "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "answer": "", **address, } response = self.client.post(url, data=post_data) self.assertFormError(response, "form_accept", None, JobApplication.ERROR_END_IS_BEFORE_START) # No address provided. job_application = JobApplicationSentByJobSeekerFactory( state=JobApplicationWorkflow.STATE_PROCESSING, to_siae=siae ) url = reverse("apply:accept", kwargs={"job_application_id": job_application.pk}) hiring_start_at = today hiring_end_at = Approval.get_default_end_date(hiring_start_at) post_data = { # Data for `JobSeekerPoleEmploiStatusForm`. "pole_emploi_id": job_application.job_seeker.pole_emploi_id, # Data for `AcceptForm`. "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT), "answer": "", } response = self.client.post(url, data=post_data) self.assertFormError(response, "form_user_address", "address_line_1", "Ce champ est obligatoire.") self.assertFormError(response, "form_user_address", "city", "Ce champ est obligatoire.") self.assertFormError(response, "form_user_address", "post_code", "Ce champ est obligatoire.")
def test_accept(self): """Test the `accept` transition.""" create_test_cities(["54", "57"], num_per_department=2) city = City.objects.first() job_seeker = JobSeekerWithAddressFactory(city=city.name) address = { "address_line_1": job_seeker.address_line_1, "post_code": job_seeker.post_code, "city_name": city.name, "city": city.slug, } job_application = JobApplicationSentByAuthorizedPrescriberOrganizationFactory( state=JobApplicationWorkflow.STATE_PROCESSING, job_seeker=job_seeker) self.assertTrue(job_application.state.is_processing) siae_user = job_application.to_siae.members.first() self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD) url = reverse("apply:accept", kwargs={"job_application_id": job_application.pk}) response = self.client.get(url) self.assertEqual(response.status_code, 200) # Wrong dates: force `hiring_start_at` in past. hiring_start_at = datetime.date.today() - relativedelta(days=1) hiring_end_at = hiring_start_at + relativedelta(years=2) post_data = { "hiring_start_at": hiring_start_at.strftime("%d/%m/%Y"), "hiring_end_at": hiring_end_at.strftime("%d/%m/%Y"), "answer": "", **address, } response = self.client.post(url, data=post_data) self.assertFormError(response, "form_accept", "hiring_start_at", JobApplication.ERROR_START_IN_PAST) # Wrong dates: end < start. hiring_start_at = datetime.date.today() hiring_end_at = hiring_start_at - relativedelta(days=1) post_data = { "hiring_start_at": hiring_start_at.strftime("%d/%m/%Y"), "hiring_end_at": hiring_end_at.strftime("%d/%m/%Y"), "answer": "", **address, } response = self.client.post(url, data=post_data) self.assertFormError(response, "form_accept", None, JobApplication.ERROR_END_IS_BEFORE_START) # Duration too long. hiring_start_at = datetime.date.today() hiring_end_at = hiring_start_at + relativedelta(years=2, days=1) post_data = { "hiring_start_at": hiring_start_at.strftime("%d/%m/%Y"), "hiring_end_at": hiring_end_at.strftime("%d/%m/%Y"), "answer": "", **address, } response = self.client.post(url, data=post_data) self.assertFormError(response, "form_accept", None, JobApplication.ERROR_DURATION_TOO_LONG) # Good duration. hiring_start_at = datetime.date.today() hiring_end_at = hiring_start_at + relativedelta(years=2) post_data = { # Data for `JobSeekerPoleEmploiStatusForm`. "pole_emploi_id": job_application.job_seeker.pole_emploi_id, # Data for `AcceptForm`. "hiring_start_at": hiring_start_at.strftime("%d/%m/%Y"), "hiring_end_at": hiring_end_at.strftime("%d/%m/%Y"), "answer": "", **address, } response = self.client.post(url, data=post_data) self.assertEqual(response.status_code, 302) next_url = reverse("apply:details_for_siae", kwargs={"job_application_id": job_application.pk}) self.assertEqual(response.url, next_url) job_application = JobApplication.objects.get(pk=job_application.pk) self.assertEqual(job_application.hiring_start_at, hiring_start_at) self.assertEqual(job_application.hiring_end_at, hiring_end_at) self.assertTrue(job_application.state.is_accepted) # No address provided job_application = JobApplicationSentByAuthorizedPrescriberOrganizationFactory( state=JobApplicationWorkflow.STATE_PROCESSING) hiring_start_at = datetime.date.today() hiring_end_at = hiring_start_at + relativedelta(years=2) post_data = { # Data for `JobSeekerPoleEmploiStatusForm`. "pole_emploi_id": job_application.job_seeker.pole_emploi_id, # Data for `AcceptForm`. "hiring_start_at": hiring_start_at.strftime("%d/%m/%Y"), "hiring_end_at": hiring_end_at.strftime("%d/%m/%Y"), "answer": "", } with self.assertRaises(KeyError): response = self.client.post(url, data=post_data)