diff --git a/deploy/local.init.sql b/deploy/local.init.sql index 8ac668a..eab3437 100644 --- a/deploy/local.init.sql +++ b/deploy/local.init.sql @@ -27,4 +27,12 @@ VALUES (default, null, null, 'STAFF', '2024-12-11', 2, 2, 1, 1); INSERT INTO user_club (id, created_date, updated_date, club_role, join_date, match_count, schedule_count, club_id, user_id) VALUES (default, null, null, 'STAFF', '2024-12-11', 2, 2, 3, 3); INSERT INTO user_club (id, created_date, updated_date, club_role, join_date, match_count, schedule_count, club_id, user_id) -VALUES (default, null, null, 'STAFF', '2024-12-11', 2, 2, 3, 4); \ No newline at end of file +VALUES (default, null, null, 'STAFF', '2024-12-11', 2, 2, 3, 4); + +-- notice +INSERT INTO notice (id, created_date, updated_date, club_id, title, content) +values (default, '2024-12-10', null, 1, 'title', 'content'); +INSERT INTO notice (id, created_date, updated_date, club_id, title, content) +values (default, '2024-12-11', null, 1, 'title2', 'content2'); +INSERT INTO notice (id, created_date, updated_date, club_id, title, content) +values (default, '2024-12-12', null, 1, 'title3', 'content4'); \ No newline at end of file diff --git a/src/main/java/com/example/moim/club/controller/NoticeController.java b/src/main/java/com/example/moim/club/controller/NoticeController.java index 23587ab..54d5eab 100644 --- a/src/main/java/com/example/moim/club/controller/NoticeController.java +++ b/src/main/java/com/example/moim/club/controller/NoticeController.java @@ -1,43 +1,45 @@ package com.example.moim.club.controller; import com.example.moim.club.dto.request.NoticeInput; -import com.example.moim.club.dto.request.NoticeOutput; +import com.example.moim.club.dto.response.NoticeOutput; import com.example.moim.club.service.NoticeCommandService; -import com.example.moim.club.service.NoticeCommandServiceImpl; import com.example.moim.club.service.NoticeQueryService; import com.example.moim.global.exception.BaseResponse; import com.example.moim.global.exception.ResponseCode; import com.example.moim.user.dto.UserDetailsImpl; +import com.example.moim.user.entity.User; +import com.example.moim.user.repository.UserRepository; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Slice; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; -import java.util.List; @RestController @RequiredArgsConstructor public class NoticeController implements NoticeControllerDocs { private final NoticeCommandService noticeCommandService; private final NoticeQueryService noticeQueryService; + private final UserRepository userRepository; - /** - * FIXME: 응답 값이 무조건 있어야 함. user 의 권한 체크는 안해도 되나? - */ - @PostMapping("/notice") - public BaseResponse noticeSave(NoticeInput noticeInput, @AuthenticationPrincipal UserDetailsImpl userDetailsImpl) { - noticeCommandService.saveNotice(noticeInput); - return BaseResponse.onSuccess(null, ResponseCode.OK); + @PostMapping("/notice/{clubId}") + public BaseResponse saveNotice(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @ModelAttribute NoticeInput noticeInput, @PathVariable("clubId") Long clubId) { +// public BaseResponse saveNotice(@ModelAttribute NoticeInput noticeInput, @PathVariable("clubId") Long clubId) { + /** + * FIXME: 컨트롤러 테스트용 코드 + */ +// User user = userRepository.findById(3L).get(); + NoticeOutput noticeOutput = noticeCommandService.saveNotice(userDetailsImpl.getUser(), noticeInput, clubId); + return BaseResponse.onSuccess(noticeOutput, ResponseCode.OK); } - /** - * FIXME: 공지를 시간 순으로 정렬하지 않아도 되나? - * @return - */ @GetMapping("/notice/{clubId}") - public BaseResponse> noticeSave(@PathVariable Long clubId, @AuthenticationPrincipal UserDetailsImpl userDetailsImpl) { - return BaseResponse.onSuccess(noticeQueryService.findNotice(clubId), ResponseCode.OK); + public BaseResponse> findNotices(@PathVariable Long clubId, @AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @RequestParam("cursorId") Long cursorId) { +// public BaseResponse> findNotices(@PathVariable Long clubId, @RequestParam(value = "cursorId", required = false) Long cursorId) { + /** + * FIXME: 컨트롤러 테스트용 코드 + */ +// User user = userRepository.findById(1L).get(); + return BaseResponse.onSuccess(noticeQueryService.findNotice(userDetailsImpl.getUser(), clubId, cursorId), ResponseCode.OK); } } diff --git a/src/main/java/com/example/moim/club/controller/NoticeControllerDocs.java b/src/main/java/com/example/moim/club/controller/NoticeControllerDocs.java index eb7a706..cd0f5c6 100644 --- a/src/main/java/com/example/moim/club/controller/NoticeControllerDocs.java +++ b/src/main/java/com/example/moim/club/controller/NoticeControllerDocs.java @@ -1,21 +1,27 @@ package com.example.moim.club.controller; import com.example.moim.club.dto.request.NoticeInput; -import com.example.moim.club.dto.request.NoticeOutput; +import com.example.moim.club.dto.response.NoticeOutput; import com.example.moim.global.exception.BaseResponse; import com.example.moim.user.dto.UserDetailsImpl; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; import java.util.List; @Tag(name = "모임 공지 api", description = "모임(club) 공지 api 분리") public interface NoticeControllerDocs { @Operation(summary = "공지 생성") - BaseResponse noticeSave(NoticeInput noticeInput, @AuthenticationPrincipal UserDetailsImpl userDetailsImpl); + BaseResponse saveNotice(@AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @ModelAttribute NoticeInput noticeInput, @PathVariable("clubId") Long clubId); +// BaseResponse saveNotice(@ModelAttribute NoticeInput noticeInput, @PathVariable Long clubId); - @Operation(summary = "공지 조회") - BaseResponse> noticeSave(@PathVariable Long clubId, @AuthenticationPrincipal UserDetailsImpl userDetailsImpl); + @Operation(summary = "공지 조회, 최초 요청 시 cursorId null 로 보내기") + BaseResponse> findNotices(@PathVariable Long clubId, @AuthenticationPrincipal UserDetailsImpl userDetailsImpl, @RequestParam Long cursorId); +// BaseResponse> findNotices(@PathVariable Long clubId, @RequestParam Long cursorId); } diff --git a/src/main/java/com/example/moim/club/dto/request/NoticeInput.java b/src/main/java/com/example/moim/club/dto/request/NoticeInput.java index 64a8859..a9a27bd 100644 --- a/src/main/java/com/example/moim/club/dto/request/NoticeInput.java +++ b/src/main/java/com/example/moim/club/dto/request/NoticeInput.java @@ -7,13 +7,11 @@ @Data @NoArgsConstructor public class NoticeInput { - private Long clubId; private String title; private String content; @Builder - public NoticeInput(Long clubId, String title, String content) { - this.clubId = clubId; + public NoticeInput(String title, String content) { this.title = title; this.content = content; } diff --git a/src/main/java/com/example/moim/club/dto/request/NoticeOutput.java b/src/main/java/com/example/moim/club/dto/response/NoticeOutput.java similarity index 92% rename from src/main/java/com/example/moim/club/dto/request/NoticeOutput.java rename to src/main/java/com/example/moim/club/dto/response/NoticeOutput.java index 5e4267e..3633b87 100644 --- a/src/main/java/com/example/moim/club/dto/request/NoticeOutput.java +++ b/src/main/java/com/example/moim/club/dto/response/NoticeOutput.java @@ -1,4 +1,4 @@ -package com.example.moim.club.dto.request; +package com.example.moim.club.dto.response; import com.example.moim.club.entity.Notice; import lombok.Data; diff --git a/src/main/java/com/example/moim/club/repository/ClubRepositoryImpl.java b/src/main/java/com/example/moim/club/repository/ClubRepositoryImpl.java index f9908d7..7fd19ac 100644 --- a/src/main/java/com/example/moim/club/repository/ClubRepositoryImpl.java +++ b/src/main/java/com/example/moim/club/repository/ClubRepositoryImpl.java @@ -18,7 +18,7 @@ import static com.example.moim.club.entity.QClubSearch.clubSearch; import static org.springframework.util.StringUtils.hasText; -public class ClubRepositoryImpl implements ClubRepositoryCustom{ +public class ClubRepositoryImpl implements ClubRepositoryCustom { private final JPAQueryFactory queryFactory; public ClubRepositoryImpl(EntityManager em) { diff --git a/src/main/java/com/example/moim/club/repository/NoticeRepository.java b/src/main/java/com/example/moim/club/repository/NoticeRepository.java index b2547ad..0d38aaa 100644 --- a/src/main/java/com/example/moim/club/repository/NoticeRepository.java +++ b/src/main/java/com/example/moim/club/repository/NoticeRepository.java @@ -2,10 +2,13 @@ import com.example.moim.club.entity.Club; import com.example.moim.club.entity.Notice; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; -public interface NoticeRepository extends JpaRepository { - List findByClub(Club club); +public interface NoticeRepository extends JpaRepository, NoticeRepositoryCustom { +// Page findByClub(Club club, Pageable pageable); + } diff --git a/src/main/java/com/example/moim/club/repository/NoticeRepositoryCustom.java b/src/main/java/com/example/moim/club/repository/NoticeRepositoryCustom.java new file mode 100644 index 0000000..fb80b4a --- /dev/null +++ b/src/main/java/com/example/moim/club/repository/NoticeRepositoryCustom.java @@ -0,0 +1,11 @@ +package com.example.moim.club.repository; + +import com.example.moim.club.entity.Club; +import com.example.moim.club.entity.Notice; + +import java.util.List; + +public interface NoticeRepositoryCustom { + List findByCursor(Long cursor, int size, Club club); +} + diff --git a/src/main/java/com/example/moim/club/repository/NoticeRepositoryImpl.java b/src/main/java/com/example/moim/club/repository/NoticeRepositoryImpl.java new file mode 100644 index 0000000..94f9e79 --- /dev/null +++ b/src/main/java/com/example/moim/club/repository/NoticeRepositoryImpl.java @@ -0,0 +1,42 @@ +package com.example.moim.club.repository; + +import com.example.moim.club.entity.Club; +import com.example.moim.club.entity.Notice; +import com.example.moim.club.entity.QNotice; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +public class NoticeRepositoryImpl implements NoticeRepositoryCustom { + + private final JPAQueryFactory queryFactory; + + public NoticeRepositoryImpl(EntityManager em) { + this.queryFactory = new JPAQueryFactory(em); + } + + @Override + public List findByCursor(Long cursor, int size, Club club) { + return queryFactory + .selectFrom(QNotice.notice) + .where( + ltCursor(cursor), + clubEq(club) + ) + .orderBy(QNotice.notice.id.desc()) + .limit(size + 1) // hasNext 판단용으로 1개 더 + .fetch(); + } + + private BooleanExpression ltCursor(Long cursor) { + return cursor != null ? QNotice.notice.id.lt(cursor) : null; + } + + private BooleanExpression clubEq(Club club) { + return club != null ? QNotice.notice.club.eq(club) : null; + } +} + diff --git a/src/main/java/com/example/moim/club/service/ClubCommandServiceImpl.java b/src/main/java/com/example/moim/club/service/ClubCommandServiceImpl.java index 7dadc51..88cbcc6 100644 --- a/src/main/java/com/example/moim/club/service/ClubCommandServiceImpl.java +++ b/src/main/java/com/example/moim/club/service/ClubCommandServiceImpl.java @@ -83,6 +83,9 @@ public UserClubOutput saveClubUser(User user, ClubUserSaveInput clubUserSaveInpu } club.plusMemberCount(); UserClub userClub = userClubRepository.save(UserClub.createUserClub(user, club)); + /** + * TODO: 알림 보내는 것 새로운 방식에 맞춰서 다시 구현해야 함 + */ eventPublisher.publishEvent(new ClubJoinEvent(user, club)); return new UserClubOutput(userClub); } diff --git a/src/main/java/com/example/moim/club/service/NoticeCommandService.java b/src/main/java/com/example/moim/club/service/NoticeCommandService.java index 4d42d48..f6c3e0b 100644 --- a/src/main/java/com/example/moim/club/service/NoticeCommandService.java +++ b/src/main/java/com/example/moim/club/service/NoticeCommandService.java @@ -1,7 +1,9 @@ package com.example.moim.club.service; import com.example.moim.club.dto.request.NoticeInput; +import com.example.moim.club.dto.response.NoticeOutput; +import com.example.moim.user.entity.User; public interface NoticeCommandService { - void saveNotice(NoticeInput noticeInput); + NoticeOutput saveNotice(User user, NoticeInput noticeInput, Long clubId); } diff --git a/src/main/java/com/example/moim/club/service/NoticeCommandServiceImpl.java b/src/main/java/com/example/moim/club/service/NoticeCommandServiceImpl.java index 8423415..b42a3f5 100644 --- a/src/main/java/com/example/moim/club/service/NoticeCommandServiceImpl.java +++ b/src/main/java/com/example/moim/club/service/NoticeCommandServiceImpl.java @@ -1,32 +1,45 @@ package com.example.moim.club.service; import com.example.moim.club.dto.request.NoticeInput; -import com.example.moim.club.dto.request.NoticeOutput; +import com.example.moim.club.dto.response.NoticeOutput; +import com.example.moim.club.entity.Club; import com.example.moim.club.entity.Notice; +import com.example.moim.club.entity.UserClub; import com.example.moim.club.exception.advice.ClubControllerAdvice; import com.example.moim.club.repository.ClubRepository; import com.example.moim.club.repository.NoticeRepository; +import com.example.moim.club.repository.UserClubRepository; +import com.example.moim.global.enums.ClubRole; import com.example.moim.global.exception.ResponseCode; +import com.example.moim.user.entity.User; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import java.util.List; - @Slf4j @Service @RequiredArgsConstructor public class NoticeCommandServiceImpl implements NoticeCommandService { private final NoticeRepository noticeRepository; private final ClubRepository clubRepository; + private final UserClubRepository userClubRepository; + + public NoticeOutput saveNotice(User user, NoticeInput noticeInput, Long clubId) { + log.debug("saveNotice 진입"); + Club club = clubRepository.findById(clubId).orElseThrow(() -> new ClubControllerAdvice(ResponseCode.CLUB_NOT_FOUND)); + UserClub userClub = userClubRepository.findByClubAndUser(club, user).orElseThrow(() -> new ClubControllerAdvice(ResponseCode.CLUB_USER_NOT_FOUND)); + + if (!userClub.getClubRole().equals(ClubRole.STAFF)) { // 공지 등록 권한 확인 + log.debug("권한 오류"); + throw new ClubControllerAdvice(ResponseCode.CLUB_PERMISSION_DENIED); + } + + Notice notice = noticeRepository.save(Notice.createNotice(club, noticeInput.getTitle(), noticeInput.getContent())); + + /** + * TODO: 알림 보내는 부분 구현해야함 + */ - /** - * transaction 필요하지 않음. save 작업 하나만 이루어지고, 나머지는 다 조회 연산이므로 - * save 함수 내부에 이미 transaction 적용되어 있음 - * @param noticeInput - */ - public void saveNotice(NoticeInput noticeInput) { - noticeRepository.save(Notice.createNotice(clubRepository.findById(noticeInput.getClubId()).orElseThrow(() -> new ClubControllerAdvice(ResponseCode.CLUB_NOT_FOUND)), - noticeInput.getTitle(), noticeInput.getContent())); + return new NoticeOutput(notice); } } diff --git a/src/main/java/com/example/moim/club/service/NoticeQueryService.java b/src/main/java/com/example/moim/club/service/NoticeQueryService.java index 214db10..a763238 100644 --- a/src/main/java/com/example/moim/club/service/NoticeQueryService.java +++ b/src/main/java/com/example/moim/club/service/NoticeQueryService.java @@ -1,9 +1,11 @@ package com.example.moim.club.service; -import com.example.moim.club.dto.request.NoticeOutput; +import com.example.moim.club.dto.response.NoticeOutput; +import com.example.moim.user.entity.User; +import org.springframework.data.domain.Slice; import java.util.List; public interface NoticeQueryService { - List findNotice(Long clubId); + Slice findNotice(User user, Long clubId, Long cursorId); } diff --git a/src/main/java/com/example/moim/club/service/NoticeQueryServiceImpl.java b/src/main/java/com/example/moim/club/service/NoticeQueryServiceImpl.java index 02ce284..719afd4 100644 --- a/src/main/java/com/example/moim/club/service/NoticeQueryServiceImpl.java +++ b/src/main/java/com/example/moim/club/service/NoticeQueryServiceImpl.java @@ -1,11 +1,18 @@ package com.example.moim.club.service; -import com.example.moim.club.dto.request.NoticeOutput; +import com.example.moim.club.dto.response.NoticeOutput; +import com.example.moim.club.entity.Club; +import com.example.moim.club.entity.Notice; +import com.example.moim.club.entity.UserClub; import com.example.moim.club.exception.advice.ClubControllerAdvice; import com.example.moim.club.repository.ClubRepository; import com.example.moim.club.repository.NoticeRepository; +import com.example.moim.club.repository.UserClubRepository; +import com.example.moim.global.enums.ClubRole; import com.example.moim.global.exception.ResponseCode; +import com.example.moim.user.entity.User; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.*; import org.springframework.stereotype.Service; import java.util.List; @@ -14,10 +21,29 @@ @RequiredArgsConstructor public class NoticeQueryServiceImpl implements NoticeQueryService { + private static final int SIZE = 20; + private final NoticeRepository noticeRepository; private final ClubRepository clubRepository; + private final UserClubRepository userClubRepository; + + public Slice findNotice(User user, Long clubId, Long cursorId) { + // 가입된 회원인지 확인, 가입되지 않은 회원이면 공지 열람 권한이 없는 것 + Club club = clubRepository.findById(clubId).orElseThrow(() -> new ClubControllerAdvice(ResponseCode.CLUB_NOT_FOUND)); + UserClub userClub = userClubRepository.findByClubAndUser(club, user).orElseThrow(() -> new ClubControllerAdvice(ResponseCode.CLUB_USER_NOT_FOUND)); + + List notices = noticeRepository.findByCursor(cursorId, SIZE, club); + + boolean hasNext = notices.size() > SIZE; + + if (hasNext) { + notices.remove(SIZE); + } + + List outputs = notices.stream() + .map(NoticeOutput::new) + .toList(); - public List findNotice(Long clubId) { - return noticeRepository.findByClub(clubRepository.findById(clubId).orElseThrow(() -> new ClubControllerAdvice(ResponseCode.CLUB_NOT_FOUND))).stream().map(NoticeOutput::new).toList(); + return new SliceImpl<>(outputs, PageRequest.of(0, SIZE), hasNext); } } diff --git a/src/main/java/com/example/moim/config/security/SecurityConfig.java b/src/main/java/com/example/moim/config/security/SecurityConfig.java index 3705755..3fed711 100644 --- a/src/main/java/com/example/moim/config/security/SecurityConfig.java +++ b/src/main/java/com/example/moim/config/security/SecurityConfig.java @@ -71,6 +71,8 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { httpSecurity.csrf(csrf -> csrf .ignoringRequestMatchers("/club/**") ); + httpSecurity.csrf(csrf -> csrf.ignoringRequestMatchers("/notice/**")); + //From 로그인 방식 disable httpSecurity.formLogin(AbstractHttpConfigurer::disable); //http basic 인증 방식 disable diff --git a/src/test/java/com/example/moim/club/service/NoticeCommandServiceImplTest.java b/src/test/java/com/example/moim/club/service/NoticeCommandServiceImplTest.java index 2fc715f..4547a71 100644 --- a/src/test/java/com/example/moim/club/service/NoticeCommandServiceImplTest.java +++ b/src/test/java/com/example/moim/club/service/NoticeCommandServiceImplTest.java @@ -2,11 +2,13 @@ import com.example.moim.club.dto.request.ClubInput; import com.example.moim.club.dto.request.NoticeInput; -import com.example.moim.club.dto.request.NoticeOutput; import com.example.moim.club.entity.*; import com.example.moim.club.repository.ClubRepository; import com.example.moim.club.repository.NoticeRepository; +import com.example.moim.club.repository.UserClubRepository; import com.example.moim.global.enums.*; +import com.example.moim.user.dto.SignupInput; +import com.example.moim.user.entity.User; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -15,9 +17,10 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.multipart.MultipartFile; -import java.util.List; +import java.time.LocalDateTime; import java.util.Optional; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; @@ -31,11 +34,15 @@ class NoticeCommandServiceImplTest { private NoticeRepository noticeRepository; @Mock private ClubRepository clubRepository; + @Mock + private UserClubRepository userClubRepository; @InjectMocks private NoticeCommandServiceImpl noticeCommandServiceImpl; private ClubInput clubInput; private NoticeInput noticeInput; + private SignupInput signupInput; + @BeforeEach void init() { // Club @@ -58,7 +65,17 @@ void init() { .clubPassword(clubPassword).profileImg(profileImg).mainUniformColor(mainUniformColor).subUniformColor(subUniformColor).build(); // Notice - this.noticeInput = NoticeInput.builder().title("notice title").content("notice content").clubId(1L).build(); + this.noticeInput = NoticeInput.builder().title("notice title").content("notice content").build(); + + // User + this.signupInput = SignupInput.builder() + .email("email") + .phone("phone") + .birthday("2000-08-28") + .name("name") + .password("password") + .gender(Gender.WOMAN.getKoreanName()) + .build(); } @Test @DisplayName("공지를 저장할 수 있다") @@ -66,10 +83,15 @@ void saveNotice() { //given Club club = Club.createClub(clubInput, null); Notice notice = Notice.createNotice(club, noticeInput.getTitle(), noticeInput.getContent()); + // notice - createAt 강제로 주입하기 + ReflectionTestUtils.setField(notice, "createdDate", LocalDateTime.now()); + User user = User.createUser(signupInput); + UserClub userClub = UserClub.createLeaderUserClub(user, club); //when when(noticeRepository.save(any(Notice.class))).thenReturn(notice); when(clubRepository.findById(any(Long.class))).thenReturn(Optional.of(club)); - noticeCommandServiceImpl.saveNotice(noticeInput); + when(userClubRepository.findByClubAndUser(any(Club.class), any(User.class))).thenReturn(Optional.of(userClub)); + noticeCommandServiceImpl.saveNotice(user, noticeInput, 1L); //then verify(noticeRepository, times(1)).save(any(Notice.class)); verify(clubRepository, times(1)).findById(any(Long.class)); diff --git a/src/test/java/com/example/moim/club/service/NoticeQueryServiceImplTest.java b/src/test/java/com/example/moim/club/service/NoticeQueryServiceImplTest.java index 68cce48..5b66afd 100644 --- a/src/test/java/com/example/moim/club/service/NoticeQueryServiceImplTest.java +++ b/src/test/java/com/example/moim/club/service/NoticeQueryServiceImplTest.java @@ -2,11 +2,15 @@ import com.example.moim.club.dto.request.ClubInput; import com.example.moim.club.dto.request.NoticeInput; -import com.example.moim.club.dto.request.NoticeOutput; +import com.example.moim.club.dto.response.NoticeOutput; import com.example.moim.club.entity.*; import com.example.moim.club.repository.ClubRepository; import com.example.moim.club.repository.NoticeRepository; +import com.example.moim.club.repository.UserClubRepository; import com.example.moim.global.enums.*; +import com.example.moim.user.dto.SignupInput; +import com.example.moim.user.entity.User; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -14,9 +18,13 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Slice; import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.multipart.MultipartFile; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -25,17 +33,21 @@ import static org.mockito.Mockito.*; import static org.mockito.Mockito.times; +@Slf4j @ExtendWith(MockitoExtension.class) public class NoticeQueryServiceImplTest { @Mock private NoticeRepository noticeRepository; @Mock private ClubRepository clubRepository; + @Mock + private UserClubRepository userClubRepository; @InjectMocks private NoticeQueryServiceImpl noticeQueryService; private ClubInput clubInput; private NoticeInput noticeInput; + private SignupInput signupInput; @BeforeEach void init() { @@ -59,7 +71,17 @@ void init() { .clubPassword(clubPassword).profileImg(profileImg).mainUniformColor(mainUniformColor).subUniformColor(subUniformColor).build(); // Notice - this.noticeInput = NoticeInput.builder().title("notice title").content("notice content").clubId(1L).build(); + this.noticeInput = NoticeInput.builder().title("notice title").content("notice content").build(); + + // User + this.signupInput = SignupInput.builder() + .email("email") + .phone("phone") + .birthday("2000-08-28") + .name("name") + .password("password") + .gender(Gender.WOMAN.getKoreanName()) + .build(); } @Test @@ -68,18 +90,23 @@ void findNotice() { //given Club club = Club.createClub(clubInput, null); Notice notice = Notice.createNotice(club, noticeInput.getTitle(), noticeInput.getContent()); + User user = User.createUser(signupInput); + UserClub userClub = UserClub.createUserClub(user, club); notice.setCreatedDate(); notice.setUpdatedDate(); //when when(clubRepository.findById(any(Long.class))).thenReturn(Optional.of(club)); - when(noticeRepository.findByClub(any(Club.class))).thenReturn(List.of(notice)); - List result = noticeQueryService.findNotice(1L); + when(noticeRepository.findByCursor(any(Long.class), any(Integer.class), any(Club.class))).thenReturn(List.of(notice)); + when(userClubRepository.findByClubAndUser(any(Club.class), any(User.class))).thenReturn(Optional.of(userClub)); + Slice result = noticeQueryService.findNotice(user, 1L, 0L); + //then - assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0).getTitle()).isEqualTo(noticeInput.getTitle()); - assertThat(result.get(0).getContent()).isEqualTo(noticeInput.getContent()); + assertThat(result.getSize()).isEqualTo(20); + assertThat(result.getNumberOfElements()).isEqualTo(1); + assertThat(result.hasNext()).isFalse(); + assertThat(result.getContent().get(0).getTitle()).isEqualTo("notice title"); verify(clubRepository, times(1)).findById(any(Long.class)); - verify(noticeRepository, times(1)).findByClub(any(Club.class)); + verify(noticeRepository, times(1)).findByCursor(any(Long.class), any(Integer.class), any(Club.class)); } @Test @@ -87,14 +114,52 @@ void findNotice() { void findNotice_zero_notice() { //given Club club = Club.createClub(clubInput, null); + User user = User.createUser(signupInput); + UserClub userClub = UserClub.createUserClub(user, club); //when when(clubRepository.findById(any(Long.class))).thenReturn(Optional.of(club)); - when(noticeRepository.findByClub(any(Club.class))).thenReturn(List.of()); - List result = noticeQueryService.findNotice(1L); + when(noticeRepository.findByCursor(any(Long.class), any(Integer.class), any(Club.class))).thenReturn(List.of()); + when(userClubRepository.findByClubAndUser(any(Club.class), any(User.class))).thenReturn(Optional.of(userClub)); + Slice result = noticeQueryService.findNotice(user, 1L, 0L); + //then + assertThat(result.getSize()).isEqualTo(20); + assertThat(result.getNumberOfElements()).isEqualTo(0); + assertThat(result.hasNext()).isFalse(); + assertThat(result.hasContent()).isFalse(); + verify(clubRepository, times(1)).findById(any(Long.class)); + verify(noticeRepository, times(1)).findByCursor(any(Long.class), any(Integer.class), any(Club.class)); + } + + @Test + @DisplayName("동아리에 등록된 공지를 조회하면 나중에 저장된 순으로 정렬되어 있다.") + void findNotice_sort() { + //given + Club club = Club.createClub(clubInput, null); + Notice notice = Notice.createNotice(club, noticeInput.getTitle(), noticeInput.getContent()); + ReflectionTestUtils.setField(notice, "id", 1L); + LocalDateTime localDateTime = LocalDateTime.now(); + ReflectionTestUtils.setField(notice, "createdDate", localDateTime); + Notice notice2 = Notice.createNotice(club, noticeInput.getTitle(), noticeInput.getContent()); + ReflectionTestUtils.setField(notice2, "id", 2L); + LocalDateTime localDateTime2 = LocalDateTime.now(); + ReflectionTestUtils.setField(notice2, "createdDate", localDateTime2); + User user = User.createUser(signupInput); + UserClub userClub = UserClub.createUserClub(user, club); + + //when + when(clubRepository.findById(any(Long.class))).thenReturn(Optional.of(club)); + when(noticeRepository.findByCursor(any(Long.class), any(Integer.class), any(Club.class))).thenReturn(List.of(notice, notice2)); + when(userClubRepository.findByClubAndUser(any(Club.class), any(User.class))).thenReturn(Optional.of(userClub)); + Slice result = noticeQueryService.findNotice(user, 1L, 0L); + //then - assertThat(result.size()).isEqualTo(0); + assertThat(result.getSize()).isEqualTo(20); + assertThat(result.getNumberOfElements()).isEqualTo(2); + assertThat(result.hasNext()).isFalse(); + assertThat(result.getContent().get(0).getCreatedDate()).isEqualTo(LocalDate.from(localDateTime2)); + assertThat(result.getContent().get(1).getCreatedDate()).isEqualTo(LocalDate.from(localDateTime)); verify(clubRepository, times(1)).findById(any(Long.class)); - verify(noticeRepository, times(1)).findByClub(any(Club.class)); + verify(noticeRepository, times(1)).findByCursor(any(Long.class), any(Integer.class), any(Club.class)); } }