diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/adapter/ApplicationStatusAdapter.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/adapter/ApplicationStatusAdapter.java deleted file mode 100644 index 4fe6be82..00000000 --- a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/adapter/ApplicationStatusAdapter.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.dynonuggets.refonteimplicaction.adapter; - -import com.dynonuggets.refonteimplicaction.dto.ApplicationStatusDto; -import com.dynonuggets.refonteimplicaction.model.Status; - -public class ApplicationStatusAdapter { - - public ApplicationStatusDto toDto(Status model) { - return ApplicationStatusDto.builder() - .id(model.getId()) - .label(model.getLabel()) - .build(); - } -} diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/adapter/JobApplicationAdapter.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/adapter/JobApplicationAdapter.java new file mode 100644 index 00000000..95c79973 --- /dev/null +++ b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/adapter/JobApplicationAdapter.java @@ -0,0 +1,27 @@ +package com.dynonuggets.refonteimplicaction.adapter; + +import com.dynonuggets.refonteimplicaction.dto.JobApplicationDto; +import com.dynonuggets.refonteimplicaction.model.JobApplication; +import com.dynonuggets.refonteimplicaction.model.JobPosting; +import org.springframework.stereotype.Component; + +@Component +public class JobApplicationAdapter { + + public JobApplicationDto toDto(JobApplication model) { + final JobPosting job = model.getJob(); + final boolean hasCompany = job.getCompany() != null; + final String companyName = hasCompany ? job.getCompany().getName() : null; + final String companyImageUrl = hasCompany ? job.getCompany().getLogo() : null; + + return JobApplicationDto.builder() + .id(model.getId()) + .jobId(job.getId()) + .jobTitle(job.getTitle()) + .companyName(companyName) + .companyImageUri(companyImageUrl) + .status(model.getStatus()) + .contractType(job.getContractType().getCode()) + .build(); + } +} diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/adapter/StatusAdapter.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/adapter/StatusAdapter.java deleted file mode 100644 index 3837b20b..00000000 --- a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/adapter/StatusAdapter.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.dynonuggets.refonteimplicaction.adapter; - -import com.dynonuggets.refonteimplicaction.dto.StatusDto; -import com.dynonuggets.refonteimplicaction.model.Status; -import lombok.AllArgsConstructor; -import org.springframework.stereotype.Component; - - -@Component -@AllArgsConstructor -public class StatusAdapter { - - public StatusDto toDto(Status model) { - return StatusDto.builder() - .id(model.getId()) - .label(model.getLabel()) - .type(model.getType()) - .build(); - } - - public Status toModel(StatusDto dto) { - return Status.builder() - .id(dto.getId()) - .label(dto.getLabel()) - .type(dto.getType()) - .build(); - } -} diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/controller/JobApplicationController.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/controller/JobApplicationController.java new file mode 100644 index 00000000..33f22fcf --- /dev/null +++ b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/controller/JobApplicationController.java @@ -0,0 +1,36 @@ +package com.dynonuggets.refonteimplicaction.controller; + +import com.dynonuggets.refonteimplicaction.dto.ApplicationRequest; +import com.dynonuggets.refonteimplicaction.dto.JobApplicationDto; +import com.dynonuggets.refonteimplicaction.exception.ImplicactionException; +import com.dynonuggets.refonteimplicaction.service.JobApplicationService; +import lombok.AllArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; + +import static com.dynonuggets.refonteimplicaction.utils.ApiUrls.APPLY_BASE_URI; +import static com.dynonuggets.refonteimplicaction.utils.ApiUrls.GET_APPLY_URI; + +@RestController +@RequestMapping(APPLY_BASE_URI) +@AllArgsConstructor +public class JobApplicationController { + + private final JobApplicationService applyService; + + @PostMapping + public ResponseEntity createApply(@RequestBody ApplicationRequest requestDto) throws ImplicactionException { + final JobApplicationDto saveDto = applyService.createApplyIfNotExists(requestDto); + URI location = UriComponentsBuilder.fromPath(APPLY_BASE_URI + GET_APPLY_URI) + .buildAndExpand(saveDto.getId()) + .toUri(); + + return ResponseEntity.created(location).body(saveDto); + } +} diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/StatusDto.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/ApplicationRequest.java similarity index 50% rename from backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/StatusDto.java rename to backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/ApplicationRequest.java index 97407186..7df2f6e4 100644 --- a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/StatusDto.java +++ b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/ApplicationRequest.java @@ -1,16 +1,14 @@ package com.dynonuggets.refonteimplicaction.dto; +import com.dynonuggets.refonteimplicaction.model.ApplyStatusEnum; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.Getter; @Data -@Builder @Getter @AllArgsConstructor -public class StatusDto { - private Long id; - private String label; - private String type; +public class ApplicationRequest { + private Long jobId; + private ApplyStatusEnum status; } diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/FakeDto.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/FakeDto.java deleted file mode 100644 index 3dfec211..00000000 --- a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/FakeDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.dynonuggets.refonteimplicaction.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@AllArgsConstructor -@NoArgsConstructor -@Builder -@Getter -public class FakeDto { - private String username; -} diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/JobApplicationDto.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/JobApplicationDto.java index 34591149..28d80650 100644 --- a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/JobApplicationDto.java +++ b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/dto/JobApplicationDto.java @@ -1,5 +1,6 @@ package com.dynonuggets.refonteimplicaction.dto; +import com.dynonuggets.refonteimplicaction.model.ApplyStatusEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -8,13 +9,19 @@ @Builder @AllArgsConstructor public class JobApplicationDto { - + private Long id; - private JobPostingDto jobPosting; + private Long jobId; + + private String jobTitle; + + private String companyName; + + private String companyImageUri; - private UserDto user; + private ApplyStatusEnum status; - private ApplicationStatusDto status; + private String contractType; } diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/model/ApplyStatusEnum.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/model/ApplyStatusEnum.java new file mode 100644 index 00000000..b58d5558 --- /dev/null +++ b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/model/ApplyStatusEnum.java @@ -0,0 +1,5 @@ +package com.dynonuggets.refonteimplicaction.model; + +public enum ApplyStatusEnum { + PENDING, SENT, CHASED, INTERVIEW, REFUSED, HIRED +} diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/model/JobApplication.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/model/JobApplication.java index e2f5c897..9b80ccf0 100644 --- a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/model/JobApplication.java +++ b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/model/JobApplication.java @@ -1,9 +1,6 @@ package com.dynonuggets.refonteimplicaction.model; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; import javax.persistence.*; import java.time.Instant; @@ -15,6 +12,7 @@ @NoArgsConstructor @Getter @Setter +@Builder @Table(name = "job_application") public class JobApplication { @@ -25,16 +23,18 @@ public class JobApplication { @ManyToOne @JoinColumn(name = "job_id", nullable = false) - private JobPosting jobPosting; + private JobPosting job; @ManyToOne @JoinColumn(name = "user_id", nullable = false) private User user; - @ManyToOne - @JoinColumn(name = "status_id", nullable = false) - private Status status; + @Enumerated(EnumType.STRING) + private ApplyStatusEnum status; + + @Column(name = "last_update") + private Instant lastUpdate; - @Column(name = "submitted_at") - private Instant submitedAt; + @Column(columnDefinition = "boolean default false") + private boolean archive; } diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/model/Status.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/model/Status.java deleted file mode 100644 index a8c2f9d5..00000000 --- a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/model/Status.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.dynonuggets.refonteimplicaction.model; - -import lombok.*; - -import javax.persistence.*; - -import static javax.persistence.GenerationType.IDENTITY; - -@Entity -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Table(name = "status") -public class Status { - @Id - @GeneratedValue(strategy = IDENTITY) - @Column(name = "id", unique = true, nullable = false) - private Long id; - - @Column(name = "label", nullable = false) - private String label; - - @Column(name = "type", nullable = false) - private String type; -} diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/repository/JobApplicationRepository.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/repository/JobApplicationRepository.java index a4409066..419ccf49 100644 --- a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/repository/JobApplicationRepository.java +++ b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/repository/JobApplicationRepository.java @@ -1,7 +1,11 @@ package com.dynonuggets.refonteimplicaction.repository; import com.dynonuggets.refonteimplicaction.model.JobApplication; +import com.dynonuggets.refonteimplicaction.model.JobPosting; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface JobApplicationRepository extends JpaRepository { + Optional findByJob(JobPosting job); } diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/repository/StatusRepository.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/repository/StatusRepository.java deleted file mode 100644 index c7bb7de4..00000000 --- a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/repository/StatusRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.dynonuggets.refonteimplicaction.repository; - -import com.dynonuggets.refonteimplicaction.model.Status; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface StatusRepository extends JpaRepository { -} diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/service/JobApplicationService.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/service/JobApplicationService.java new file mode 100644 index 00000000..e6e3f363 --- /dev/null +++ b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/service/JobApplicationService.java @@ -0,0 +1,48 @@ +package com.dynonuggets.refonteimplicaction.service; + +import com.dynonuggets.refonteimplicaction.adapter.JobApplicationAdapter; +import com.dynonuggets.refonteimplicaction.dto.ApplicationRequest; +import com.dynonuggets.refonteimplicaction.dto.JobApplicationDto; +import com.dynonuggets.refonteimplicaction.exception.NotFoundException; +import com.dynonuggets.refonteimplicaction.model.JobApplication; +import com.dynonuggets.refonteimplicaction.model.JobPosting; +import com.dynonuggets.refonteimplicaction.repository.JobApplicationRepository; +import com.dynonuggets.refonteimplicaction.repository.JobPostingRepository; +import com.dynonuggets.refonteimplicaction.utils.Message; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.Instant; + +import static com.dynonuggets.refonteimplicaction.utils.Message.JOB_NOT_FOUND_MESSAGE; + +@Service +@AllArgsConstructor +public class JobApplicationService { + + private final JobApplicationRepository applyRepository; + private final JobPostingRepository jobRepository; + private final JobApplicationAdapter applyAdapter; + private final AuthService authService; + + public JobApplicationDto createApplyIfNotExists(ApplicationRequest applyRequest) { + final JobPosting job = jobRepository.findById(applyRequest.getJobId()) + .orElseThrow(() -> new NotFoundException(String.format(JOB_NOT_FOUND_MESSAGE, applyRequest.getJobId()))); + + if (applyRepository.findByJob(job).isPresent()) { + throw new IllegalArgumentException(String.format(Message.APPLY_ALREADY_EXISTS_FOR_JOB, job.getId())); + } + + final JobApplication apply = JobApplication.builder() + .job(job) + .archive(false) + .user(authService.getCurrentUser()) + .status(applyRequest.getStatus()) + .lastUpdate(Instant.now()) + .build(); + + final JobApplication applySave = applyRepository.save(apply); + + return applyAdapter.toDto(applySave); + } +} diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/utils/ApiUrls.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/utils/ApiUrls.java index 9233cdb0..ae3c43d5 100644 --- a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/utils/ApiUrls.java +++ b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/utils/ApiUrls.java @@ -41,6 +41,10 @@ public class ApiUrls { public static final String FILE_BASE_URI = "/api/files"; public static final String GET_FILE_BY_KEY = "/{objectKey}"; + // APPLICATION + public static final String APPLY_BASE_URI = "/api/applies"; + public static final String GET_APPLY_URI = "/{applyId}"; + private ApiUrls() { // empêche la construction d'un objet ApiUrls } diff --git a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/utils/Message.java b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/utils/Message.java index f1b5c510..510359cf 100644 --- a/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/utils/Message.java +++ b/backend-implicaction/src/main/java/com/dynonuggets/refonteimplicaction/utils/Message.java @@ -27,6 +27,10 @@ public class Message { public static final String UNAUTHORIZED_CONTENT_TYPE_MESSAGE = "Exception occured while uploading the file [%s] : unauthorized content type"; public static final String FILE_NOT_FOUND_MESSAGE = "No file found with id [%s]"; + // JobApplication messages + public static final String APPLY_NOT_FOUND_MESSAGE = "No apply found with id [%d]"; + public static final String APPLY_ALREADY_EXISTS_FOR_JOB = "Unable to apply, apply already exists with jobId [%d]"; + public static final String BAD_CREDENTIAL_MESSAGE = "Nom d'utilisateur ou mot de passe incorrect."; public static final String USER_DISABLED_MESSAGE = "Votre compte n'a pas encore été activé."; diff --git a/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/adapter/JobApplicationAdapterTest.java b/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/adapter/JobApplicationAdapterTest.java new file mode 100644 index 00000000..ff36b35b --- /dev/null +++ b/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/adapter/JobApplicationAdapterTest.java @@ -0,0 +1,59 @@ +package com.dynonuggets.refonteimplicaction.adapter; + +import com.dynonuggets.refonteimplicaction.controller.ControllerIntegrationTestBase; +import com.dynonuggets.refonteimplicaction.dto.JobApplicationDto; +import com.dynonuggets.refonteimplicaction.model.*; +import org.junit.jupiter.api.Test; + +import java.time.Instant; + +import static org.assertj.core.api.Assertions.assertThat; + +class JobApplicationAdapterTest extends ControllerIntegrationTestBase { + + JobApplicationAdapter adapter = new JobApplicationAdapter(); + + @Test + void given_company_should_return_dto() { + // given + Company company = new Company(1234L, "World Company", "http://logo.com", "La World Company est une multinationale imaginaire basée aux États-Unis", "http://word-company.com"); + ContractType contractType = new ContractType(234L, "CDD", "CDD"); + JobPosting job = new JobPosting(34L, company, "Job de folie", "blablabla", "blablabla", "Paris", "240k", null, contractType, Instant.now(), false); + User user = User.builder().id(87L).build(); + JobApplication model = new JobApplication(123L, job, user, ApplyStatusEnum.PENDING, Instant.now(), false); + + // when + final JobApplicationDto actual = adapter.toDto(model); + + // then + assertThat(actual.getId()).isEqualTo(model.getId()); + assertThat(actual.getCompanyImageUri()).isEqualTo(model.getJob().getCompany().getLogo()); + assertThat(actual.getCompanyName()).isEqualTo(model.getJob().getCompany().getName()); + assertThat(actual.getJobId()).isEqualTo(model.getJob().getId()); + assertThat(actual.getJobTitle()).isEqualTo(model.getJob().getTitle()); + assertThat(actual.getStatus()).isEqualTo(model.getStatus()); + assertThat(actual.getContractType()).isEqualTo(model.getJob().getContractType().getCode()); + } + + @Test + void given_no_company_should_return_dto() { + // given + Company company = null; + ContractType contractType = new ContractType(234L, "CDD", "CDD"); + JobPosting job = new JobPosting(34L, company, "Job de folie", "blablabla", "blablabla", "Paris", "240k", null, contractType, Instant.now(), false); + User user = User.builder().id(87L).build(); + JobApplication model = new JobApplication(123L, job, user, ApplyStatusEnum.PENDING, Instant.now(), false); + + // when + final JobApplicationDto actual = adapter.toDto(model); + + // then + assertThat(actual.getId()).isEqualTo(model.getId()); + assertThat(actual.getCompanyImageUri()).isNull(); + assertThat(actual.getCompanyName()).isNull(); + assertThat(actual.getJobId()).isEqualTo(model.getJob().getId()); + assertThat(actual.getJobTitle()).isEqualTo(model.getJob().getTitle()); + assertThat(actual.getStatus()).isEqualTo(model.getStatus()); + assertThat(actual.getContractType()).isEqualTo(model.getJob().getContractType().getCode()); + } +} diff --git a/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/adapter/StatusAdapterTest.java b/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/adapter/StatusAdapterTest.java deleted file mode 100644 index d53ec5b4..00000000 --- a/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/adapter/StatusAdapterTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.dynonuggets.refonteimplicaction.adapter; - -import com.dynonuggets.refonteimplicaction.dto.StatusDto; -import com.dynonuggets.refonteimplicaction.model.Status; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class StatusAdapterTest { - Status status; - StatusDto statusDto; - StatusAdapter statusAdapter; - - @BeforeEach - public void SetUp() { - statusAdapter = new StatusAdapter(); - - status = Status.builder() - .id(1L) - .label("labelModel") - .type("typeModel") - .build(); - - statusDto = StatusDto.builder() - .id(2L) - .label("labelDto") - .type("typeDto") - .build(); - } - - @Test - void toDtoTest() { - StatusDto statusDtoTest = statusAdapter.toDto(status); - - assertThat(statusDtoTest.getId()).isEqualTo(status.getId()); - assertThat(statusDtoTest.getLabel()).isEqualTo(status.getLabel()); - assertThat(statusDtoTest.getType()).isEqualTo(status.getType()); - } - - @Test - void toModelTest() { - Status statusTest = statusAdapter.toModel(statusDto); - - assertThat(statusTest.getId()).isEqualTo(statusDto.getId()); - assertThat(statusTest.getLabel()).isEqualTo(statusDto.getLabel()); - assertThat(statusTest.getType()).isEqualTo(statusDto.getType()); - } -} diff --git a/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/controller/JobApplicationControllerTest.java b/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/controller/JobApplicationControllerTest.java new file mode 100644 index 00000000..b838a4c6 --- /dev/null +++ b/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/controller/JobApplicationControllerTest.java @@ -0,0 +1,120 @@ +package com.dynonuggets.refonteimplicaction.controller; + +import com.dynonuggets.refonteimplicaction.dto.ApplicationRequest; +import com.dynonuggets.refonteimplicaction.dto.JobApplicationDto; +import com.dynonuggets.refonteimplicaction.exception.NotFoundException; +import com.dynonuggets.refonteimplicaction.model.ApplyStatusEnum; +import com.dynonuggets.refonteimplicaction.service.JobApplicationService; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.ResultActions; + +import static com.dynonuggets.refonteimplicaction.utils.ApiUrls.APPLY_BASE_URI; +import static com.dynonuggets.refonteimplicaction.utils.Message.APPLY_ALREADY_EXISTS_FOR_JOB; +import static com.dynonuggets.refonteimplicaction.utils.Message.JOB_NOT_FOUND_MESSAGE; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.*; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest(controllers = JobApplicationController.class) +class JobApplicationControllerTest extends ControllerIntegrationTestBase { + + @MockBean + JobApplicationService applicationService; + + @Test + @WithMockUser + void should_create_apply() throws Exception { + // given + ApplicationRequest request = new ApplicationRequest(123L, ApplyStatusEnum.PENDING); + JobApplicationDto response = new JobApplicationDto(243L, 123L, "Mon super Job", "Google", "http://uri.com", ApplyStatusEnum.PENDING, "CDI"); + given(applicationService.createApplyIfNotExists(any())).willReturn(response); + String json = gson.toJson(request); + + // when + final ResultActions resultActions = mvc.perform( + post(APPLY_BASE_URI).content(json).accept(APPLICATION_JSON).contentType(APPLICATION_JSON) + ); + + // then + resultActions + .andExpect(status().isCreated()) + .andExpect(content().contentType(APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.id", is(response.getId().intValue()))) + .andExpect(jsonPath("$.jobId", is(response.getJobId().intValue()))) + .andExpect(jsonPath("$.jobTitle", is(response.getJobTitle()))) + .andExpect(jsonPath("$.companyName", is(response.getCompanyName()))) + .andExpect(jsonPath("$.companyImageUri", is(response.getCompanyImageUri()))) + .andExpect(jsonPath("$.status", is(response.getStatus().name()))); + + verify(applicationService, times(1)).createApplyIfNotExists(any()); + } + + @Test + @WithMockUser + void should_return_notfound_when_creating_apply_and_job_notfound() throws Exception { + // given + ApplicationRequest request = new ApplicationRequest(123L, ApplyStatusEnum.PENDING); + String json = gson.toJson(request); + final NotFoundException exception = new NotFoundException(String.format(JOB_NOT_FOUND_MESSAGE, request.getJobId())); + given(applicationService.createApplyIfNotExists(any())).willThrow(exception); + + // when + final ResultActions resultActions = mvc.perform( + post(APPLY_BASE_URI).content(json).accept(APPLICATION_JSON).contentType(APPLICATION_JSON) + ); + + // then + resultActions + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.errorMessage", is(exception.getMessage()))); + + verify(applicationService, times(1)).createApplyIfNotExists(any()); + } + + @Test + @WithMockUser + void should_return_bad_request_when_creating_apply_with_already_applied_job() throws Exception { + // given + ApplicationRequest request = new ApplicationRequest(123L, ApplyStatusEnum.PENDING); + String json = gson.toJson(request); + final IllegalArgumentException exception = new IllegalArgumentException(String.format(APPLY_ALREADY_EXISTS_FOR_JOB, request.getJobId())); + given(applicationService.createApplyIfNotExists(any())).willThrow(exception); + + // when + final ResultActions resultActions = mvc.perform( + post(APPLY_BASE_URI).content(json).accept(APPLICATION_JSON).contentType(APPLICATION_JSON) + ); + + // then + resultActions + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorMessage", is(exception.getMessage()))); + + verify(applicationService, times(1)).createApplyIfNotExists(any()); + } + + @Test + void should_return_forbidden_when_creating_apply_and_no_auth() throws Exception { + // given + ApplicationRequest request = new ApplicationRequest(123L, ApplyStatusEnum.PENDING); + String json = gson.toJson(request); + + // when + final ResultActions resultActions = mvc.perform( + post(APPLY_BASE_URI).content(json).accept(APPLICATION_JSON).contentType(APPLICATION_JSON) + ); + + // then + resultActions.andExpect(status().isForbidden()); + + verify(applicationService, never()).createApplyIfNotExists(any()); + } +} diff --git a/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/service/JobApplicationServiceTest.java b/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/service/JobApplicationServiceTest.java new file mode 100644 index 00000000..0da541dd --- /dev/null +++ b/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/service/JobApplicationServiceTest.java @@ -0,0 +1,102 @@ +package com.dynonuggets.refonteimplicaction.service; + +import com.dynonuggets.refonteimplicaction.adapter.JobApplicationAdapter; +import com.dynonuggets.refonteimplicaction.controller.ControllerIntegrationTestBase; +import com.dynonuggets.refonteimplicaction.dto.ApplicationRequest; +import com.dynonuggets.refonteimplicaction.dto.JobApplicationDto; +import com.dynonuggets.refonteimplicaction.exception.NotFoundException; +import com.dynonuggets.refonteimplicaction.model.*; +import com.dynonuggets.refonteimplicaction.repository.JobApplicationRepository; +import com.dynonuggets.refonteimplicaction.repository.JobPostingRepository; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Instant; + +import static com.dynonuggets.refonteimplicaction.utils.Message.APPLY_ALREADY_EXISTS_FOR_JOB; +import static com.dynonuggets.refonteimplicaction.utils.Message.JOB_NOT_FOUND_MESSAGE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.given; + +@ExtendWith(MockitoExtension.class) +class JobApplicationServiceTest extends ControllerIntegrationTestBase { + + @Mock + JobApplicationRepository applyRepostitory; + + @Mock + JobPostingRepository jobRepository; + + @Mock + JobApplicationAdapter applyAdapter; + + @Mock + AuthService authService; + + @InjectMocks + JobApplicationService jobApplicationService; + + @Test + void should_create_apply() { + // given + ApplicationRequest request = new ApplicationRequest(123L, ApplyStatusEnum.PENDING); + final ContractType contractType = ContractType.builder().code("CDI").build(); + JobPosting job = new JobPosting(123L, Company.builder().id(23L).build(), "Mon super job", "Il est trop cool", "Blablabla", "Paris", "140k", null, contractType, Instant.now(), false); + final User currentUser = User.builder().id(45L).build(); + JobApplication jobApplication = new JobApplication(67L, job, currentUser, request.getStatus(), Instant.now(), false); + JobApplicationDto expectedDto = new JobApplicationDto(jobApplication.getId(), jobApplication.getJob().getId(), jobApplication.getJob().getTitle(), jobApplication.getJob().getCompany().getName(), jobApplication.getJob().getCompany().getLogo(), jobApplication.getStatus(), "CDI"); + given(jobRepository.findById(anyLong())).willReturn(java.util.Optional.of(job)); + given(authService.getCurrentUser()).willReturn(currentUser); + given(applyRepostitory.save(any())).willReturn(jobApplication); + given(applyAdapter.toDto(any())).willReturn(expectedDto); + + // when + JobApplicationDto actual = jobApplicationService.createApplyIfNotExists(request); + + //then + assertThat(actual.getId()).isNotNull(); + assertThat(actual.getId()).isEqualTo(jobApplication.getId()); + assertThat(actual.getStatus()).isEqualTo(jobApplication.getStatus()); + assertThat(actual.getJobId()).isEqualTo(jobApplication.getJob().getId()); + assertThat(actual.getJobTitle()).isEqualTo(jobApplication.getJob().getTitle()); + assertThat(actual.getContractType()).isEqualTo(jobApplication.getJob().getContractType().getCode()); + assertThat(actual.getCompanyName()).isEqualTo(jobApplication.getJob().getCompany().getName()); + assertThat(actual.getCompanyImageUri()).isEqualTo(jobApplication.getJob().getCompany().getLogo()); + } + + @Test + void should_throw_notfound_when_creating_with_no_found_job() { + // given + long jobId = 123L; + ApplicationRequest request = new ApplicationRequest(jobId, ApplyStatusEnum.PENDING); + NotFoundException expectedException = new NotFoundException(String.format(JOB_NOT_FOUND_MESSAGE, jobId)); + given(jobRepository.findById(anyLong())).willThrow(expectedException); + + // when + final NotFoundException actualException = assertThrows(NotFoundException.class, () -> jobApplicationService.createApplyIfNotExists(request)); + + // then + assertThat(actualException.getMessage()).isEqualTo(expectedException.getMessage()); + } + + @Test + void should_throw_exception_when_creating_with_already_applied_job() { + // given + long jobId = 123L; + ApplicationRequest request = new ApplicationRequest(jobId, ApplyStatusEnum.PENDING); + IllegalArgumentException expectedException = new IllegalArgumentException(String.format(APPLY_ALREADY_EXISTS_FOR_JOB, jobId)); + given(jobRepository.findById(anyLong())).willThrow(expectedException); + + // when + final IllegalArgumentException actualException = assertThrows(IllegalArgumentException.class, () -> jobApplicationService.createApplyIfNotExists(request)); + + // then + assertThat(actualException.getMessage()).isEqualTo(expectedException.getMessage()); + } +} diff --git a/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/service/PostServiceTest.java b/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/service/PostServiceTest.java index dcf63797..c66b9a9b 100644 --- a/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/service/PostServiceTest.java +++ b/backend-implicaction/src/test/java/com/dynonuggets/refonteimplicaction/service/PostServiceTest.java @@ -54,10 +54,10 @@ class PostServiceTest { VoteService voteService; @Captor - private ArgumentCaptor argumentCaptor; + ArgumentCaptor argumentCaptor; @InjectMocks - private PostService postService; + PostService postService; @Test void should_save_post_if_subreddit_exists() {