diff --git a/src/main/java/kr/tgwing/tech/blog/dto/PostQuery.java b/src/main/java/kr/tgwing/tech/blog/dto/PostQuery.java index 5979cec..36bdd39 100644 --- a/src/main/java/kr/tgwing/tech/blog/dto/PostQuery.java +++ b/src/main/java/kr/tgwing/tech/blog/dto/PostQuery.java @@ -9,5 +9,6 @@ public class PostQuery { private String keyword = ""; private Set hashtag; + private boolean me = false; } diff --git a/src/main/java/kr/tgwing/tech/blog/entity/PostSpecifications.java b/src/main/java/kr/tgwing/tech/blog/entity/PostSpecifications.java index 2fdd101..01a6a48 100644 --- a/src/main/java/kr/tgwing/tech/blog/entity/PostSpecifications.java +++ b/src/main/java/kr/tgwing/tech/blog/entity/PostSpecifications.java @@ -6,6 +6,8 @@ import org.springframework.data.jpa.domain.Specification; +import kr.tgwing.tech.user.entity.User; + /** * PostSpecification */ @@ -35,4 +37,11 @@ public static Specification hasHashtagIn(Set hashtags) { }; } + public static Specification hasWriterStudentNumber(String studentNumber) { + return (root, query, cb) -> { + Join userPost = root.join("writer"); + return userPost.get("studentNumber").in(studentNumber); + }; + } + } diff --git a/src/main/java/kr/tgwing/tech/blog/exception/post/UserNotLoggedInException.java b/src/main/java/kr/tgwing/tech/blog/exception/post/UserNotLoggedInException.java new file mode 100644 index 0000000..adc9cf1 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/blog/exception/post/UserNotLoggedInException.java @@ -0,0 +1,6 @@ +package kr.tgwing.tech.blog.exception.post; + +import kr.tgwing.tech.common.exception.CommonException; + +public class UserNotLoggedInException extends CommonException { +} diff --git a/src/main/java/kr/tgwing/tech/blog/service/PostServiceImpl.java b/src/main/java/kr/tgwing/tech/blog/service/PostServiceImpl.java index d0b7c3f..35b79cc 100644 --- a/src/main/java/kr/tgwing/tech/blog/service/PostServiceImpl.java +++ b/src/main/java/kr/tgwing/tech/blog/service/PostServiceImpl.java @@ -29,6 +29,7 @@ import kr.tgwing.tech.blog.exception.comment.CommentNotFoundException; import kr.tgwing.tech.blog.exception.post.PostNotFoundException; import kr.tgwing.tech.blog.exception.post.UserIsNotPostWriterException; +import kr.tgwing.tech.blog.exception.post.UserNotLoggedInException; import kr.tgwing.tech.blog.exception.reply.ReplyNotFoundException; import kr.tgwing.tech.blog.repository.CommentRepository; import kr.tgwing.tech.blog.repository.LikeHistoryRepository; @@ -133,6 +134,12 @@ public Page getPostOverviews(PostQuery query, String userStudentNu spec = spec.and(PostSpecifications.hasHashtagIn(query.getHashtag())); } + if (query.isMe()) { + if (userStudentNumber == null || userStudentNumber.isEmpty()) + throw new UserNotLoggedInException(); + spec = spec.and(PostSpecifications.hasWriterStudentNumber(userStudentNumber)); + } + Page posts = postRepository.findAll(spec, pageable); return posts.map((post) -> { return PostOverview.of(post, doILikeIt(post.getId(), user)); diff --git a/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java b/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java index 311b232..b2ea68d 100644 --- a/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java +++ b/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java @@ -10,6 +10,7 @@ import kr.tgwing.tech.blog.exception.post.PathHasNoPostIdException; import kr.tgwing.tech.blog.exception.post.PostNotFoundException; import kr.tgwing.tech.blog.exception.post.UserIsNotPostWriterException; +import kr.tgwing.tech.blog.exception.post.UserNotLoggedInException; import kr.tgwing.tech.blog.exception.post.WrongPostRequestException; import kr.tgwing.tech.blog.exception.reply.ReplyBadRequestException; import kr.tgwing.tech.blog.exception.reply.ReplyForbiddenException; @@ -59,6 +60,9 @@ private static void setUpPostException() { ExceptionSituation.of("블로그 수정(삭제) 권한이 존재하지 않습니다", HttpStatus.FORBIDDEN, 4404)); mapper.put(WrongPostRequestException.class, ExceptionSituation.of("요청에 잘못된 정보가 포함되어 있습니다", HttpStatus.BAD_REQUEST, 4406)); + mapper.put(UserNotLoggedInException.class, + ExceptionSituation.of("로그인되지 않았습니다.", HttpStatus.BAD_REQUEST, 4407)); + } private static void setUpReplyException() { diff --git a/src/test/java/kr/tgwing/tech/annotation/IntegrationTest.java b/src/test/java/kr/tgwing/tech/annotation/IntegrationTest.java new file mode 100644 index 0000000..b8de414 --- /dev/null +++ b/src/test/java/kr/tgwing/tech/annotation/IntegrationTest.java @@ -0,0 +1,20 @@ +package kr.tgwing.tech.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +/** + * CustomTest + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@SpringBootTest +@ActiveProfiles("test") +@AutoConfigureMockMvc +public @interface IntegrationTest {} diff --git a/src/test/java/kr/tgwing/tech/blog/BlogIntegrationTest.java b/src/test/java/kr/tgwing/tech/blog/BlogIntegrationTest.java new file mode 100644 index 0000000..cc29505 --- /dev/null +++ b/src/test/java/kr/tgwing/tech/blog/BlogIntegrationTest.java @@ -0,0 +1,171 @@ +package kr.tgwing.tech.blog; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.time.LocalDate; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.test.web.servlet.MockMvc; + +import kr.tgwing.tech.annotation.IntegrationTest; +import kr.tgwing.tech.blog.entity.Comment; +import kr.tgwing.tech.blog.entity.Hashtag; +import kr.tgwing.tech.blog.entity.Post; +import kr.tgwing.tech.blog.entity.Reply; +import kr.tgwing.tech.blog.repository.CommentRepository; +import kr.tgwing.tech.blog.repository.PostRepository; +import kr.tgwing.tech.blog.repository.ReplyRepository; +import kr.tgwing.tech.user.entity.User; +import kr.tgwing.tech.user.repository.UserRepository; + +/** + * PostIntegrationTest + */ +@IntegrationTest +public class BlogIntegrationTest { + + @Autowired MockMvc mvc; + + @BeforeAll + static void prepare_test_data( + @Autowired BCryptPasswordEncoder bCryptPasswordEncoder, + @Autowired UserRepository userRepository, + @Autowired PostRepository postRepository, + @Autowired CommentRepository commentRepository, + @Autowired ReplyRepository replyRepository + ) { + User writer1 = User.builder() + .studentNumber("2018000000") + .phoneNumber("01000000000") + .email("oldman@khu.ac.kr") + .name("늙은이") + .password("12345678") + .birth(LocalDate.parse("1999-01-01")) + .build(); + User writer2 = User.builder() + .studentNumber("2022000000") + .phoneNumber("01011111111") + .email("youngman@khu.ac.kr") + .name("젊은이") + .password("12345678") + .birth(LocalDate.parse("2003-01-01")) + .build(); + writer1.hashPassword(bCryptPasswordEncoder); + writer2.hashPassword(bCryptPasswordEncoder); + + userRepository.save(writer1); + userRepository.save(writer2); + + Post post1 = Post.builder() + .title("sample blog 1") + .content("sample content 1") + .thumbnail("sample thumbnail 1") + .writer(writer1) + .build(); + Post post2 = Post.builder() + .title("sample blog 2") + .content("sample content 2") + .thumbnail("sample thumbnail 2") + .writer(writer2) + .build(); + Hashtag tag1 = Hashtag.builder() + .name("tag1") + .post(post1) + .build(); + Hashtag tag2 = Hashtag.builder() + .name("tag2") + .post(post2) + .build(); + Hashtag tag3 = Hashtag.builder() + .name("tag2") + .post(post1) + .build(); + Comment comment1 = Comment.builder() + .content("sample comment 1") + .post(post1) + .writer(writer2) + .build(); + Reply reply1 = Reply.builder() + .content("sample reply 1") + .post(post1) + .comment(comment1) + .writer(writer1) + .build(); + + post1.getHashtags().add(tag1); + post1.getHashtags().add(tag3); + post2.getHashtags().add(tag2); + + post1.getComments().add(comment1); + post1.increaseCommentCount(); + comment1.getReplies().add(reply1); + post1.increaseCommentCount(); + + postRepository.save(post1); + postRepository.save(post2); + commentRepository.save(comment1); + replyRepository.save(reply1); + } + + @Test + void get_first_page_of_posts() throws Exception { + mvc.perform(get("/post")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.totalElements").value(2)); + } + + @Test + void get_a_post_by_id() throws Exception { + mvc.perform(get("/post/{postId}", 1)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(1)) + .andExpect(jsonPath("$.title").value("sample blog 1")) + .andExpect(jsonPath("$.content").value("sample content 1")) + .andExpect(jsonPath("$.thumbnail").value("sample thumbnail 1")) + .andExpect(jsonPath("$.likeCount").value(0)) + .andExpect(jsonPath("$.commentCount").value(2)); + } + + @Test + void get_posts_title_contain_keyword() throws Exception { + mvc.perform(get("/post?keyword=1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.totalElements").value(1)); + } + + @Test + void get_posts_has_hashtag() throws Exception { + mvc.perform(get("/post?hashtag=tag1,tag2")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.totalElements").value(2)); + } + + @Test + void get_my_posts() throws Exception { + mvc.perform(post("/login") + .param("username", "2018000000") + .param("password", "12345678")) + .andExpect(status().isOk()) + .andExpect(header().exists("Authorization")) + .andDo((result) -> { + String token = result.getResponse().getHeader("Authorization"); + mvc.perform(get("/post?me=true").header("Authorization", token)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.totalElements").value(1)); + }); + } + + @Test + void throw_when_get_my_posts_but_not_logged_in() throws Exception { + mvc.perform(get("/post?me=true")) + .andExpect(status().isBadRequest()); + } + +}