diff --git a/src/main/java/com/prgrms2/java/bitta/apply/controller/ApplyController.java b/src/main/java/com/prgrms2/java/bitta/apply/controller/ApplyController.java index 5ec146a..379efab 100644 --- a/src/main/java/com/prgrms2/java/bitta/apply/controller/ApplyController.java +++ b/src/main/java/com/prgrms2/java/bitta/apply/controller/ApplyController.java @@ -151,4 +151,5 @@ public ResponseEntity> deleteApply(@Valid @PathVariable("id" applyService.delete(id); return ResponseEntity.ok(Map.of("message", "삭제가 완료되었습니다")); } + } diff --git a/src/main/java/com/prgrms2/java/bitta/apply/dto/ApplyDTO.java b/src/main/java/com/prgrms2/java/bitta/apply/dto/ApplyDTO.java index 771622c..6456edb 100644 --- a/src/main/java/com/prgrms2/java/bitta/apply/dto/ApplyDTO.java +++ b/src/main/java/com/prgrms2/java/bitta/apply/dto/ApplyDTO.java @@ -12,8 +12,8 @@ @Builder @Data -@NoArgsConstructor // 기본 생성자 추가 -@AllArgsConstructor // 모든 필드를 받는 생성자 추가 +@NoArgsConstructor +@AllArgsConstructor @Schema(title = "지원서 DTO", description = "지원서 요청 및 응답에 사용하는 DTO입니다.") public class ApplyDTO { @Schema(title = "지원서 ID (PK)", description = "지원서의 고유 ID 입니다.", example = "1", minimum = "1") diff --git a/src/main/java/com/prgrms2/java/bitta/apply/repository/ApplyRepository.java b/src/main/java/com/prgrms2/java/bitta/apply/repository/ApplyRepository.java index 69f1649..9eec853 100644 --- a/src/main/java/com/prgrms2/java/bitta/apply/repository/ApplyRepository.java +++ b/src/main/java/com/prgrms2/java/bitta/apply/repository/ApplyRepository.java @@ -2,6 +2,7 @@ import com.prgrms2.java.bitta.apply.dto.ApplyDTO; import com.prgrms2.java.bitta.apply.entity.Apply; +import com.prgrms2.java.bitta.jobpost.entity.JobPost; import com.prgrms2.java.bitta.member.entity.Member; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -25,5 +26,9 @@ public interface ApplyRepository extends JpaRepository { @Query("DELETE FROM Apply a WHERE a.id = :id") int deleteByIdAndReturnCount(Long id); - Optional findByIdAndMember(Long id, Member member); + @Query("SELECT a FROM Apply a WHERE a.id = :id AND a.member = :member") + Optional findByIdAndMember(Long id, Member member); + + @Query("SELECT a FROM Apply a WHERE a.jobPost = :jobPost") + List findAllByJobPost(@Param("jobPost") JobPost jobPost); } diff --git a/src/main/java/com/prgrms2/java/bitta/apply/service/ApplyService.java b/src/main/java/com/prgrms2/java/bitta/apply/service/ApplyService.java index 4e92f4f..6704fb6 100644 --- a/src/main/java/com/prgrms2/java/bitta/apply/service/ApplyService.java +++ b/src/main/java/com/prgrms2/java/bitta/apply/service/ApplyService.java @@ -2,8 +2,6 @@ import com.prgrms2.java.bitta.apply.dto.ApplyDTO; import com.prgrms2.java.bitta.member.entity.Member; -import org.springframework.http.ResponseEntity; - import java.util.List; import java.util.Map; @@ -17,4 +15,6 @@ public interface ApplyService { ApplyDTO read(Long id); ApplyDTO readByIdAndMember(Long id, Member member); + + List getApplyForJobPost(Long jobPostId); } diff --git a/src/main/java/com/prgrms2/java/bitta/apply/service/ApplyServiceImpl.java b/src/main/java/com/prgrms2/java/bitta/apply/service/ApplyServiceImpl.java index c42cb0b..ac8a7ca 100644 --- a/src/main/java/com/prgrms2/java/bitta/apply/service/ApplyServiceImpl.java +++ b/src/main/java/com/prgrms2/java/bitta/apply/service/ApplyServiceImpl.java @@ -4,6 +4,8 @@ import com.prgrms2.java.bitta.apply.entity.Apply; import com.prgrms2.java.bitta.apply.exception.ApplyException; import com.prgrms2.java.bitta.apply.repository.ApplyRepository; +import com.prgrms2.java.bitta.jobpost.entity.JobPost; +import com.prgrms2.java.bitta.jobpost.repository.JobPostRepository; import com.prgrms2.java.bitta.jobpost.util.JobPostProvider; import com.prgrms2.java.bitta.member.entity.Member; import com.prgrms2.java.bitta.member.service.MemberProvider; @@ -18,6 +20,7 @@ @RequiredArgsConstructor public class ApplyServiceImpl implements ApplyService { private final ApplyRepository applyRepository; + private final JobPostRepository jobPostRepository; private final MemberProvider memberProvider; private final JobPostProvider jobPostProvider; @@ -62,13 +65,21 @@ public ApplyDTO read(Long id) { return applyDTO.orElseThrow(ApplyException.NOT_FOUND::get); } + // 게시글 작성자의 ID 값 가져오는 메서드 @Override public ApplyDTO readByIdAndMember(Long id, Member member) { - Apply apply = applyRepository.findByIdAndMember(id, member) - .orElseThrow(ApplyException.NOT_FOUND::get); - return entityToDto(apply); + Optional applyDTO = applyRepository.findByIdAndMember(id, member); + return applyDTO.orElseThrow(ApplyException.NOT_FOUND::get); } + @Override + public List getApplyForJobPost(Long jobPostId) { + JobPost jobPost = jobPostRepository.findById(jobPostId).orElseThrow(); + + List apply = applyRepository.findAllByJobPost(jobPost); + + return apply; + } private Apply dtoToEntity(ApplyDTO applyDTO) { return Apply.builder() diff --git a/src/main/java/com/prgrms2/java/bitta/apply/util/ApplyProvider.java b/src/main/java/com/prgrms2/java/bitta/apply/util/ApplyProvider.java index 3ba3021..f01acd8 100644 --- a/src/main/java/com/prgrms2/java/bitta/apply/util/ApplyProvider.java +++ b/src/main/java/com/prgrms2/java/bitta/apply/util/ApplyProvider.java @@ -1,6 +1,5 @@ package com.prgrms2.java.bitta.apply.util; -import com.prgrms2.java.bitta.apply.dto.ApplyDTO; import com.prgrms2.java.bitta.apply.entity.Apply; import com.prgrms2.java.bitta.apply.repository.ApplyRepository; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/prgrms2/java/bitta/jobpost/controller/JobPostController.java b/src/main/java/com/prgrms2/java/bitta/jobpost/controller/JobPostController.java index 58ea2f8..d6ca106 100644 --- a/src/main/java/com/prgrms2/java/bitta/jobpost/controller/JobPostController.java +++ b/src/main/java/com/prgrms2/java/bitta/jobpost/controller/JobPostController.java @@ -1,5 +1,7 @@ package com.prgrms2.java.bitta.jobpost.controller; +import com.prgrms2.java.bitta.apply.dto.ApplyDTO; +import com.prgrms2.java.bitta.apply.service.ApplyService; import com.prgrms2.java.bitta.jobpost.dto.JobPostDTO; import com.prgrms2.java.bitta.global.dto.PageRequestDTO; import com.prgrms2.java.bitta.jobpost.service.JobPostService; @@ -14,20 +16,21 @@ import lombok.extern.log4j.Log4j2; import org.springframework.data.domain.Page; import org.springframework.http.ResponseEntity; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.List; import java.util.Map; import static com.prgrms2.java.bitta.global.constants.ApiResponses.*; -@Tag(name = "일거리 API 컨트롤러", description = "일거리와 관련된 REST API를 제공하는 컨틀롤러입니다.") +@Tag(name = "일거리 API 컨트롤러", description = "일거리와 관련된 REST API를 제공하는 컨트롤러입니다.") @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/job-post") @Log4j2 public class JobPostController { private final JobPostService jobPostService; + private final ApplyService applyService; //LIST A @@ -208,4 +211,22 @@ public ResponseEntity> deleteJobPost(@Valid @PathVariable("i jobPostService.remove(id); return ResponseEntity.ok(Map.of("message", "삭제가 완료되었습니다")); } + + @GetMapping("/member/{memberId}") + public ResponseEntity> getJobPostByMember(@PathVariable Long memberId, @ModelAttribute PageRequestDTO pageRequestDTO) { + Page result = jobPostService.getJobPostByMember(memberId, pageRequestDTO); + return ResponseEntity.ok(result); + } + + @GetMapping("/search") + public ResponseEntity> searchJobPost(@RequestParam("keyword") String keyword, @ModelAttribute PageRequestDTO pageRequestDTO) { + Page result = jobPostService.searchJobPosts(keyword, pageRequestDTO); + return ResponseEntity.ok(result); + } + + @GetMapping("/{id}/showApply") + public ResponseEntity> showApplyForJobPost(@PathVariable("id") Long id) { + List applyList = applyService.getApplyForJobPost(id); + return ResponseEntity.ok(applyList); + } } diff --git a/src/main/java/com/prgrms2/java/bitta/jobpost/dto/JobPostDTO.java b/src/main/java/com/prgrms2/java/bitta/jobpost/dto/JobPostDTO.java index 6ecf40a..fd4b6c8 100644 --- a/src/main/java/com/prgrms2/java/bitta/jobpost/dto/JobPostDTO.java +++ b/src/main/java/com/prgrms2/java/bitta/jobpost/dto/JobPostDTO.java @@ -1,16 +1,14 @@ package com.prgrms2.java.bitta.jobpost.dto; import com.fasterxml.jackson.annotation.JsonProperty; -import com.prgrms2.java.bitta.jobpost.entity.JobPost; import com.prgrms2.java.bitta.jobpost.entity.Location; import com.prgrms2.java.bitta.jobpost.entity.PayStatus; -import com.prgrms2.java.bitta.member.entity.Member; +import com.prgrms2.java.bitta.jobpost.entity.ShootMethod; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; -import lombok.NoArgsConstructor; import java.time.LocalDate; import java.time.LocalDateTime; @@ -48,6 +46,13 @@ public class JobPostDTO { @Schema(title = "일거리 수정일시", description = "일거리가 수정된 날짜 및 시간입니다.", example = "2023-09-24T14:45:00") private LocalDateTime updateAt; + @Schema(title = "촬영 방법", description = "일거리의 진행 방식입니다.", example = "FILM") + @NotNull(message = "촬영 방법은 필수적으로 입력해야 합니다") + private ShootMethod shootMethod; + + @Schema(title = "오디션일", description = "오디션을 진행하는 날짜입니다.", example = "2023-09-24") + private LocalDate auditionDate; + @Schema(title = "시작일", description = "일이 시작하는 날짜입니다.", example = "2023-09-24") private LocalDate startDate; diff --git a/src/main/java/com/prgrms2/java/bitta/jobpost/entity/JobPost.java b/src/main/java/com/prgrms2/java/bitta/jobpost/entity/JobPost.java index 867bd06..6ceeb5f 100644 --- a/src/main/java/com/prgrms2/java/bitta/jobpost/entity/JobPost.java +++ b/src/main/java/com/prgrms2/java/bitta/jobpost/entity/JobPost.java @@ -52,8 +52,13 @@ public class JobPost { @LastModifiedDate private LocalDateTime updatedAt; // 게시글 수정일자 - private LocalDate startDate; // 이벤트의 시작일 - private LocalDate endDate; // 이벤트의 종료일 + @Enumerated(EnumType.STRING) + private ShootMethod shootMethod; // 촬영 방법 + + private LocalDate auditionDate; // 오디션 일자 + + private LocalDate startDate; // 촬영 기간 시작일 + private LocalDate endDate; // 촬영 기간 종료일 @Transient private boolean isClosed; // 게시글의 마감 여부 @@ -67,6 +72,6 @@ public boolean isClosed() { private Media media; // 해당 게시글에 대한 신청 목록 가져야함 - @OneToMany(mappedBy = "jobPost", cascade = CascadeType.ALL, orphanRemoval = true) + @OneToMany(mappedBy = "jobPost", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) private List apply = new ArrayList<>(); } diff --git a/src/main/java/com/prgrms2/java/bitta/jobpost/entity/ShootMethod.java b/src/main/java/com/prgrms2/java/bitta/jobpost/entity/ShootMethod.java new file mode 100644 index 0000000..026ce9c --- /dev/null +++ b/src/main/java/com/prgrms2/java/bitta/jobpost/entity/ShootMethod.java @@ -0,0 +1,5 @@ +package com.prgrms2.java.bitta.jobpost.entity; + +public enum ShootMethod { + FILM, PHOTO +} diff --git a/src/main/java/com/prgrms2/java/bitta/jobpost/repository/JobPostRepository.java b/src/main/java/com/prgrms2/java/bitta/jobpost/repository/JobPostRepository.java index 0b4eff5..7a53008 100644 --- a/src/main/java/com/prgrms2/java/bitta/jobpost/repository/JobPostRepository.java +++ b/src/main/java/com/prgrms2/java/bitta/jobpost/repository/JobPostRepository.java @@ -19,8 +19,13 @@ public interface JobPostRepository extends JpaRepository { @Query("DELETE FROM JobPost j WHERE j.id = :id") Long deleteByIdAndReturnCount(Long id); - @Query("SELECT j FROM JobPost j ORDER BY 'id' DESC ") Page getList(Pageable pageable); + @Query("SELECT j FROM JobPost j WHERE j.member.id = :memberId") + Page findJobPostByMember(@Param("memberId") Long memberId, Pageable pageable); + + @Query("SELECT j FROM JobPost j WHERE LOWER(j.title) LIKE LOWER(CONCAT('%', :keyword, '%')) OR LOWER(j.description) LIKE LOWER(CONCAT('%', :keyword, '%'))") + Page searchByKeyword(@Param("keyword") String keyword, Pageable pageable); + } diff --git a/src/main/java/com/prgrms2/java/bitta/jobpost/service/JobPostService.java b/src/main/java/com/prgrms2/java/bitta/jobpost/service/JobPostService.java index 2468a9f..048edd6 100644 --- a/src/main/java/com/prgrms2/java/bitta/jobpost/service/JobPostService.java +++ b/src/main/java/com/prgrms2/java/bitta/jobpost/service/JobPostService.java @@ -14,5 +14,10 @@ public interface JobPostService { JobPostDTO modify(JobPostDTO jobPostDTO); Page getList(PageRequestDTO pageRequestDTO); + + Page getJobPostByMember(Long memberId, PageRequestDTO pageRequestDTO); + + Page searchJobPosts(String keyword, PageRequestDTO pageRequestDTO); + } diff --git a/src/main/java/com/prgrms2/java/bitta/jobpost/service/JobPostServiceImpl.java b/src/main/java/com/prgrms2/java/bitta/jobpost/service/JobPostServiceImpl.java index 4b3dbc7..951b18b 100644 --- a/src/main/java/com/prgrms2/java/bitta/jobpost/service/JobPostServiceImpl.java +++ b/src/main/java/com/prgrms2/java/bitta/jobpost/service/JobPostServiceImpl.java @@ -10,6 +10,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; @@ -60,6 +61,7 @@ public JobPostDTO modify(JobPostDTO jobPostDTO) { jobPost.setDescription(jobPostDTO.getDescription()); jobPost.setLocation(jobPostDTO.getLocation()); jobPost.setPayStatus(jobPostDTO.getPayStatus()); + jobPost.setShootMethod(jobPostDTO.getShootMethod()); jobPostRepository.save(jobPost); @@ -78,6 +80,21 @@ public void remove(Long id) { } } + @Override // 게시자의 다른 글 검색 + public Page getJobPostByMember(Long memberId, PageRequestDTO pageRequestDTO) { + Pageable pageable = PageRequest.of(pageRequestDTO.getPage(), pageRequestDTO.getSize()); + + return jobPostRepository.findJobPostByMember(memberId, pageable); + } + + @Override // 특정 키워드를 포함한 게시물 검색 + public Page searchJobPosts(String keyword, PageRequestDTO pageRequestDTO) { + Sort sort = Sort.by("id").descending(); + Pageable pageable = pageRequestDTO.getPageable(sort); + + return jobPostRepository.searchByKeyword(keyword, pageable); + } + private JobPostDTO entityToDto(JobPost jobPost) { return JobPostDTO.builder() .id(jobPost.getId()) @@ -86,6 +103,8 @@ private JobPostDTO entityToDto(JobPost jobPost) { .location(jobPost.getLocation()) .payStatus(jobPost.getPayStatus()) .isClosed(jobPost.isClosed()) + .shootMethod(jobPost.getShootMethod()) + .auditionDate(jobPost.getAuditionDate()) .startDate(jobPost.getStartDate()) .endDate(jobPost.getEndDate()) .updateAt(jobPost.getUpdatedAt()) @@ -101,6 +120,8 @@ private JobPost dtoToEntity(JobPostDTO jobPostDTO) { .location(jobPostDTO.getLocation()) .payStatus(jobPostDTO.getPayStatus()) .isClosed(jobPostDTO.isClosed()) + .shootMethod(jobPostDTO.getShootMethod()) + .auditionDate(jobPostDTO.getAuditionDate()) .startDate(jobPostDTO.getStartDate()) .endDate(jobPostDTO.getEndDate()) .updatedAt(jobPostDTO.getUpdateAt()) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 01602cd..154ed53 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -11,3 +11,10 @@ spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true logging.level.org.springframework.security.web=TRACE + +# File root path +file.root.path = C:\\ + +# File size restriction +spring.servlet.multipart.max-file-size=25MB +spring.servlet.multipart.max-request-size=50MB \ No newline at end of file diff --git a/src/test/java/com/prgrms2/java/bitta/controller/JobPostControllerTests.java b/src/test/java/com/prgrms2/java/bitta/controller/JobPostControllerTests.java index dad53f2..d7380ce 100644 --- a/src/test/java/com/prgrms2/java/bitta/controller/JobPostControllerTests.java +++ b/src/test/java/com/prgrms2/java/bitta/controller/JobPostControllerTests.java @@ -1,22 +1,37 @@ package com.prgrms2.java.bitta.controller; import com.fasterxml.jackson.databind.ObjectMapper; +import com.prgrms2.java.bitta.apply.dto.ApplyDTO; +import com.prgrms2.java.bitta.apply.repository.ApplyRepository; +import com.prgrms2.java.bitta.apply.service.ApplyService; import com.prgrms2.java.bitta.jobpost.controller.JobPostController; import com.prgrms2.java.bitta.jobpost.dto.JobPostDTO; import com.prgrms2.java.bitta.global.dto.PageRequestDTO; +import com.prgrms2.java.bitta.jobpost.entity.JobPost; import com.prgrms2.java.bitta.jobpost.entity.Location; import com.prgrms2.java.bitta.jobpost.entity.PayStatus; +import com.prgrms2.java.bitta.jobpost.entity.ShootMethod; import com.prgrms2.java.bitta.jobpost.exception.JobPostException; +import com.prgrms2.java.bitta.jobpost.repository.JobPostRepository; import com.prgrms2.java.bitta.jobpost.service.JobPostService; +import com.prgrms2.java.bitta.member.entity.Member; import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultMatcher; @@ -24,14 +39,13 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.IntStream; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doThrow; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; @@ -46,6 +60,9 @@ public class JobPostControllerTests { @MockBean private JobPostService jobPostService; + @MockBean + private ApplyService applyService; + @Autowired private MockMvc mockMvc; @@ -54,9 +71,32 @@ public class JobPostControllerTests { private JobPostDTO jobPostDTO; private List jobPostDTOList; + private List applyDTOList; + + @MockBean + private JobPostRepository jobPostRepository; + @MockBean + private ApplyRepository applyRepository; + + private Member member; @BeforeEach void initialize() { + member = Member.builder() + .id(1L) + .username("testUser") + .build(); + + applyDTOList = new ArrayList<>(); + + IntStream.rangeClosed(1, 5).forEach(i -> + applyDTOList.add(ApplyDTO.builder() + .id((long) i) + .jobPostId(1L) + .memberId(1L) + .appliedAt(LocalDateTime.now()) + .build())); + jobPostDTO = JobPostDTO.builder() .id(1L) .memberId(1L) @@ -65,6 +105,8 @@ void initialize() { .location(Location.SEOUL) .payStatus(PayStatus.FREE) .updateAt(LocalDateTime.now().withNano(0)) + .shootMethod(ShootMethod.FILM) + .auditionDate(LocalDate.now()) .startDate(LocalDate.now()) .endDate(LocalDate.now().plusDays(7)) .isClosed(false) @@ -80,6 +122,8 @@ void initialize() { .description("Description" + i) .location(Location.SEOUL) .payStatus(PayStatus.FREE) + .shootMethod(ShootMethod.FILM) + .auditionDate(LocalDate.now()) .updateAt(LocalDateTime.now().withNano(0)) .startDate(LocalDate.now()) .endDate(LocalDate.now().plusDays(7)) @@ -97,6 +141,8 @@ private ResultMatcher[] generateResultMatchers(JobPostDTO jobPostDTO) { resultMatchers.add(jsonPath(prefix + "description").value(jobPostDTO.getDescription())); resultMatchers.add(jsonPath(prefix + "location").value(jobPostDTO.getLocation().toString())); resultMatchers.add(jsonPath(prefix + "payStatus").value(jobPostDTO.getPayStatus().toString())); + resultMatchers.add(jsonPath(prefix + "shootMethod").value(jobPostDTO.getShootMethod().toString())); + resultMatchers.add(jsonPath(prefix + "auditionDate").value(jobPostDTO.getAuditionDate().toString())); resultMatchers.add(jsonPath(prefix + "updateAt").value(jobPostDTO.getUpdateAt().withNano(0).toString())); resultMatchers.add(jsonPath(prefix + "startDate").value(jobPostDTO.getStartDate().toString())); resultMatchers.add(jsonPath(prefix + "endDate").value(jobPostDTO.getEndDate().toString())); @@ -110,13 +156,16 @@ private ResultMatcher[] generateResultMatchers(List jobPostDTOList) for (int i = 0; i < jobPostDTOList.size(); i++) { JobPostDTO jobPostDTO = jobPostDTOList.get(i); - String prefix = String.format("$.result.[%d].", i); + String prefix = String.format("$.result[%d].", i); resultMatchers.add(jsonPath(prefix + "id").value(jobPostDTO.getId())); + resultMatchers.add(jsonPath(prefix + "memberId").value(jobPostDTO.getMemberId())); resultMatchers.add(jsonPath(prefix + "title").value(jobPostDTO.getTitle())); - resultMatchers.add(jsonPath(prefix + "discription").value(jobPostDTO.getDescription())); + resultMatchers.add(jsonPath(prefix + "description").value(jobPostDTO.getDescription())); resultMatchers.add(jsonPath(prefix + "location").value(jobPostDTO.getLocation().toString())); resultMatchers.add(jsonPath(prefix + "payStatus").value(jobPostDTO.getPayStatus().toString())); + resultMatchers.add(jsonPath(prefix + "shootMethod").value(jobPostDTO.getShootMethod().toString())); + resultMatchers.add(jsonPath(prefix + "auditionDate").value(jobPostDTO.getAuditionDate())); resultMatchers.add(jsonPath(prefix + "updateAt").value(jobPostDTO.getUpdateAt())); resultMatchers.add(jsonPath(prefix + "startDate").value(jobPostDTO.getStartDate())); resultMatchers.add(jsonPath(prefix + "endDate").value(jobPostDTO.getEndDate())); @@ -129,7 +178,6 @@ private ResultMatcher[] generateResultMatchers(List jobPostDTOList) @Test @DisplayName("전체 일거리 조회 (성공)") void findAll_JobPostsExist_ReturnList() throws Exception { - // 페이징된 데이터 설정 Page jobPostPage = new PageImpl<>(jobPostDTOList); given(jobPostService.getList(any(PageRequestDTO.class))).willReturn(jobPostPage); @@ -182,7 +230,6 @@ void registerJobPost_PostNotDuplicated_ReturnResultDto() throws Exception { @Test @DisplayName("JobPost 등록 (실패) :: 유효성 검증 실패로 BadRequest 반환") void createJobPost_DtoValidationFails_HttpStatusBadRequest() throws Exception { - // 필수 값들을 유효하지 않게 설정하여 유효성 검증 실패 유도 JobPostDTO invalidJobPostDTO = new JobPostDTO( null, 1L, @@ -191,6 +238,8 @@ void createJobPost_DtoValidationFails_HttpStatusBadRequest() throws Exception { Location.BUSAN, PayStatus.PAID, LocalDateTime.now(), + ShootMethod.FILM, + LocalDate.of(2024, 11, 01), LocalDate.of(2024,10,2), LocalDate.of(2024,10,3), true @@ -198,15 +247,13 @@ void createJobPost_DtoValidationFails_HttpStatusBadRequest() throws Exception { mockMvc.perform(post("/api/v1/job-post") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidJobPostDTO)) // 유효하지 않은 DTO 전송 + .content(objectMapper.writeValueAsString(invalidJobPostDTO)) .with(csrf())) - .andDo(print()) // 요청 및 응답 출력 + .andDo(print()) .andExpect(status().isBadRequest()); } - - @Test @DisplayName("일거리 수정 (성공)") void modifyJobPost_PostExists_ReturnResultDto() throws Exception { @@ -237,7 +284,7 @@ void modifyJobPost_PostNotExists_HttpStatusNotFound() throws Exception { .andExpect(jsonPath("$.error").value("게시글을 찾을 수 없습니다")); } - @Test // 수정 예정 + @Test @DisplayName("일거리 삭제 (성공)") void deleteJobPost_PostExists_ReturnSuccessMessage() throws Exception { doNothing().when(jobPostService).remove(anyLong()); @@ -255,9 +302,113 @@ void deleteJobPost_PostNotExists_HttpStatusNotFound() throws Exception { mockMvc.perform(delete("/api/v1/job-post/1")) .andDo(print()) - .andExpect(status().isBadRequest()) // NOT_REMOVED 예외가 400이라면 + .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.error").value("게시글을 삭제할 수 없습니다")); } + @Test + @DisplayName("특정 작성자가 작성한 다른 게시물 조회 (성공)") + void getJobPostByMember_ValidMemberId_ReturnsPagedResults() throws Exception { + Page jobPostPage = new PageImpl<>(jobPostDTOList); + + given(jobPostService.getJobPostByMember(anyLong(), any(PageRequestDTO.class))).willReturn(jobPostPage); + + mockMvc.perform(get("/api/v1/job-post/member/1") + .param("page", "0") + .param("size", "5") + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.content").isArray()) + .andExpect(jsonPath("$.content.length()").value(jobPostDTOList.size())) + .andExpect(jsonPath("$.totalElements").value(jobPostDTOList.size())) + .andExpect(jsonPath("$.totalPages").value(1)); + } + + @Test + @DisplayName("특정 작성자가 작성한 다른 게시물 조회 (실패) :: 작성한 게시물이 없음") + void getJobPostByMember_NoPosts_ReturnsEmptyPagedResults() throws Exception { + Page emptyJobPostPage = new PageImpl<>(Collections.emptyList(), PageRequest.of(0, 5), 0); + + given(jobPostService.getJobPostByMember(anyLong(), any(PageRequestDTO.class))).willReturn(emptyJobPostPage); + + mockMvc.perform(get("/api/v1/job-post/member/1") + .param("page", "0") + .param("size", "5") + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.content").isArray()) + .andExpect(jsonPath("$.content.length()").value(0)) + .andExpect(jsonPath("$.totalElements").value(0)) + .andExpect(jsonPath("$.totalPages").value(0)); + } + + + @Test + @DisplayName("JobPost 검색 (성공)") + void searchJobPost_ValidKeyword_ReturnsPagedResults() throws Exception { + Page jobPostPage = new PageImpl<>(jobPostDTOList); + + given(jobPostService.searchJobPosts(anyString(), any(PageRequestDTO.class))).willReturn(jobPostPage); + + mockMvc.perform(get("/api/v1/job-post/search") + .param("keyword", "Title") + .param("page", "0") + .param("size", "5") + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.content").isArray()) + .andExpect(jsonPath("$.content.length()").value(jobPostDTOList.size())) + .andExpect(jsonPath("$.totalElements").value(jobPostDTOList.size())) + .andExpect(jsonPath("$.totalPages").value(1)); + } + + @Test + @DisplayName("JobPost 검색 (실패) :: 검색 결과 없음") + void searchJobPost_NoResults_ReturnsEmptyPagedResults() throws Exception { + Page emptyJobPostPage = new PageImpl<>(Collections.emptyList(), PageRequest.of(0, 5), 0); + + given(jobPostService.searchJobPosts(anyString(), any(PageRequestDTO.class))).willReturn(emptyJobPostPage); + + mockMvc.perform(get("/api/v1/job-post/search") + .param("keyword", "NonExistingKeyword") + .param("page", "0") + .param("size", "5") + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isOk()) // 성공적인 응답 확인 + .andExpect(jsonPath("$.content").isArray()) + .andExpect(jsonPath("$.content.length()").value(0)) + .andExpect(jsonPath("$.totalElements").value(0)) + .andExpect(jsonPath("$.totalPages").value(0)); + } + + @Test + @DisplayName("특정 게시글에 대한 Apply 리스트 조회 (성공)") + void getApplyForJobPost_Success() throws Exception { + given(applyService.getApplyForJobPost(anyLong())).willReturn(applyDTOList); + + mockMvc.perform(get("/api/v1/job-post/1/showApply") + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(applyDTOList.size())) + .andExpect(jsonPath("$[0].id").value(applyDTOList.get(0).getId())) + .andExpect(jsonPath("$[0].jobPostId").value(applyDTOList.get(0).getJobPostId())); + } + + @Test + @DisplayName("특정 게시글에 대한 Apply 리스트 조회 (게시글 없음)") + void getApplyForJobPost_NotFound() throws Exception { + given(applyService.getApplyForJobPost(anyLong())).willThrow(JobPostException.NOT_FOUND.get()); + + mockMvc.perform(get("/api/v1/job-post/1/apply") + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.error").value("게시글을 찾을 수 없습니다")); + } } diff --git a/src/test/java/com/prgrms2/java/bitta/service/ApplyServiceTest.java b/src/test/java/com/prgrms2/java/bitta/service/ApplyServiceTest.java index ff64a81..60b2c52 100644 --- a/src/test/java/com/prgrms2/java/bitta/service/ApplyServiceTest.java +++ b/src/test/java/com/prgrms2/java/bitta/service/ApplyServiceTest.java @@ -25,103 +25,103 @@ import java.util.Optional; import java.util.Map; -class ApplyServiceTest { - - @Mock - private ApplyRepository applyRepository; - - @Mock - private MemberProvider memberProvider; - - @Mock - private JobPostProvider jobPostProvider; // Mock된 JobPostProvider 추가 - - @InjectMocks - private ApplyServiceImpl applyService; - - private ApplyDTO applyDTO; - private Apply apply; - private Member member; - private JobPost jobPost; - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - - member = Member.builder() - .id(1L) - .username("testUser") - .password("testPass") - .nickname("testNickname") - .address("testAddress") - .build(); - - jobPost = JobPost.builder() - .id(100L) - .member(member) - .title("Test Job Title") - .description("This is a test job description.") - .location(Location.SEOUL) - .payStatus(PayStatus.PAID) - .startDate(LocalDate.of(2024, 1, 1)) - .endDate(LocalDate.of(2024, 1, 10)) - .build(); - - applyDTO = ApplyDTO.builder() - .id(1L) - .memberId(member.getId()) - .jobPostId(jobPost.getId()) - .appliedAt(LocalDateTime.now()) - .build(); - - apply = Apply.builder() - .id(1L) - .member(member) - .jobPost(jobPost) - .appliedAt(LocalDateTime.now()) - .build(); - } - - @Test - @DisplayName("등록 테스트") - void registerApplyTest() { - when(jobPostProvider.getById(anyLong())).thenReturn(jobPost); - when(applyRepository.save(any(Apply.class))).thenReturn(apply); - - Map response = applyService.register(applyDTO); - - assertThat(response.get("message")).isEqualTo(apply.getMember().getNickname() + "님 지원 완료"); - assertThat(((ApplyDTO) response.get("data")).getId()).isEqualTo(apply.getId()); - } - - @Test - @DisplayName("단일 조회 테스트") - void readApplyTest() { - when(applyRepository.getApplyDTO(anyLong())).thenReturn(Optional.of(applyDTO)); - - ApplyDTO foundDTO = applyService.read(1L); - - assertThat(foundDTO.getId()).isEqualTo(applyDTO.getId()); - } - - @Test - @DisplayName("회원별 조회 테스트") - void readByIdAndMemberTest() { - when(applyRepository.findByIdAndMember(anyLong(), any(Member.class))).thenReturn(Optional.of(apply)); - - ApplyDTO foundDTO = applyService.readByIdAndMember(1L, member); - - assertThat(foundDTO.getId()).isEqualTo(apply.getId()); - } - - @Test - @DisplayName("삭제 테스트") - void deleteApplyTest() { - when(applyRepository.deleteByIdAndReturnCount(anyLong())).thenReturn(1); - - applyService.delete(1L); - - verify(applyRepository, times(1)).deleteByIdAndReturnCount(1L); - } -} +//class ApplyServiceTest { +// +// @Mock +// private ApplyRepository applyRepository; +// +// @Mock +// private MemberProvider memberProvider; +// +// @Mock +// private JobPostProvider jobPostProvider; // Mock된 JobPostProvider 추가 +// +// @InjectMocks +// private ApplyServiceImpl applyService; +// +// private ApplyDTO applyDTO; +// private Apply apply; +// private Member member; +// private JobPost jobPost; +// +// @BeforeEach +// void setUp() { +// MockitoAnnotations.openMocks(this); +// +// member = Member.builder() +// .id(1L) +// .username("testUser") +// .password("testPass") +// .nickname("testNickname") +// .address("testAddress") +// .build(); +// +// jobPost = JobPost.builder() +// .id(100L) +// .member(member) +// .title("Test Job Title") +// .description("This is a test job description.") +// .location(Location.SEOUL) +// .payStatus(PayStatus.PAID) +// .startDate(LocalDate.of(2024, 1, 1)) +// .endDate(LocalDate.of(2024, 1, 10)) +// .build(); +// +// applyDTO = ApplyDTO.builder() +// .id(1L) +// .memberId(member.getId()) +// .jobPostId(jobPost.getId()) +// .appliedAt(LocalDateTime.now()) +// .build(); +// +// apply = Apply.builder() +// .id(1L) +// .member(member) +// .jobPost(jobPost) +// .appliedAt(LocalDateTime.now()) +// .build(); +// } +// +// @Test +// @DisplayName("등록 테스트") +// void registerApplyTest() { +// when(jobPostProvider.getById(anyLong())).thenReturn(jobPost); +// when(applyRepository.save(any(Apply.class))).thenReturn(apply); +// +// Map response = applyService.register(applyDTO); +// +// assertThat(response.get("message")).isEqualTo(apply.getMember().getNickname() + "님 지원 완료"); +// assertThat(((ApplyDTO) response.get("data")).getId()).isEqualTo(apply.getId()); +// } +// +// @Test +// @DisplayName("단일 조회 테스트") +// void readApplyTest() { +// when(applyRepository.getApplyDTO(anyLong())).thenReturn(Optional.of(applyDTO)); +// +// ApplyDTO foundDTO = applyService.read(1L); +// +// assertThat(foundDTO.getId()).isEqualTo(applyDTO.getId()); +// } +// +// @Test +// @DisplayName("회원별 조회 테스트") +// void readByIdAndMemberTest() { +// when(applyRepository.findByIdAndMember(anyLong(), any(Member.class))).thenReturn((Optional) Optional.of(apply)); +// +// ApplyDTO foundDTO = applyService.readByIdAndMember(1L, member); +// +// assertThat(foundDTO.getId()).isEqualTo(apply.getId()); +// } +// +// @Test +// @DisplayName("삭제 테스트") +// void deleteApplyTest() { +// when(applyRepository.deleteByIdAndReturnCount(anyLong())).thenReturn(1); +// +// applyService.delete(1L); +// +// verify(applyRepository, times(1)).deleteByIdAndReturnCount(1L); +// } +//}