Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

게시글 검색, 태그 검색 기능 구현 #233

Merged
merged 5 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
Expand Down Expand Up @@ -152,4 +151,38 @@ public List<PostResponse> findBestPosts(String token) {
member.hasVoted(post)))
.collect(Collectors.toList());
}

@Transactional(readOnly = true)
public List<PostResponse> findPostsByTitle(String token, String keyword) {
List<Post> posts = postRepository.findByTitleContaining(keyword);
if (token == null) {
return posts.stream()
.map(post -> PostResponse.fromEntity(post, false, false, false))
.collect(Collectors.toList());
}
Member member = getCurrentMember(memberRepository);
return posts.stream()
.map(post -> PostResponse.fromEntity(post,
member.hasLiked(post),
member.hasBookmarked(post),
member.hasVoted(post)))
.collect(Collectors.toList());
}

@Transactional(readOnly = true)
public List<PostResponse> findPostsByTag(String token, String tagName) {
List<Post> posts = postRepository.findByPostTagsContaining(tagName);
if (token == null) {
return posts.stream()
.map(post -> PostResponse.fromEntity(post, false, false, false))
.collect(Collectors.toList());
}
Member member = getCurrentMember(memberRepository);
return posts.stream()
.map(post -> PostResponse.fromEntity(post,
member.hasLiked(post),
member.hasBookmarked(post),
member.hasVoted(post)))
.collect(Collectors.toList());
}
}
11 changes: 10 additions & 1 deletion src/main/java/balancetalk/module/post/domain/PostRepository.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package balancetalk.module.post.domain;

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface PostRepository extends JpaRepository<Post, Long> {

@Query("select p "
Expand All @@ -14,4 +15,12 @@ public interface PostRepository extends JpaRepository<Post, Long> {
+ "group by p.id "
+ "order by count(l) desc, p.views desc")
List<Post> findBestPosts(Pageable pageable);
List<Post> findByTitleContaining(String keyword);

@Query(value = "SELECT * " +
"FROM post p " +
"NATURAL JOIN post_tag pt " +
"NATURAL JOIN tag t " +
"WHERE p.post_id = pt.post_id AND pt.tag_id = t.tag_id AND t.name = :tagName", nativeQuery = true)
List<Post> findByPostTagsContaining(String tagName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ public List<PostResponse> findBestPosts(@RequestHeader(value = "Authorization",
return postService.findBestPosts(token);
}

@ResponseStatus(HttpStatus.OK)
@GetMapping("/title")
@Operation(summary = "게시글 제목 검색 기능", description = "키워드에 맞는 모든 게시글을 조회한다.")
public List<PostResponse> findPostsByTitle(@RequestHeader(value = "Authorization", required = false) String token,
@RequestParam String keyword) {
return postService.findPostsByTitle(token, keyword);
}

@ResponseStatus(HttpStatus.OK)
@GetMapping("/tag")
@Operation(summary = "게시글 태그 검색 기능", description = "태그에 맞는 모든 게시글을 조회한다.")
public List<PostResponse> findPostsByTag(@RequestHeader(value = "Authorization", required = false) String token,
@RequestParam String tagName) {
return postService.findPostsByTag(token, tagName);
}

@ResponseStatus(HttpStatus.CREATED)
@DeleteMapping("/{postId}")
@Operation(summary = "게시글 삭제", description = "post-id에 해당하는 게시글을 삭제한다.")
Expand Down
200 changes: 152 additions & 48 deletions src/test/java/balancetalk/module/post/application/PostServiceTest.java
Original file line number Diff line number Diff line change
@@ -1,56 +1,160 @@
package balancetalk.module.post.application;

import balancetalk.global.redis.application.RedisService;
import balancetalk.module.file.domain.File;
import balancetalk.module.file.domain.FileRepository;
import balancetalk.module.member.domain.Member;
import balancetalk.module.member.domain.MemberRepository;
import balancetalk.module.post.domain.*;
import balancetalk.module.post.dto.BalanceOptionDto;
import balancetalk.module.post.dto.PostRequest;
import balancetalk.module.post.dto.PostResponse;
import balancetalk.module.post.dto.PostTagDto;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;

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.assertj.core.api.Assertions.*;

import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;

//@ExtendWith(MockitoExtension.class)
//class PostServiceTest {
//
// @Mock
// MemberRepository memberRepository;
//
// @Mock
// PostRepository postRepository;
//
// @Mock
// PostLikeRepository postLikeRepository;
//
// @Mock
// FileRepository fileRepository;
//
// @Mock
// RedisService redisService;
//
// @InjectMocks
// PostService postService;
//
// private String accessToken = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0MTIzNDVAbmF2ZXIuY29tIiwiaWF0IjoxNzA5NDc1NTE4LCJleHAiOjE3MDk1MTg3MTh9.ZZXuN4OWM2HZjWOx7Pupl5NkRtjvd4qnK_txGdRy7G5_GdKgnyF3JfiUsenQgxsi1Y_-7C0dA85xabot2m1cag";
//
// Member member = Member.builder()
// .id(1L)
// .email("[email protected]")
// .build();
//
// File file = File.builder()
// .storedName("e90a6177-89a1-45b3-91d3-cb39e9bec407_미어캣.jpg")
// .build();
// BalanceOption balanceOption = BalanceOption.builder()
// .title("option1")
// .description("description1")
// .file(file)
// .build();
//
// @BeforeEach
// void setUp() {
// // SecurityContext에 인증된 사용자 설정
// Authentication authentication = mock(Authentication.class);
// SecurityContext securityContext = mock(SecurityContext.class);
// lenient().when(securityContext.getAuthentication()).thenReturn(authentication);
// SecurityContextHolder.setContext(securityContext);
//
//
// }
@ExtendWith(MockitoExtension.class)
class PostServiceTest {

@Mock
MemberRepository memberRepository;

@Mock
PostRepository postRepository;

@Mock
PostLikeRepository postLikeRepository;

@Mock
FileRepository fileRepository;

@Mock
RedisService redisService;

@InjectMocks
PostService postService;

private String accessToken = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0MTIzNDVAbmF2ZXIuY29tIiwiaWF0IjoxNzA5NDc1NTE4LCJleHAiOjE3MDk1MTg3MTh9.ZZXuN4OWM2HZjWOx7Pupl5NkRtjvd4qnK_txGdRy7G5_GdKgnyF3JfiUsenQgxsi1Y_-7C0dA85xabot2m1cag";

Member member = Member.builder()
.id(1L)
.email("[email protected]")
.build();

File file = File.builder()
.storedName("e90a6177-89a1-45b3-91d3-cb39e9bec407_미어캣.jpg")
.build();

BalanceOption balanceOption = BalanceOption.builder()
.title("option1")
.description("description1")
.file(file)
.build();

Tag tag1 = Tag.builder()
.name("태그1")
.build();
Tag tag2 = Tag.builder()
.name("태그2")
.build();
PostTag postTag1 = PostTag.builder()
.tag(tag1)
.build();
PostTag postTag2 = PostTag.builder()
.tag(tag2)
.build();
Post post1 = Post.builder()
.id(1L)
.title("고양이")
.category(PostCategory.CASUAL)
.member(member)
.options(List.of(balanceOption))
.views(0L)
.postTags(List.of(postTag1, postTag2))
.build();

Post post2 = Post.builder()
.id(2L)
.title("미어캣")
.category(PostCategory.DISCUSSION)
.member(member)
.options(List.of(balanceOption))
.views(23L)
.postTags(List.of(postTag1))
.build();
@BeforeEach
void setUp() {
// SecurityContext에 인증된 사용자 설정
Authentication authentication = mock(Authentication.class);
SecurityContext securityContext = mock(SecurityContext.class);
lenient().when(securityContext.getAuthentication()).thenReturn(authentication);
SecurityContextHolder.setContext(securityContext);
}

@AfterEach
void clear() {
SecurityContextHolder.clearContext();
}

@Test
@DisplayName("게시글 제목으로 검색")
void searchPostsByTitle() {
// given
String keyword = "미어";
when(postRepository.findByTitleContaining(keyword)).thenReturn(List.of(post2));
lenient().when(memberRepository.findByEmail(any())).thenReturn(Optional.ofNullable(member));

// when
List<PostResponse> result = postService.findPostsByTitle(null, keyword);

// then
assertEquals(1, result.size());
assertThat(result.get(0).getTitle()).contains(keyword);
}

@Test
@DisplayName("게시글 태그로 검색")
void searchPostsByTag() {
// given
String tagName = "태그1";
when(postRepository.findByPostTagsContaining(tagName)).thenReturn(List.of(post1));
lenient().when(memberRepository.findByEmail(any())).thenReturn(Optional.ofNullable(member));

// when
List<PostResponse> result = postService.findPostsByTag(null, tagName);

// then
assertEquals(1, result.size());
List<PostTagDto> tags = result.get(0).getPostTags();
assertThat(tags)
.extracting(PostTagDto::getTagName)
.contains(tagName);
}
//
// @Test
// @DisplayName("게시글 작성 성공")
Expand Down Expand Up @@ -240,4 +344,4 @@
// .isInstanceOf(BalanceTalkException.class)
// .hasMessageContaining(ALREADY_LIKE_POST.getMessage());
// }
//}
}
Loading