diff --git a/src/main/java/com/yapp/artie/domain/notice/adapter/in/web/GetNoticeDetailController.java b/src/main/java/com/yapp/artie/domain/notice/adapter/in/web/GetNoticeDetailController.java new file mode 100644 index 00000000..8048c59c --- /dev/null +++ b/src/main/java/com/yapp/artie/domain/notice/adapter/in/web/GetNoticeDetailController.java @@ -0,0 +1,36 @@ +package com.yapp.artie.domain.notice.adapter.in.web; + + +import com.yapp.artie.domain.notice.application.port.in.GetNoticeDetailQuery; +import com.yapp.artie.domain.notice.application.port.in.GetNoticeDetailResponse; +import com.yapp.artie.global.common.annotation.WebAdapter; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +@WebAdapter +@RequestMapping("/notice") +@RequiredArgsConstructor +class GetNoticeDetailController { + + private final GetNoticeDetailQuery getNoticeDetailQuery; + + @Operation(summary = "공지사항 상세 조회", description = "특정 공지사항 상세를 조회") + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "공지사항 상세가 성공적으로 조회됨", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = GetNoticeDetailResponse.class))), + }) + @GetMapping("/{id}") + public ResponseEntity getNoticeDetail(@PathVariable("id") Long id) { + return ResponseEntity.ok(getNoticeDetailQuery.loadNoticeDetail(id)); + } +} diff --git a/src/main/java/com/yapp/artie/domain/notice/adapter/in/web/GetNoticeListController.java b/src/main/java/com/yapp/artie/domain/notice/adapter/in/web/GetNoticeListController.java new file mode 100644 index 00000000..b9078e8e --- /dev/null +++ b/src/main/java/com/yapp/artie/domain/notice/adapter/in/web/GetNoticeListController.java @@ -0,0 +1,37 @@ +package com.yapp.artie.domain.notice.adapter.in.web; + + +import com.yapp.artie.domain.notice.application.port.in.GetNoticeDetailResponse; +import com.yapp.artie.domain.notice.application.port.in.GetNoticeListQuery; +import com.yapp.artie.global.common.annotation.WebAdapter; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@WebAdapter +@RequestMapping("/notice") +@RequiredArgsConstructor +class GetNoticeListController { + + private final GetNoticeListQuery getNoticeListQuery; + + @Operation(summary = "공지사항 목록 조회", description = "공지사항 목록을 조회") + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "공지사항 목록이 성공적으로 조회됨", + content = @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = GetNoticeDetailResponse.class)))), + }) + @GetMapping() + public ResponseEntity> getNotices() { + return ResponseEntity.ok(getNoticeListQuery.loadNoticeList()); + } +} diff --git a/src/main/java/com/yapp/artie/domain/notice/adapter/out/persistence/NoticePersistenceAdapter.java b/src/main/java/com/yapp/artie/domain/notice/adapter/out/persistence/NoticePersistenceAdapter.java new file mode 100644 index 00000000..9e1fea2a --- /dev/null +++ b/src/main/java/com/yapp/artie/domain/notice/adapter/out/persistence/NoticePersistenceAdapter.java @@ -0,0 +1,25 @@ +package com.yapp.artie.domain.notice.adapter.out.persistence; + +import com.yapp.artie.domain.notice.application.port.out.LoadNoticePort; +import com.yapp.artie.domain.notice.domain.Notice; +import com.yapp.artie.domain.notice.domain.NoticeNotFoundException; +import com.yapp.artie.global.common.annotation.PersistenceAdapter; +import java.util.List; +import lombok.AllArgsConstructor; + +@PersistenceAdapter +@AllArgsConstructor +class NoticePersistenceAdapter implements LoadNoticePort { + + private final NoticeRepository noticeRepository; + + @Override + public List loadNoticeList() { + return noticeRepository.findAll(); + } + + @Override + public Notice loadNoticeDetail(Long id) { + return noticeRepository.findById(id).orElseThrow(NoticeNotFoundException::new); + } +} diff --git a/src/main/java/com/yapp/artie/domain/notice/adapter/out/persistence/NoticeRepository.java b/src/main/java/com/yapp/artie/domain/notice/adapter/out/persistence/NoticeRepository.java new file mode 100644 index 00000000..4fe51e9f --- /dev/null +++ b/src/main/java/com/yapp/artie/domain/notice/adapter/out/persistence/NoticeRepository.java @@ -0,0 +1,8 @@ +package com.yapp.artie.domain.notice.adapter.out.persistence; + +import com.yapp.artie.domain.notice.domain.Notice; +import org.springframework.data.jpa.repository.JpaRepository; + +interface NoticeRepository extends JpaRepository { + +} diff --git a/src/main/java/com/yapp/artie/domain/notice/application/port/in/GetNoticeDetailQuery.java b/src/main/java/com/yapp/artie/domain/notice/application/port/in/GetNoticeDetailQuery.java new file mode 100644 index 00000000..d3ad9199 --- /dev/null +++ b/src/main/java/com/yapp/artie/domain/notice/application/port/in/GetNoticeDetailQuery.java @@ -0,0 +1,6 @@ +package com.yapp.artie.domain.notice.application.port.in; + +public interface GetNoticeDetailQuery { + + GetNoticeDetailResponse loadNoticeDetail(Long id); +} diff --git a/src/main/java/com/yapp/artie/domain/notice/application/port/in/GetNoticeDetailResponse.java b/src/main/java/com/yapp/artie/domain/notice/application/port/in/GetNoticeDetailResponse.java new file mode 100644 index 00000000..3cc3e16d --- /dev/null +++ b/src/main/java/com/yapp/artie/domain/notice/application/port/in/GetNoticeDetailResponse.java @@ -0,0 +1,26 @@ +package com.yapp.artie.domain.notice.application.port.in; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@Schema(description = "공지사항 상세 Response") +@AllArgsConstructor +public class GetNoticeDetailResponse { + + @Schema(description = "공지사항 아이디") + private final Long id; + + @Schema(description = "공지일") + private final LocalDateTime date; + + @Schema(description = "공지사항 제목") + private final String title; + + @Schema(description = "공지 내용") + private String contents; +} + + diff --git a/src/main/java/com/yapp/artie/domain/notice/application/port/in/GetNoticeListQuery.java b/src/main/java/com/yapp/artie/domain/notice/application/port/in/GetNoticeListQuery.java new file mode 100644 index 00000000..d831430c --- /dev/null +++ b/src/main/java/com/yapp/artie/domain/notice/application/port/in/GetNoticeListQuery.java @@ -0,0 +1,8 @@ +package com.yapp.artie.domain.notice.application.port.in; + +import java.util.List; + +public interface GetNoticeListQuery { + + List loadNoticeList(); +} diff --git a/src/main/java/com/yapp/artie/domain/notice/application/port/out/LoadNoticePort.java b/src/main/java/com/yapp/artie/domain/notice/application/port/out/LoadNoticePort.java new file mode 100644 index 00000000..9fa7adc4 --- /dev/null +++ b/src/main/java/com/yapp/artie/domain/notice/application/port/out/LoadNoticePort.java @@ -0,0 +1,11 @@ +package com.yapp.artie.domain.notice.application.port.out; + +import com.yapp.artie.domain.notice.domain.Notice; +import java.util.List; + +public interface LoadNoticePort { + + List loadNoticeList(); + + Notice loadNoticeDetail(Long id); +} diff --git a/src/main/java/com/yapp/artie/domain/notice/application/service/GetNoticeDetailService.java b/src/main/java/com/yapp/artie/domain/notice/application/service/GetNoticeDetailService.java new file mode 100644 index 00000000..03f72511 --- /dev/null +++ b/src/main/java/com/yapp/artie/domain/notice/application/service/GetNoticeDetailService.java @@ -0,0 +1,28 @@ +package com.yapp.artie.domain.notice.application.service; + +import com.yapp.artie.domain.notice.application.port.in.GetNoticeDetailQuery; +import com.yapp.artie.domain.notice.application.port.in.GetNoticeDetailResponse; +import com.yapp.artie.domain.notice.application.port.out.LoadNoticePort; +import com.yapp.artie.domain.notice.domain.Notice; +import com.yapp.artie.global.common.annotation.UseCase; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@Transactional(readOnly = true) +@AllArgsConstructor +class GetNoticeDetailService implements GetNoticeDetailQuery { + + private final LoadNoticePort loadNoticePort; + + @Override + public GetNoticeDetailResponse loadNoticeDetail(Long id) { + Notice notice = loadNoticePort.loadNoticeDetail(id); + return new GetNoticeDetailResponse( + notice.getId(), + notice.getCreatedAt(), + notice.getTitle(), + notice.getContents() + ); + } +} diff --git a/src/main/java/com/yapp/artie/domain/notice/application/service/GetNoticeListService.java b/src/main/java/com/yapp/artie/domain/notice/application/service/GetNoticeListService.java new file mode 100644 index 00000000..023bff0a --- /dev/null +++ b/src/main/java/com/yapp/artie/domain/notice/application/service/GetNoticeListService.java @@ -0,0 +1,30 @@ +package com.yapp.artie.domain.notice.application.service; + +import com.yapp.artie.domain.notice.application.port.in.GetNoticeDetailResponse; +import com.yapp.artie.domain.notice.application.port.in.GetNoticeListQuery; +import com.yapp.artie.domain.notice.application.port.out.LoadNoticePort; +import com.yapp.artie.global.common.annotation.UseCase; +import java.util.List; +import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@Transactional(readOnly = true) +@AllArgsConstructor +class GetNoticeListService implements GetNoticeListQuery { + + private final LoadNoticePort loadNoticePort; + + @Override + public List loadNoticeList() { + return loadNoticePort.loadNoticeList().stream() + .map(eachNotice -> + new GetNoticeDetailResponse( + eachNotice.getId(), + eachNotice.getCreatedAt(), + eachNotice.getTitle(), + eachNotice.getContents() + )).collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/yapp/artie/domain/notice/controller/NoticeController.java b/src/main/java/com/yapp/artie/domain/notice/controller/NoticeController.java deleted file mode 100644 index e4f162aa..00000000 --- a/src/main/java/com/yapp/artie/domain/notice/controller/NoticeController.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.yapp.artie.domain.notice.controller; - - -import com.yapp.artie.domain.notice.dto.NoticeDetailInfo; -import com.yapp.artie.domain.notice.service.NoticeService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.ArraySchema; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RequestMapping("/notice") -@RequiredArgsConstructor -@RestController -public class NoticeController { - - private final NoticeService noticeService; - - @Operation(summary = "공지사항 목록 조회", description = "공지사항 목록을 조회") - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "공지사항 목록이 성공적으로 조회됨", - content = @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = NoticeDetailInfo.class)))), - }) - @GetMapping() - public ResponseEntity> getNotices() { - return ResponseEntity.ok(noticeService.notices()); - } - - @Operation(summary = "공지사항 상세 조회", description = "특정 공지사항 상세를 조회") - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "공지사항 상세가 성공적으로 조회됨", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = NoticeDetailInfo.class))), - }) - @GetMapping("/{id}") - public ResponseEntity getNoticeDetail(@PathVariable("id") Long id) { - return ResponseEntity.ok(noticeService.notice(id)); - } -} diff --git a/src/main/java/com/yapp/artie/domain/notice/domain/Notice.java b/src/main/java/com/yapp/artie/domain/notice/domain/Notice.java index f8f1b525..d7d8a914 100644 --- a/src/main/java/com/yapp/artie/domain/notice/domain/Notice.java +++ b/src/main/java/com/yapp/artie/domain/notice/domain/Notice.java @@ -6,6 +6,8 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -20,12 +22,14 @@ public class Notice { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @NotBlank @Column(nullable = false) private String title; @Column(columnDefinition = "text") private String contents; + @NotNull @Column(nullable = false, updatable = false) @CreatedDate private LocalDateTime createdAt; diff --git a/src/main/java/com/yapp/artie/domain/notice/exception/NoticeNotFoundException.java b/src/main/java/com/yapp/artie/domain/notice/domain/NoticeNotFoundException.java similarity index 85% rename from src/main/java/com/yapp/artie/domain/notice/exception/NoticeNotFoundException.java rename to src/main/java/com/yapp/artie/domain/notice/domain/NoticeNotFoundException.java index d7b5263e..4bb7531b 100644 --- a/src/main/java/com/yapp/artie/domain/notice/exception/NoticeNotFoundException.java +++ b/src/main/java/com/yapp/artie/domain/notice/domain/NoticeNotFoundException.java @@ -1,4 +1,4 @@ -package com.yapp.artie.domain.notice.exception; +package com.yapp.artie.domain.notice.domain; import com.yapp.artie.global.common.exception.BusinessException; import com.yapp.artie.global.common.exception.ErrorCode; diff --git a/src/main/java/com/yapp/artie/domain/notice/dto/NoticeDetailInfo.java b/src/main/java/com/yapp/artie/domain/notice/dto/NoticeDetailInfo.java deleted file mode 100644 index e054535f..00000000 --- a/src/main/java/com/yapp/artie/domain/notice/dto/NoticeDetailInfo.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.yapp.artie.domain.notice.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import java.time.LocalDateTime; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@Schema(description = "공지사항 상세 Response") -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class NoticeDetailInfo { - - @Schema(description = "공지사항 아이디") - private Long id; - - @Schema(description = "공지일") - private LocalDateTime date; - - @Schema(description = "공지사항 제목") - private String title; - - @Schema(description = "공지 내용") - private String contents; - - public NoticeDetailInfo(Long id, LocalDateTime date, String title, String contents) { - this.id = id; - this.date = date; - this.title = title; - this.contents = contents; - } -} diff --git a/src/main/java/com/yapp/artie/domain/notice/repository/NoticeRepository.java b/src/main/java/com/yapp/artie/domain/notice/repository/NoticeRepository.java deleted file mode 100644 index 27f8dea2..00000000 --- a/src/main/java/com/yapp/artie/domain/notice/repository/NoticeRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.yapp.artie.domain.notice.repository; - -import com.yapp.artie.domain.notice.domain.Notice; -import com.yapp.artie.domain.notice.dto.NoticeDetailInfo; -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; - -@Repository -public interface NoticeRepository extends JpaRepository { - - @Query("select new com.yapp.artie.domain.notice.dto.NoticeDetailInfo(n.id, n.createdAt, n.title, n.contents) from Notice n") - List findNoticeDto(); -} diff --git a/src/main/java/com/yapp/artie/domain/notice/service/NoticeService.java b/src/main/java/com/yapp/artie/domain/notice/service/NoticeService.java deleted file mode 100644 index 84d82719..00000000 --- a/src/main/java/com/yapp/artie/domain/notice/service/NoticeService.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.yapp.artie.domain.notice.service; - -import com.yapp.artie.domain.notice.domain.Notice; -import com.yapp.artie.domain.notice.dto.NoticeDetailInfo; -import com.yapp.artie.domain.notice.exception.NoticeNotFoundException; -import com.yapp.artie.domain.notice.repository.NoticeRepository; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@Transactional(readOnly = true) -@RequiredArgsConstructor -public class NoticeService { - - private final NoticeRepository noticeRepository; - - public List notices() { - return noticeRepository.findNoticeDto(); - } - - public NoticeDetailInfo notice(Long id) { - Notice notice = noticeRepository.findById(id).orElseThrow(NoticeNotFoundException::new); - return new NoticeDetailInfo( - notice.getId(), - notice.getCreatedAt(), - notice.getTitle(), - notice.getContents() - ); - } -} diff --git a/src/main/java/com/yapp/artie/domain/user/adapter/in/web/GetUserController.java b/src/main/java/com/yapp/artie/domain/user/adapter/in/web/GetUserController.java index b5ccc51e..a82519f1 100644 --- a/src/main/java/com/yapp/artie/domain/user/adapter/in/web/GetUserController.java +++ b/src/main/java/com/yapp/artie/domain/user/adapter/in/web/GetUserController.java @@ -14,10 +14,8 @@ import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; @WebAdapter -@RestController @RequestMapping("/user") @RequiredArgsConstructor class GetUserController { diff --git a/src/main/java/com/yapp/artie/domain/user/adapter/in/web/GetUserThumbnailController.java b/src/main/java/com/yapp/artie/domain/user/adapter/in/web/GetUserThumbnailController.java index 5eabf276..fbacfbba 100644 --- a/src/main/java/com/yapp/artie/domain/user/adapter/in/web/GetUserThumbnailController.java +++ b/src/main/java/com/yapp/artie/domain/user/adapter/in/web/GetUserThumbnailController.java @@ -15,10 +15,8 @@ import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; @WebAdapter -@RestController @RequestMapping("/user") @RequiredArgsConstructor class GetUserThumbnailController { diff --git a/src/main/java/com/yapp/artie/domain/user/adapter/in/web/RegisterUserController.java b/src/main/java/com/yapp/artie/domain/user/adapter/in/web/RegisterUserController.java index c742dee7..39cffaf2 100644 --- a/src/main/java/com/yapp/artie/domain/user/adapter/in/web/RegisterUserController.java +++ b/src/main/java/com/yapp/artie/domain/user/adapter/in/web/RegisterUserController.java @@ -19,10 +19,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; @WebAdapter -@RestController @RequestMapping("/user") @RequiredArgsConstructor class RegisterUserController { diff --git a/src/main/java/com/yapp/artie/domain/user/adapter/in/web/RenameUserController.java b/src/main/java/com/yapp/artie/domain/user/adapter/in/web/RenameUserController.java index f4335f3a..a0389ad9 100644 --- a/src/main/java/com/yapp/artie/domain/user/adapter/in/web/RenameUserController.java +++ b/src/main/java/com/yapp/artie/domain/user/adapter/in/web/RenameUserController.java @@ -19,11 +19,9 @@ import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; @WebAdapter -@RestController @RequestMapping("/user") @RequiredArgsConstructor class RenameUserController { diff --git a/src/main/java/com/yapp/artie/domain/user/adapter/in/web/UserWithdrawalController.java b/src/main/java/com/yapp/artie/domain/user/adapter/in/web/UserWithdrawalController.java index 29df60c1..f622ad05 100644 --- a/src/main/java/com/yapp/artie/domain/user/adapter/in/web/UserWithdrawalController.java +++ b/src/main/java/com/yapp/artie/domain/user/adapter/in/web/UserWithdrawalController.java @@ -14,10 +14,8 @@ import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; @WebAdapter -@RestController @RequestMapping("/user") @RequiredArgsConstructor class UserWithdrawalController { diff --git a/src/main/java/com/yapp/artie/global/common/annotation/WebAdapter.java b/src/main/java/com/yapp/artie/global/common/annotation/WebAdapter.java index d4d38c77..f9560fb0 100644 --- a/src/main/java/com/yapp/artie/global/common/annotation/WebAdapter.java +++ b/src/main/java/com/yapp/artie/global/common/annotation/WebAdapter.java @@ -5,13 +5,14 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RestController; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) -@Component +@RestController public @interface WebAdapter { - @AliasFor(annotation = Component.class) + @AliasFor(annotation = Controller.class) String value() default ""; } diff --git a/src/test/java/com/yapp/artie/domain/notice/adapter/in/web/GetNoticeDetailControllerTest.java b/src/test/java/com/yapp/artie/domain/notice/adapter/in/web/GetNoticeDetailControllerTest.java new file mode 100644 index 00000000..8089d1a7 --- /dev/null +++ b/src/test/java/com/yapp/artie/domain/notice/adapter/in/web/GetNoticeDetailControllerTest.java @@ -0,0 +1,32 @@ +package com.yapp.artie.domain.notice.adapter.in.web; + +import static com.yapp.artie.common.UserTestData.defaultUser; +import static org.mockito.BDDMockito.then; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.yapp.artie.common.BaseControllerIntegrationTest; +import com.yapp.artie.domain.notice.application.port.in.GetNoticeDetailQuery; +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.http.MediaType; + +@WebMvcTest(controllers = GetNoticeDetailController.class) +class GetNoticeDetailControllerTest extends BaseControllerIntegrationTest { + + @MockBean + private GetNoticeDetailQuery getNoticeDetailQuery; + + @Test + void getNoticeDetail() throws Exception { + givenUserByReference(defaultUser().build()); + Long noticeId = 1L; + + mvc.perform(get("/notice/{noticeId}", noticeId).accept(MediaType.APPLICATION_JSON) + .header("Authorization", "sample")) + .andExpect(status().isOk()); + + then(getNoticeDetailQuery).should().loadNoticeDetail(noticeId); + } +} \ No newline at end of file diff --git a/src/test/java/com/yapp/artie/domain/notice/adapter/in/web/GetNoticeListControllerTest.java b/src/test/java/com/yapp/artie/domain/notice/adapter/in/web/GetNoticeListControllerTest.java new file mode 100644 index 00000000..1ce307d3 --- /dev/null +++ b/src/test/java/com/yapp/artie/domain/notice/adapter/in/web/GetNoticeListControllerTest.java @@ -0,0 +1,31 @@ +package com.yapp.artie.domain.notice.adapter.in.web; + +import static com.yapp.artie.common.UserTestData.defaultUser; +import static org.mockito.BDDMockito.then; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.yapp.artie.common.BaseControllerIntegrationTest; +import com.yapp.artie.domain.notice.application.port.in.GetNoticeListQuery; +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.http.MediaType; + +@WebMvcTest(controllers = GetNoticeListController.class) +class GetNoticeListControllerTest extends BaseControllerIntegrationTest { + + @MockBean + private GetNoticeListQuery getNoticeListQuery; + + @Test + void getNotices() throws Exception { + givenUserByReference(defaultUser().build()); + + mvc.perform(get("/notice").accept(MediaType.APPLICATION_JSON) + .header("Authorization", "sample")) + .andExpect(status().isOk()); + + then(getNoticeListQuery).should().loadNoticeList(); + } +} \ No newline at end of file diff --git a/src/test/java/com/yapp/artie/domain/notice/adapter/out/persistence/NoticePersistenceAdapterTest.java b/src/test/java/com/yapp/artie/domain/notice/adapter/out/persistence/NoticePersistenceAdapterTest.java new file mode 100644 index 00000000..243ee70e --- /dev/null +++ b/src/test/java/com/yapp/artie/domain/notice/adapter/out/persistence/NoticePersistenceAdapterTest.java @@ -0,0 +1,60 @@ +package com.yapp.artie.domain.notice.adapter.out.persistence; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.yapp.artie.domain.notice.domain.Notice; +import com.yapp.artie.domain.notice.domain.NoticeNotFoundException; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.jdbc.Sql; + +@DataJpaTest +@Import({NoticePersistenceAdapter.class}) +@AutoConfigureTestDatabase(replace = Replace.NONE) +class NoticePersistenceAdapterTest { + + @Autowired + NoticePersistenceAdapter noticePersistenceAdapter; + + @Autowired + NoticeRepository noticeRepository; + + @Test + @Sql("NoticePersistenceAdapterTest.sql") + void loadNoticeList_모든_공지_조회() { + List notices = noticePersistenceAdapter.loadNoticeList(); + + assertThat(notices.size()).isEqualTo(6); + assertThat(notices.get(0).getId()).isEqualTo(1L); + } + + @Test + void loadNoticeList_공지가_없을_경우_빈_리스트를_반환한다() { + List notices = noticePersistenceAdapter.loadNoticeList(); + + assertThat(notices.size()).isEqualTo(0); + } + + + @Test + @Sql("NoticePersistenceAdapterTest.sql") + void loadNoticeDetail_공지_ID를_이용해서_공지_세부_내용을_조회한다() { + Notice notice = noticePersistenceAdapter.loadNoticeDetail(1L); + + assertThat(notice.getId()).isEqualTo(1L); + } + + @Test + void loadNoticeDetail_공지_ID를_찾지_못한_경우_예외를_발생한다() { + assertThatThrownBy(() -> { + noticePersistenceAdapter.loadNoticeDetail(1L); + }).isInstanceOf( + NoticeNotFoundException.class); + } +} \ No newline at end of file diff --git a/src/test/java/com/yapp/artie/domain/notice/application/service/GetNoticeDetailServiceTest.java b/src/test/java/com/yapp/artie/domain/notice/application/service/GetNoticeDetailServiceTest.java new file mode 100644 index 00000000..0173a114 --- /dev/null +++ b/src/test/java/com/yapp/artie/domain/notice/application/service/GetNoticeDetailServiceTest.java @@ -0,0 +1,49 @@ +package com.yapp.artie.domain.notice.application.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +import com.yapp.artie.domain.notice.application.port.in.GetNoticeDetailResponse; +import com.yapp.artie.domain.notice.application.port.out.LoadNoticePort; +import com.yapp.artie.domain.notice.domain.Notice; +import com.yapp.artie.domain.notice.domain.NoticeNotFoundException; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class GetNoticeDetailServiceTest { + + private final LoadNoticePort loadNoticePort = Mockito.mock(LoadNoticePort.class); + private final GetNoticeDetailService getNoticeDetailService = new GetNoticeDetailService( + loadNoticePort); + + @Test + void loadNoticeDetail_공지_ID를_이용해서_공지_세부_내용을_조회한다() { + Notice notice = Notice.create("공지 제목 1", "공지 내용 1"); + givenNotice(notice); + + GetNoticeDetailResponse noticeDetailResponse = getNoticeDetailService.loadNoticeDetail( + notice.getId()); + + assertThat(noticeDetailResponse.getId()).isEqualTo(notice.getId()); + } + + @Test + void loadNoticeDetail_공지_ID를_찾지_못한_경우_예외를_발생한다() { + givenNoticeByIdWillFail(); + + assertThatThrownBy(() -> getNoticeDetailService.loadNoticeDetail(1L)).isInstanceOf( + NoticeNotFoundException.class); + } + + private void givenNotice(Notice notice) { + given(loadNoticePort.loadNoticeDetail(any())) + .willReturn(notice); + } + + private void givenNoticeByIdWillFail() { + given(loadNoticePort.loadNoticeDetail(any())) + .willThrow(NoticeNotFoundException.class); + } +} \ No newline at end of file diff --git a/src/test/java/com/yapp/artie/domain/notice/application/service/GetNoticeListServiceTest.java b/src/test/java/com/yapp/artie/domain/notice/application/service/GetNoticeListServiceTest.java new file mode 100644 index 00000000..92791558 --- /dev/null +++ b/src/test/java/com/yapp/artie/domain/notice/application/service/GetNoticeListServiceTest.java @@ -0,0 +1,45 @@ +package com.yapp.artie.domain.notice.application.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; + +import com.yapp.artie.domain.notice.application.port.in.GetNoticeDetailResponse; +import com.yapp.artie.domain.notice.application.port.out.LoadNoticePort; +import com.yapp.artie.domain.notice.domain.Notice; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class GetNoticeListServiceTest { + + private final LoadNoticePort loadNoticePort = Mockito.mock(LoadNoticePort.class); + private final GetNoticeListService getNoticeListService = new GetNoticeListService( + loadNoticePort); + + @Test + void loadNoticeList_모든_공지_조회() { + List notices = Arrays.asList(Notice.create("공지 제목 1", "공지 내용 1"), + Notice.create("공지 제목 2", null)); + givenNoticeList(notices); + + List getNoticeDetailResponses = getNoticeListService.loadNoticeList(); + + assertThat(getNoticeDetailResponses.size()).isEqualTo(2); + assertThat(getNoticeDetailResponses.get(0).getTitle()).isEqualTo("공지 제목 1"); + } + + @Test + void loadNoticeList_공지가_없을_경우_빈_리스트를_반환한다() { + givenNoticeList(new ArrayList<>()); + + List noticeDetailResponses = getNoticeListService.loadNoticeList(); + + assertThat(noticeDetailResponses.size()).isEqualTo(0); + } + + private void givenNoticeList(List notices) { + given(loadNoticePort.loadNoticeList()).willReturn(notices); + } +} \ No newline at end of file diff --git a/src/test/java/com/yapp/artie/domain/notice/service/NoticeServiceTest.java b/src/test/java/com/yapp/artie/domain/notice/service/NoticeServiceTest.java deleted file mode 100644 index 708eae63..00000000 --- a/src/test/java/com/yapp/artie/domain/notice/service/NoticeServiceTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.yapp.artie.domain.notice.service; - - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import com.yapp.artie.domain.notice.domain.Notice; -import com.yapp.artie.domain.notice.dto.NoticeDetailInfo; -import com.yapp.artie.domain.notice.exception.NoticeNotFoundException; -import java.util.List; -import javax.persistence.EntityManager; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.transaction.annotation.Transactional; - -@Transactional -@SpringBootTest -class NoticeServiceTest { - - @Autowired - EntityManager em; - - @Autowired - NoticeService noticeService; - - @Test - public void notices_공지사항_목록을_조회한다() throws Exception { - for (int i = 0; i < 4; i++) { - Notice notice = Notice.create("test " + i, "sample data"); - em.persist(notice); - } - List notices = noticeService.notices(); - assertThat(notices.size()).isEqualTo(4); - } - - @Test - public void notice_공지사항_상세를_조회한다() throws Exception { - String expectedContents = "sample data"; - Notice notice = Notice.create("test1", expectedContents); - em.persist(notice); - NoticeDetailInfo detail = noticeService.notice(notice.getId()); - assertThat(detail.getContents()).isEqualTo(expectedContents); - } - - @Test - public void notice_존재하지_않는_공지사항을_조회_시도할_경우_예외를_발생() throws Exception { - String expectedContents = "sample data"; - Notice notice = Notice.create("test1", expectedContents); - em.persist(notice); - assertThatThrownBy(() -> { - noticeService.notice(22L); - }).isInstanceOf(NoticeNotFoundException.class); - } -} \ No newline at end of file diff --git a/src/test/resources/com/yapp/artie/domain/notice/adapter/out/persistence/NoticePersistenceAdapterTest.sql b/src/test/resources/com/yapp/artie/domain/notice/adapter/out/persistence/NoticePersistenceAdapterTest.sql new file mode 100644 index 00000000..b1557e47 --- /dev/null +++ b/src/test/resources/com/yapp/artie/domain/notice/adapter/out/persistence/NoticePersistenceAdapterTest.sql @@ -0,0 +1,12 @@ +INSERT INTO `notice` (id, contents, created_at, title) +VALUES (1, '아르티가 업데이트 되었어요~ 1', '2023-04-01 10:58:50', '공지사항 제목1'); +INSERT INTO `notice` (id, contents, created_at, title) +VALUES (2, '아르티가 업데이트 되었어요~ 2', '2023-04-02 12:58:50', '공지사항 제목2'); +INSERT INTO `notice` (id, contents, created_at, title) +VALUES (3, '아르티가 업데이트 되었어요~ 3', '2023-04-03 15:58:50', '공지사항 제목3'); +INSERT INTO `notice` (id, contents, created_at, title) +VALUES (4, '아르티가 업데이트 되었어요~ 4', '2023-04-04 19:58:50', '공지사항 제목4'); +INSERT INTO `notice` (id, contents, created_at, title) +VALUES (5, '아르티가 업데이트 되었어요~ 5', '2023-04-05 08:58:50', '공지사항 제목5'); +INSERT INTO `notice` (id, contents, created_at, title) +VALUES (6, '아르티가 업데이트 되었어요~ 6', '2023-04-20 20:58:50', '공지사항 제목6'); \ No newline at end of file