diff --git a/backend/core/build.gradle b/backend/core/build.gradle index 8c0c2e44..2497c09c 100644 --- a/backend/core/build.gradle +++ b/backend/core/build.gradle @@ -21,6 +21,10 @@ repositories { mavenCentral() } +ext { + queryDslVersion = "5.0.0" +} + dependencies { if (isAppleSilicon()) { runtimeOnly("io.netty:netty-resolver-dns-native-macos:4.1.94.Final:osx-aarch_64") @@ -41,6 +45,12 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' implementation 'org.springframework.boot:spring-boot-starter-data-redis:2.3.1.RELEASE' implementation 'org.springframework.boot:spring-boot-starter-webflux' + implementation 'com.fasterxml.jackson.core:jackson-core:2.17.0' + + implementation "com.querydsl:querydsl-jpa:${queryDslVersion}:jakarta" + annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" annotationProcessor 'org.projectlombok:lombok' annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/crawling/NewsCrawlingService.java b/backend/core/src/main/java/com/rollthedice/backend/domain/crawling/NewsCrawlingService.java index 63ba708f..15457ea8 100644 --- a/backend/core/src/main/java/com/rollthedice/backend/domain/crawling/NewsCrawlingService.java +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/crawling/NewsCrawlingService.java @@ -2,7 +2,7 @@ import com.rollthedice.backend.domain.news.dto.NewsUrlDto; import com.rollthedice.backend.domain.news.entity.News; -import com.rollthedice.backend.domain.news.service.NewsCategory; +import com.rollthedice.backend.domain.news.entity.NewsCategory; import com.rollthedice.backend.domain.news.service.NewsService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -11,7 +11,6 @@ import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.springframework.beans.factory.annotation.Value; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -32,7 +31,7 @@ public class NewsCrawlingService { private final NewsService newsService; @Transactional - // @Scheduled(cron = CRON, zone = ZONE) +// @Scheduled(cron = CRON, zone = ZONE) public void scrap() throws IOException { for (NewsCategory category : NewsCategory.values()) { String categoryUrl = MAIN_URL + category.getNum(); diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/news/service/NewsCategory.java b/backend/core/src/main/java/com/rollthedice/backend/domain/news/entity/NewsCategory.java similarity index 88% rename from backend/core/src/main/java/com/rollthedice/backend/domain/news/service/NewsCategory.java rename to backend/core/src/main/java/com/rollthedice/backend/domain/news/entity/NewsCategory.java index 6cec2e54..a21bf462 100644 --- a/backend/core/src/main/java/com/rollthedice/backend/domain/news/service/NewsCategory.java +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/news/entity/NewsCategory.java @@ -1,4 +1,4 @@ -package com.rollthedice.backend.domain.news.service; +package com.rollthedice.backend.domain.news.entity; import lombok.Getter; diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/news/repository/ReadNewsRepository.java b/backend/core/src/main/java/com/rollthedice/backend/domain/news/repository/ReadNewsRepository.java index d821b1cb..0dec52dc 100644 --- a/backend/core/src/main/java/com/rollthedice/backend/domain/news/repository/ReadNewsRepository.java +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/news/repository/ReadNewsRepository.java @@ -2,13 +2,14 @@ import com.rollthedice.backend.domain.member.entity.Member; import com.rollthedice.backend.domain.news.entity.ReadNews; +import com.rollthedice.backend.domain.statistics.repository.ReadNewsCustomRepository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; @Repository -public interface ReadNewsRepository extends JpaRepository { +public interface ReadNewsRepository extends JpaRepository, ReadNewsCustomRepository { List getTop3ByMemberOrderByCreatedAtDesc(Member member); } diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/api/StatisticsApi.java b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/api/StatisticsApi.java new file mode 100644 index 00000000..3f8b53d7 --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/api/StatisticsApi.java @@ -0,0 +1,35 @@ +package com.rollthedice.backend.domain.statistics.api; + +import com.rollthedice.backend.domain.statistics.dto.response.CategoryStatisticsResponse; +import com.rollthedice.backend.domain.statistics.dto.response.DateViewStatisticsResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; + +import java.util.List; + +public interface StatisticsApi { + @Operation( + summary = "최근 일주일 날짜별 뉴스 조회수 조회", + description = "최근 일주일간 날짜별로 뉴스 조회수를 조회합니다.", + security = {@SecurityRequirement(name = "access_token")}, + tags = {"Statistics"} + ) + @ApiResponse( + responseCode = "200", + description = "요청에 성공하였습니다." + ) + List getViewsOfDates(); + + @Operation( + summary = "카테고리별 조회수 조회", + description = "카테고리별 조회수를 조회합니다.", + security = {@SecurityRequirement(name = "access_token")}, + tags = {"Statistics"} + ) + @ApiResponse( + responseCode = "200", + description = "요청에 성공하였습니다." + ) + List getCategoryStatistics(); +} diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/api/StatisticsController.java b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/api/StatisticsController.java new file mode 100644 index 00000000..8dc19f3d --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/api/StatisticsController.java @@ -0,0 +1,34 @@ +package com.rollthedice.backend.domain.statistics.api; + +import com.rollthedice.backend.domain.statistics.dto.response.CategoryStatisticsResponse; +import com.rollthedice.backend.domain.statistics.dto.response.DateViewStatisticsResponse; +import com.rollthedice.backend.domain.statistics.service.StatisticsService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("statistics") +public class StatisticsController implements StatisticsApi { + private final StatisticsService statisticsService; + + @ResponseStatus(HttpStatus.OK) + @GetMapping("/per-dates") + @Override + public List getViewsOfDates() { + return statisticsService.getViewsOfDates(); + } + + @ResponseStatus(HttpStatus.OK) + @GetMapping("/categories") + @Override + public List getCategoryStatistics() { + return statisticsService.getCategoryStatistics(); + } +} diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/dto/response/CategoryStatisticsResponse.java b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/dto/response/CategoryStatisticsResponse.java new file mode 100644 index 00000000..dd2031bf --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/dto/response/CategoryStatisticsResponse.java @@ -0,0 +1,13 @@ +package com.rollthedice.backend.domain.statistics.dto.response; + +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CategoryStatisticsResponse { + private Long views; + private String category; +} diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/dto/response/DateViewStatisticsResponse.java b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/dto/response/DateViewStatisticsResponse.java new file mode 100644 index 00000000..449bbd91 --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/dto/response/DateViewStatisticsResponse.java @@ -0,0 +1,15 @@ +package com.rollthedice.backend.domain.statistics.dto.response; + +import lombok.*; + +import java.time.LocalDate; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class DateViewStatisticsResponse { + private Long views; + private LocalDate dateTime; +} diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/repository/ReadNewsCustomRepository.java b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/repository/ReadNewsCustomRepository.java new file mode 100644 index 00000000..472c552e --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/repository/ReadNewsCustomRepository.java @@ -0,0 +1,11 @@ +package com.rollthedice.backend.domain.statistics.repository; + +import com.rollthedice.backend.domain.member.entity.Member; + +import java.time.LocalDate; + +public interface ReadNewsCustomRepository { + Long getCountOfReadNewsByDate(Member member, LocalDate date); + + Long getCountOfReadNewsByCategory(Member member, String category); +} diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/repository/ReadNewsCustomRepositoryImpl.java b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/repository/ReadNewsCustomRepositoryImpl.java new file mode 100644 index 00000000..8fe479f3 --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/repository/ReadNewsCustomRepositoryImpl.java @@ -0,0 +1,38 @@ +package com.rollthedice.backend.domain.statistics.repository; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.rollthedice.backend.domain.member.entity.Member; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDate; + +import static com.rollthedice.backend.domain.news.entity.QNews.news; +import static com.rollthedice.backend.domain.news.entity.QReadNews.readNews; + +@RequiredArgsConstructor +public class ReadNewsCustomRepositoryImpl implements ReadNewsCustomRepository { + private final JPAQueryFactory queryFactory; + + @Override + public Long getCountOfReadNewsByDate(Member member, LocalDate date) { + return queryFactory + .select(readNews.count()) + .from(readNews) + .where(readNews.member.eq(member) + .and(readNews.createdAt.between( + date.atStartOfDay(), + date.plusDays(1).atStartOfDay().minusNanos(1)))) + .fetchOne(); + } + + @Override + public Long getCountOfReadNewsByCategory(Member member, String category) { + return queryFactory + .select(readNews.count()) + .from(readNews) + .join(readNews.news, news) + .where(readNews.member.eq(member) + .and(news.category.eq(category)) + ).fetchOne(); + } +} diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/service/StatisticsService.java b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/service/StatisticsService.java new file mode 100644 index 00000000..6a9db10f --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/statistics/service/StatisticsService.java @@ -0,0 +1,46 @@ +package com.rollthedice.backend.domain.statistics.service; + +import com.rollthedice.backend.domain.member.entity.Member; +import com.rollthedice.backend.domain.news.entity.NewsCategory; +import com.rollthedice.backend.domain.news.repository.ReadNewsRepository; +import com.rollthedice.backend.domain.statistics.dto.response.CategoryStatisticsResponse; +import com.rollthedice.backend.domain.statistics.dto.response.DateViewStatisticsResponse; +import com.rollthedice.backend.global.oauth2.service.AuthService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.IntStream; + +@Service +@RequiredArgsConstructor +public class StatisticsService { + private final ReadNewsRepository readNewsRepository; + private final AuthService authService; + + public List getViewsOfDates() { + Member member = authService.getMember(); + List responses = new ArrayList<>(); + IntStream.range(0, 7) + .forEach(day -> responses.add(DateViewStatisticsResponse.builder() + .views(readNewsRepository.getCountOfReadNewsByDate(member, LocalDate.now().minusDays(day))) + .dateTime(LocalDate.now().minusDays(day)).build())); + return responses; + } + + public List getCategoryStatistics() { + Member member = authService.getMember(); + List responses = new ArrayList<>(); + for (NewsCategory category : NewsCategory.values()) { + long views = readNewsRepository.getCountOfReadNewsByCategory(member, category.getName()); + CategoryStatisticsResponse response = CategoryStatisticsResponse.builder() + .category(category.getName()) + .views(views).build(); + responses.add(response); + } + return responses; + } +} diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/config/QuerydslConfig.java b/backend/core/src/main/java/com/rollthedice/backend/global/config/QuerydslConfig.java new file mode 100644 index 00000000..91a1e7ce --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/global/config/QuerydslConfig.java @@ -0,0 +1,26 @@ +package com.rollthedice.backend.global.config; + + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +public class QuerydslConfig { + + private final EntityManager em; + + @Autowired + public QuerydslConfig(EntityManager em) { + this.em = em; + } + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(em); + } +} \ No newline at end of file diff --git a/backend/core/src/test/java/com/rollthedice/backend/domain/news/controller/NewsControllerTest.java b/backend/core/src/test/java/com/rollthedice/backend/domain/news/api/NewsControllerTest.java similarity index 96% rename from backend/core/src/test/java/com/rollthedice/backend/domain/news/controller/NewsControllerTest.java rename to backend/core/src/test/java/com/rollthedice/backend/domain/news/api/NewsControllerTest.java index 1c790adc..200419be 100644 --- a/backend/core/src/test/java/com/rollthedice/backend/domain/news/controller/NewsControllerTest.java +++ b/backend/core/src/test/java/com/rollthedice/backend/domain/news/api/NewsControllerTest.java @@ -1,6 +1,5 @@ -package com.rollthedice.backend.domain.news.controller; +package com.rollthedice.backend.domain.news.api; -import com.rollthedice.backend.domain.news.api.NewsController; import com.rollthedice.backend.domain.news.exception.NewsNotFoundException; import com.rollthedice.backend.domain.news.repository.NewsRepository; import com.rollthedice.backend.domain.news.service.NewsService; diff --git a/backend/core/src/test/java/com/rollthedice/backend/domain/news/repository/ReadNewsRepositoryTest.java b/backend/core/src/test/java/com/rollthedice/backend/domain/news/repository/ReadNewsRepositoryTest.java new file mode 100644 index 00000000..5427888c --- /dev/null +++ b/backend/core/src/test/java/com/rollthedice/backend/domain/news/repository/ReadNewsRepositoryTest.java @@ -0,0 +1,70 @@ +package com.rollthedice.backend.domain.news.repository; + +import com.rollthedice.backend.domain.member.entity.Member; +import com.rollthedice.backend.domain.member.repository.MemberRepository; +import com.rollthedice.backend.domain.news.entity.News; +import com.rollthedice.backend.domain.news.entity.NewsCategory; +import com.rollthedice.backend.domain.news.entity.ReadNews; +import com.rollthedice.backend.support.RepositoryTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDate; + +import static com.rollthedice.backend.domain.member.MemberFixture.MEMBER; +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("ReadNewsRepository의 ") +@RepositoryTest +public class ReadNewsRepositoryTest { + @Autowired + private ReadNewsRepository readNewsRepository; + @Autowired + private NewsRepository newsRepository; + @Autowired + private MemberRepository memberRepository; + + private Member member; + + @BeforeEach + void setUp() { + member = memberRepository.save(MEMBER()); + } + + @Test + @DisplayName("하루동안 조회한 뉴스 개수를 반환할 수 있는가") + void getCountOfReadNewsByDate() { + //given + int expect = 5; + for (int i = 0; i < expect; i++) { + readNewsRepository.save(ReadNews.builder().member(member).news(newsRepository.save(News.builder().id((long) i).build())).build()); + } + + //when + Long result = readNewsRepository.getCountOfReadNewsByDate(member, LocalDate.now()); + + //then + assertThat(result).isEqualTo(expect); + } + + @Test + @DisplayName("회원이 카테고리별로 조회한 뉴스 개수를 반환할 수 있는가") + void getCountOfReadNewsByCategory() { + //given + int expect = 5; + String category = NewsCategory.SCIENCE.getName(); + for (int i = 0; i < expect; i++) { + readNewsRepository.save(ReadNews.builder().member(member) + .news(newsRepository.save( + News.builder().id((long) i).category(category).build())).build()); + } + + //when + Long result = readNewsRepository.getCountOfReadNewsByCategory(member, category); + + //then + assertThat(result).isEqualTo(expect); + } +} diff --git a/backend/core/src/test/java/com/rollthedice/backend/domain/news/service/NewsServiceTest.java b/backend/core/src/test/java/com/rollthedice/backend/domain/news/service/NewsServiceTest.java index df80c961..0ebf8b3b 100644 --- a/backend/core/src/test/java/com/rollthedice/backend/domain/news/service/NewsServiceTest.java +++ b/backend/core/src/test/java/com/rollthedice/backend/domain/news/service/NewsServiceTest.java @@ -6,23 +6,19 @@ import com.rollthedice.backend.domain.news.dto.response.NewsDetailResponse; import com.rollthedice.backend.domain.news.dto.response.ReadNewsResponse; import com.rollthedice.backend.domain.news.entity.News; -import com.rollthedice.backend.domain.news.entity.ReadNews; import com.rollthedice.backend.domain.news.exception.NewsNotFoundException; import com.rollthedice.backend.domain.news.mapper.NewsMapper; import com.rollthedice.backend.domain.news.repository.NewsRepository; import com.rollthedice.backend.domain.news.repository.ReadNewsRepository; import com.rollthedice.backend.global.LoginTest; -import com.rollthedice.backend.support.RepositoryTest; import lombok.extern.slf4j.Slf4j; 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.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -36,7 +32,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.when; @Slf4j @DisplayName("NewsService의") diff --git a/backend/core/src/test/java/com/rollthedice/backend/domain/statistics/StatisticsFixture.java b/backend/core/src/test/java/com/rollthedice/backend/domain/statistics/StatisticsFixture.java new file mode 100644 index 00000000..8c8ed161 --- /dev/null +++ b/backend/core/src/test/java/com/rollthedice/backend/domain/statistics/StatisticsFixture.java @@ -0,0 +1,24 @@ +package com.rollthedice.backend.domain.statistics; + +import com.rollthedice.backend.domain.news.entity.NewsCategory; +import com.rollthedice.backend.domain.statistics.dto.response.CategoryStatisticsResponse; +import com.rollthedice.backend.domain.statistics.dto.response.DateViewStatisticsResponse; + +import java.time.LocalDate; + +public class StatisticsFixture { + + public static DateViewStatisticsResponse DATE_VIEW_STATISTICS_RESPONSE() { + return DateViewStatisticsResponse.builder() + .views(1L) + .dateTime(LocalDate.of(2024, 6, 15)) + .build(); + } + + public static CategoryStatisticsResponse CATEGORY_STATISTICS_RESPONSE() { + return CategoryStatisticsResponse.builder() + .views(2L) + .category(NewsCategory.SCIENCE.getName()) + .build(); + } +} diff --git a/backend/core/src/test/java/com/rollthedice/backend/domain/statistics/api/StatisticsControllerTest.java b/backend/core/src/test/java/com/rollthedice/backend/domain/statistics/api/StatisticsControllerTest.java new file mode 100644 index 00000000..8580eec2 --- /dev/null +++ b/backend/core/src/test/java/com/rollthedice/backend/domain/statistics/api/StatisticsControllerTest.java @@ -0,0 +1,70 @@ +package com.rollthedice.backend.domain.statistics.api; + +import com.rollthedice.backend.domain.statistics.dto.response.CategoryStatisticsResponse; +import com.rollthedice.backend.domain.statistics.dto.response.DateViewStatisticsResponse; +import com.rollthedice.backend.domain.statistics.service.StatisticsService; +import com.rollthedice.backend.global.BaseControllerTest; +import org.junit.jupiter.api.DisplayName; +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; +import org.springframework.test.web.servlet.ResultActions; + +import java.util.List; + +import static com.rollthedice.backend.domain.statistics.StatisticsFixture.CATEGORY_STATISTICS_RESPONSE; +import static com.rollthedice.backend.domain.statistics.StatisticsFixture.DATE_VIEW_STATISTICS_RESPONSE; +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@DisplayName("StatisticsController의 ") +@WebMvcTest(StatisticsController.class) +class StatisticsControllerTest extends BaseControllerTest { + @MockBean + private StatisticsService statisticsService; + + @Test + @DisplayName("최근 일주일 날짜별 뉴스 조회수 조회 API가 수행되는가") + void getViewsOfDates() throws Exception { + //given + List responses = List.of(DATE_VIEW_STATISTICS_RESPONSE()); + given(statisticsService.getViewsOfDates()).willReturn(responses); + + //when + final ResultActions perform = mockMvc.perform( + get("/statistics/per-dates") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer " + accessToken) + ).andDo(print()); + + //then + perform.andExpect(status().isOk()) + .andExpect(jsonPath("$..['views']").exists()) + .andExpect(jsonPath("$..['dateTime']").exists()); + } + + @Test + @DisplayName("카테고리별 조회수 조회 API가 수행되는 ") + void getCategoryStatistics() throws Exception { + //given + List responses = List.of(CATEGORY_STATISTICS_RESPONSE()); + given(statisticsService.getCategoryStatistics()).willReturn(responses); + + //when + final ResultActions perform = mockMvc.perform( + get("/statistics/categories") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer " + accessToken) + ).andDo(print()); + + //perform + perform.andExpect(status().isOk()) + .andExpect(jsonPath("$..['views']").exists()) + .andExpect(jsonPath("$..['category']").exists()); + + } +} diff --git a/backend/core/src/test/java/com/rollthedice/backend/domain/statistics/service/StatisticsServiceTest.java b/backend/core/src/test/java/com/rollthedice/backend/domain/statistics/service/StatisticsServiceTest.java new file mode 100644 index 00000000..fddb53a3 --- /dev/null +++ b/backend/core/src/test/java/com/rollthedice/backend/domain/statistics/service/StatisticsServiceTest.java @@ -0,0 +1,59 @@ +package com.rollthedice.backend.domain.statistics.service; + +import com.rollthedice.backend.domain.member.entity.Member; +import com.rollthedice.backend.domain.news.entity.NewsCategory; +import com.rollthedice.backend.domain.news.repository.ReadNewsRepository; +import com.rollthedice.backend.domain.statistics.dto.response.CategoryStatisticsResponse; +import com.rollthedice.backend.domain.statistics.dto.response.DateViewStatisticsResponse; +import com.rollthedice.backend.global.LoginTest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; + +import java.time.LocalDate; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.given; + +@DisplayName("StatisticsService의 ") +@ExtendWith(MockitoExtension.class) +public class StatisticsServiceTest extends LoginTest { + @Autowired + private StatisticsService statisticsService; + @MockBean + private ReadNewsRepository readNewsRepository; + + @Test + @DisplayName("최근 일주일 날짜별 뉴스 조회수가 조회되는가") + void getViewsOfDates() { + //given + int oneWeek = 7; + given(readNewsRepository.getCountOfReadNewsByDate(loginUser, LocalDate.now())).willReturn(1L); + + //when + List result = statisticsService.getViewsOfDates(); + + //then + assertThat(result).hasSize(oneWeek); + } + + @Test + @DisplayName("카테고리별 조회수가 조회되는가") + void getCategoryStatistics() { + //given + long views = 3; + String category = NewsCategory.SCIENCE.getName(); + given(readNewsRepository.getCountOfReadNewsByCategory(loginUser, category)).willReturn(views); + + //when + List result = statisticsService.getCategoryStatistics(); + + //then + assertThat(result).hasSize(NewsCategory.values().length); + } +} diff --git a/backend/core/src/test/java/com/rollthedice/backend/support/RepositoryTest.java b/backend/core/src/test/java/com/rollthedice/backend/support/RepositoryTest.java index 89b01e50..0bfdc47c 100644 --- a/backend/core/src/test/java/com/rollthedice/backend/support/RepositoryTest.java +++ b/backend/core/src/test/java/com/rollthedice/backend/support/RepositoryTest.java @@ -1,6 +1,7 @@ package com.rollthedice.backend.support; import com.rollthedice.backend.global.config.JpaAuditingConfig; +import com.rollthedice.backend.global.config.QuerydslConfig; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; @@ -13,7 +14,7 @@ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @DataJpaTest -@Import({JpaAuditingConfig.class}) +@Import({JpaAuditingConfig.class, QuerydslConfig.class}) @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) public @interface RepositoryTest { } \ No newline at end of file