Skip to content

Commit

Permalink
#61 - 해시태그 서비스 구현
Browse files Browse the repository at this point in the history
게시글에 달린 해시태그를 관리하기 위한 기능을 구현.
독특하게도 해시태그의 저장을 다루는 메소드가 없는데
해시태그의 저장은 현재 비즈니스 로직의 구조 상
게시글이 저장, 수정될 때만 일어나는 기능으로
연관관계 매핑을 통해 게시글 서비스에서 모두 처리가 되기 때문.

* 해시태그 이름으로 해시태그 정보를 db 조회하기
* 본문에서 해시태그 파싱하기
* 게시글이 더 이상 없는 해시태그를 삭제하기
* TODO: 삭제 기능의 테스트를 강의 중에 만들지 않았다. 추가하면 좋을 듯.
  • Loading branch information
lmw7414 committed Jan 8, 2023
1 parent 313a319 commit 28f590b
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,44 @@
package com.fastcampus.projectboard.service;

import com.fastcampus.projectboard.domain.Hashtag;
import com.fastcampus.projectboard.repository.HashtagRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@RequiredArgsConstructor
@Service
public class HashtagService {
public Object parseHashtagNames(String content) {
return null;

private final HashtagRepository hashtagRepository;
public Set<String> parseHashtagNames(String content) {
if(content == null) {
return Set.of();
}

Pattern pattern = Pattern.compile("#[\\w가-힣]+");
Matcher matcher = pattern.matcher(content.strip());
Set<String> result = new HashSet<>();

while(matcher.find()) {
result.add(matcher.group().replace("#", ""));
}

return Set.copyOf(result);
}

public Object findHashtagsByNames(Set<String> expectedHashtagNames) {
return null;
public Set<Hashtag> findHashtagsByNames(Set<String> hashtagNames) {
return new HashSet<>(hashtagRepository.findByHashtagNameIn(hashtagNames));
}

public void deleteHashtagWithoutArticles(Object any) {
public void deleteHashtagWithoutArticles(Long hashtagId) {
Hashtag hashtag = hashtagRepository.getReferenceById(hashtagId);
if(hashtag.getArticles().isEmpty()) {
hashtagRepository.delete(hashtag);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.fastcampus.projectboard.service;

import com.fastcampus.projectboard.domain.Hashtag;
import com.fastcampus.projectboard.repository.HashtagRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;


@DisplayName("비즈니스 로직 - 해시태그")
@ExtendWith(MockitoExtension.class)
class HashtagServiceTest {

@InjectMocks private HashtagService sut; //System under Test

@Mock private HashtagRepository hashtagRepository;

@DisplayName("본문을 파싱하면, 해시태그 이름들을 중복없이 반환한다.")
@MethodSource
@ParameterizedTest(name = "[{index}] \"{0}\" => {1}")
void givenContent_whenParsing_thenReturnsUniqueHashtagNames(String input, Set<String> expected) {
// Given

// When
Set<String> actual = sut.parseHashtagNames(input);

// Then
assertThat(actual).containsExactlyInAnyOrderElementsOf(expected); // 순서는 상관없고, 요소들은 정확하게 다 들어가 있어야 함.
then(hashtagRepository).shouldHaveNoInteractions();
}

static Stream<Arguments> givenContent_whenParsing_thenReturnsUniqueHashtagNames() {
return Stream.of(
arguments(null, Set.of()),
arguments("", Set.of()),
arguments(" ", Set.of()),
arguments("#", Set.of()),
arguments(" #", Set.of()),
arguments("# ", Set.of()),
arguments("java", Set.of()),
arguments("java#", Set.of()),
arguments("ja#va", Set.of("va")),
arguments("#java", Set.of("java")),
arguments("#java_spring", Set.of("java_spring")),
arguments("#java-spring", Set.of("java")),
arguments("#_java_spring", Set.of("_java_spring")),
arguments("#-java-spring", Set.of()),
arguments("#_java_spring__", Set.of("_java_spring__")),
arguments("#java#spring", Set.of("java", "spring")),
arguments("#java #spring", Set.of("java", "spring")),
arguments("#java #spring", Set.of("java", "spring")),
arguments("#java #spring", Set.of("java", "spring")),
arguments("#java #spring", Set.of("java", "spring")),
arguments(" #java #spring ", Set.of("java", "spring")),
arguments(" #java #spring ", Set.of("java", "spring")),
arguments("#java#spring#부트", Set.of("java", "spring", "부트")),
arguments("#java #spring#부트", Set.of("java", "spring", "부트")),
arguments("#java#spring #부트", Set.of("java", "spring", "부트")),
arguments("#java,#spring,#부트", Set.of("java", "spring", "부트")),
arguments("#java.#spring;#부트", Set.of("java", "spring", "부트")),
arguments("#java|#spring:#부트", Set.of("java", "spring", "부트")),
arguments("#java #spring #부트", Set.of("java", "spring", "부트")),
arguments(" #java,? #spring ... #부트 ", Set.of("java", "spring", "부트")),
arguments("#java#java#spring#부트", Set.of("java", "spring", "부트")),
arguments("#java#java#java#spring#부트", Set.of("java", "spring", "부트")),
arguments("#java#spring#java#부트#java", Set.of("java", "spring", "부트")),
arguments("#java#스프링 아주 긴 글~~~~~~~~~~~~~~~~~~~~~", Set.of("java", "스프링")),
arguments("아주 긴 글~~~~~~~~~~~~~~~~~~~~~#java#스프링", Set.of("java", "스프링")),
arguments("아주 긴 글~~~~~~#java#스프링~~~~~~~~~~~~~~~", Set.of("java", "스프링")),
arguments("아주 긴 글~~~~~~#java~~~~~~~#스프링~~~~~~~~", Set.of("java", "스프링"))
);
}

@DisplayName("해시태그 이름들을 입력하면, 저장된 해시태그 중 이름에 매칭되는 것들을 중복없이 반환한다.")
@Test
void givenHashtagNames_whenFindingHashtags_thenReturnsHashtagSet() {
// Given
Set<String> hashtagNames = Set.of("java", "spring", "boots");
given(hashtagRepository.findByHashtagNameIn(hashtagNames)).willReturn(List.of(
Hashtag.of("java"),
Hashtag.of("spring")
));

// When
Set<Hashtag> hashtags = sut.findHashtagsByNames(hashtagNames);

// Then
assertThat(hashtags).hasSize(2);
then(hashtagRepository).should().findByHashtagNameIn(hashtagNames);

}
}

0 comments on commit 28f590b

Please sign in to comment.