From 693f50836a04d51584eb9fb3c704ab1667ea9990 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Thu, 8 Aug 2024 22:29:14 +0900 Subject: [PATCH 1/8] =?UTF-8?q?refactor:=20mockMvc=EB=A1=9C=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workbook/response/ReadWorkBookResponse.kt | 2 + .../api/web/controller/ControllerTestSpec.kt | 7 -- .../controller/admin/AdminControllerTest.kt | 77 +++++++------------ .../article/ArticleControllerTest.kt | 47 ++++------- .../controller/member/MemberControllerTest.kt | 44 ++++------- .../problem/ProblemControllerTest.kt | 70 +++++++---------- .../SubscriptionControllerTest.kt | 23 ------ .../workbook/WorkBookControllerTest.kt | 37 ++------- .../article/WorkBookArticleControllerTest.kt | 23 ------ 9 files changed, 93 insertions(+), 237 deletions(-) diff --git a/api/src/main/kotlin/com/few/api/web/controller/workbook/response/ReadWorkBookResponse.kt b/api/src/main/kotlin/com/few/api/web/controller/workbook/response/ReadWorkBookResponse.kt index 090ef090f..26e4ff94c 100644 --- a/api/src/main/kotlin/com/few/api/web/controller/workbook/response/ReadWorkBookResponse.kt +++ b/api/src/main/kotlin/com/few/api/web/controller/workbook/response/ReadWorkBookResponse.kt @@ -1,5 +1,6 @@ package com.few.api.web.controller.workbook.response +import com.fasterxml.jackson.annotation.JsonFormat import com.few.api.domain.workbook.usecase.dto.ReadWorkbookUseCaseOut import java.net.URL import java.time.LocalDateTime @@ -10,6 +11,7 @@ data class ReadWorkBookResponse( val title: String, val description: String, val category: String, + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") val createdAt: LocalDateTime, val writers: List, val articles: List, diff --git a/api/src/test/kotlin/com/few/api/web/controller/ControllerTestSpec.kt b/api/src/test/kotlin/com/few/api/web/controller/ControllerTestSpec.kt index 573784ea3..ac5d49cb7 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/ControllerTestSpec.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/ControllerTestSpec.kt @@ -19,7 +19,6 @@ import com.few.api.domain.workbook.article.usecase.ReadWorkBookArticleUseCase import com.few.api.domain.workbook.usecase.BrowseWorkbooksUseCase import com.few.api.domain.workbook.usecase.ReadWorkbookUseCase import com.few.api.security.token.TokenResolver -import com.few.api.web.handler.ApiControllerExceptionHandler import org.junit.jupiter.api.extension.ExtendWith import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs @@ -29,7 +28,6 @@ import org.springframework.boot.test.mock.mockito.MockBean import org.springframework.restdocs.RestDocumentationExtension import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.ContextConfiguration -import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.test.web.servlet.MockMvc @ActiveProfiles(value = ["test", "new"]) @@ -41,14 +39,9 @@ import org.springframework.test.web.servlet.MockMvc abstract class ControllerTestSpec { /** Common */ - @Autowired - lateinit var apiControllerExceptionHandler: ApiControllerExceptionHandler - @Autowired lateinit var objectMapper: ObjectMapper - lateinit var webTestClient: WebTestClient - @Autowired lateinit var mockMvc: MockMvc diff --git a/api/src/test/kotlin/com/few/api/web/controller/admin/AdminControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/admin/AdminControllerTest.kt index b4d65e5f8..70f3c1d80 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/admin/AdminControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/admin/AdminControllerTest.kt @@ -1,6 +1,5 @@ package com.few.api.web.controller.admin -import com.epages.restdocs.apispec.ResourceDocumentation import com.epages.restdocs.apispec.ResourceDocumentation.resource import com.epages.restdocs.apispec.ResourceSnippetParameters import com.epages.restdocs.apispec.Schema @@ -12,49 +11,27 @@ import com.few.api.web.controller.admin.response.ImageSourceResponse import com.few.api.web.controller.description.Description import com.few.api.web.controller.helper.* import com.few.data.common.code.CategoryType -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.mockito.Mockito.doNothing import org.mockito.Mockito.`when` -import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType -import org.springframework.http.codec.json.Jackson2JsonDecoder -import org.springframework.http.codec.json.Jackson2JsonEncoder import org.springframework.mock.web.MockMultipartFile -import org.springframework.restdocs.RestDocumentationContextProvider import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.multipart +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post import org.springframework.restdocs.payload.PayloadDocumentation -import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation -import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import org.springframework.web.util.UriComponentsBuilder import java.net.URL class AdminControllerTest : ControllerTestSpec() { - @Autowired - lateinit var adminController: AdminController - companion object { private val BASE_URL = "/api/v1/admin" private val TAG = "AdminController" } - @BeforeEach - fun setUp(restDocumentation: RestDocumentationContextProvider) { - webTestClient = WebTestClient.bindToController(adminController) - .controllerAdvice(super.apiControllerExceptionHandler) - .httpMessageCodecs { - it.defaultCodecs().jackson2JsonEncoder(Jackson2JsonEncoder(objectMapper)) - it.defaultCodecs().jackson2JsonDecoder(Jackson2JsonDecoder(objectMapper)) - } - .configureClient() - .filter(WebTestClientRestDocumentation.documentationConfiguration(restDocumentation)) - .build() - } - @Test @DisplayName("[POST] /api/v1/admin/workbooks") fun addWorkbook() { @@ -72,16 +49,16 @@ class AdminControllerTest : ControllerTestSpec() { ) // when - this.webTestClient.post() - .uri(uri) - .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_JSON) - .bodyValue(body) - .exchange().expectStatus().is2xxSuccessful() - .expectBody().consumeWith( - WebTestClientRestDocumentation.document( + mockMvc.perform( + post(uri) + .content(body) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk) + .andDo( + document( api.toIdentifier(), - ResourceDocumentation.resource( + resource( ResourceSnippetParameters.builder().description("학습지 추가") .summary(api.toIdentifier()).privateResource(false).deprecated(false) .tag(TAG).requestSchema(Schema.schema(api.toRequestSchema())) @@ -178,16 +155,16 @@ class AdminControllerTest : ControllerTestSpec() { ).thenReturn(AddArticleUseCaseOut(1L)) // when - this.webTestClient.post() - .uri(uri) - .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_JSON) - .bodyValue(body) - .exchange().expectStatus().is2xxSuccessful() - .expectBody().consumeWith( - WebTestClientRestDocumentation.document( + mockMvc.perform( + post(uri) + .content(body) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk) + .andDo( + document( api.toIdentifier(), - ResourceDocumentation.resource( + resource( ResourceSnippetParameters.builder().description("아티클 추가") .summary(api.toIdentifier()).privateResource(false).deprecated(false) .tag(TAG).requestSchema(Schema.schema(api.toRequestSchema())) @@ -217,14 +194,14 @@ class AdminControllerTest : ControllerTestSpec() { doNothing().`when`(mapArticleUseCase).execute(MapArticleUseCaseIn(1L, 1L, 1)) // when - this.webTestClient.post() - .uri(uri) - .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_JSON) - .bodyValue(body) - .exchange().expectStatus().is2xxSuccessful() - .expectBody().consumeWith( - WebTestClientRestDocumentation.document( + mockMvc.perform( + post(uri) + .content(body) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk) + .andDo( + document( api.toIdentifier(), resource( ResourceSnippetParameters.builder().description("아티클 매핑") diff --git a/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt index 44a0ea559..3c6838e77 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt @@ -2,6 +2,7 @@ package com.few.api.web.controller.article import com.epages.restdocs.apispec.ResourceDocumentation import com.epages.restdocs.apispec.ResourceDocumentation.parameterWithName +import com.epages.restdocs.apispec.ResourceDocumentation.resource import com.epages.restdocs.apispec.ResourceSnippetParameters import com.epages.restdocs.apispec.Schema import com.few.api.domain.article.usecase.dto.* @@ -9,20 +10,13 @@ import com.few.api.web.controller.ControllerTestSpec import com.few.api.web.controller.description.Description import com.few.api.web.controller.helper.* import com.few.data.common.code.CategoryType -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.mockito.Mockito.`when` -import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType -import org.springframework.http.codec.json.Jackson2JsonDecoder -import org.springframework.http.codec.json.Jackson2JsonEncoder -import org.springframework.restdocs.RestDocumentationContextProvider import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get import org.springframework.restdocs.payload.PayloadDocumentation -import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation -import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import org.springframework.web.util.UriComponentsBuilder import java.net.URL @@ -30,27 +24,11 @@ import java.time.LocalDateTime class ArticleControllerTest : ControllerTestSpec() { - @Autowired - lateinit var articleController: ArticleController - companion object { private val BASE_URL = "/api/v1/articles" private val TAG = "ArticleController" } - @BeforeEach - fun setUp(restDocumentation: RestDocumentationContextProvider) { - webTestClient = WebTestClient.bindToController(articleController) - .controllerAdvice(super.apiControllerExceptionHandler) - .httpMessageCodecs { - it.defaultCodecs().jackson2JsonEncoder(Jackson2JsonEncoder(objectMapper)) - it.defaultCodecs().jackson2JsonDecoder(Jackson2JsonDecoder(objectMapper)) - } - .configureClient() - .filter(WebTestClientRestDocumentation.documentationConfiguration(restDocumentation)) - .build() - } - /** * 인증/비인증 모두 가능한 API */ @@ -88,11 +66,12 @@ class ArticleControllerTest : ControllerTestSpec() { mockMvc.perform( get(uri, articleId) .header("Authorization", "Bearer thisisaccesstoken") + .contentType(MediaType.APPLICATION_JSON) ).andExpect(status().is2xxSuccessful) .andDo( document( api.toIdentifier(), - ResourceDocumentation.resource( + resource( ResourceSnippetParameters.builder().description("아티클 Id로 아티클 조회") .summary(api.toIdentifier()).privateResource(false).deprecated(false) .tag(TAG).requestSchema(Schema.schema(api.toRequestSchema())) @@ -193,11 +172,13 @@ class ArticleControllerTest : ControllerTestSpec() { ) // when - this.webTestClient.get().uri(uri).accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk().expectBody().consumeWith( - WebTestClientRestDocumentation.document( + mockMvc.perform( + get(uri, prevArticleId, categoryCd).contentType(MediaType.APPLICATION_JSON) + ).andExpect(status().is2xxSuccessful) + .andDo( + document( api.toIdentifier(), - ResourceDocumentation.resource( + resource( ResourceSnippetParameters.builder().description("아티 목록 10개씩 조회(조회수 기반 정렬)") .summary(api.toIdentifier()).privateResource(false).deprecated(false) .tag(TAG).requestSchema(Schema.schema(api.toRequestSchema())) @@ -265,11 +246,13 @@ class ArticleControllerTest : ControllerTestSpec() { .toUriString() // when, then - this.webTestClient.get().uri(uri).accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk().expectBody().consumeWith( - WebTestClientRestDocumentation.document( + mockMvc.perform( + get(uri).contentType(MediaType.APPLICATION_JSON) + ).andExpect(status().is2xxSuccessful) + .andDo( + document( api.toIdentifier(), - ResourceDocumentation.resource( + resource( ResourceSnippetParameters.builder().description("아티클 카테고리 code, name 조회") .summary(api.toIdentifier()).privateResource(false).deprecated(false) .tag(TAG).requestSchema(Schema.schema(api.toRequestSchema())) diff --git a/api/src/test/kotlin/com/few/api/web/controller/member/MemberControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/member/MemberControllerTest.kt index 40ee90af6..cc7273daa 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/member/MemberControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/member/MemberControllerTest.kt @@ -1,6 +1,7 @@ package com.few.api.web.controller.member import com.epages.restdocs.apispec.ResourceDocumentation +import com.epages.restdocs.apispec.ResourceDocumentation.resource import com.epages.restdocs.apispec.ResourceSnippetParameters import com.epages.restdocs.apispec.Schema import com.few.api.domain.member.usecase.dto.SaveMemberUseCaseIn @@ -12,41 +13,23 @@ import com.few.api.web.controller.description.Description import com.few.api.web.controller.helper.* import com.few.api.web.controller.member.request.SaveMemberRequest import com.few.api.web.controller.member.request.TokenRequest -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.mockito.Mockito.`when` -import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType -import org.springframework.restdocs.RestDocumentationContextProvider import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post import org.springframework.restdocs.payload.PayloadDocumentation -import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation -import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import org.springframework.web.util.UriComponentsBuilder class MemberControllerTest : ControllerTestSpec() { - @Autowired - lateinit var memberController: MemberController - companion object { private val BASE_URL = "/api/v1/members" private val TAG = "MemberController" } - @BeforeEach - fun setUp(restDocumentation: RestDocumentationContextProvider) { - webTestClient = WebTestClient - .bindToController(memberController) - .controllerAdvice(super.apiControllerExceptionHandler) - .configureClient() - .filter(WebTestClientRestDocumentation.documentationConfiguration(restDocumentation)) - .build() - } - @Test @DisplayName("[POST] /api/v1/members") fun saveMember() { @@ -64,16 +47,16 @@ class MemberControllerTest : ControllerTestSpec() { `when`(saveMemberUseCase.execute(useCaseIn)).thenReturn(SaveMemberUseCaseOut(isSendAuthEmail = true)) // when - this.webTestClient.post() - .uri(uri) - .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_JSON) - .bodyValue(body) - .exchange().expectStatus().is2xxSuccessful() - .expectBody().consumeWith( - WebTestClientRestDocumentation.document( + mockMvc.perform( + post(uri) + .content(body) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk) + .andDo( + document( api.toIdentifier(), - ResourceDocumentation.resource( + resource( ResourceSnippetParameters.builder() .description("회원가입") .summary(api.toIdentifier()) @@ -132,16 +115,17 @@ class MemberControllerTest : ControllerTestSpec() { // when mockMvc.perform( post(uri) - .contentType(MediaType.APPLICATION_JSON) .queryParam("auth_token", auth_token) .queryParam("at", at.toString()) .queryParam("rt", rt.toString()) .content(body) - ).andExpect(status().is2xxSuccessful) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk) .andDo( document( api.toIdentifier(), - ResourceDocumentation.resource( + resource( ResourceSnippetParameters.builder() .summary(api.toIdentifier()) .description("토큰 발급") diff --git a/api/src/test/kotlin/com/few/api/web/controller/problem/ProblemControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/problem/ProblemControllerTest.kt index fa5cf414a..7c28df804 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/problem/ProblemControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/problem/ProblemControllerTest.kt @@ -1,7 +1,7 @@ package com.few.api.web.controller.problem -import com.epages.restdocs.apispec.ResourceDocumentation import com.epages.restdocs.apispec.ResourceDocumentation.parameterWithName +import com.epages.restdocs.apispec.ResourceDocumentation.resource import com.epages.restdocs.apispec.ResourceSnippetParameters import com.epages.restdocs.apispec.Schema import com.few.api.web.controller.ControllerTestSpec @@ -15,39 +15,25 @@ import com.few.api.domain.problem.usecase.dto.BrowseProblemsUseCaseOut import com.few.api.domain.problem.usecase.dto.CheckProblemUseCaseOut import com.few.api.domain.problem.usecase.dto.ReadProblemContentsUseCaseOutDetail import com.few.api.domain.problem.usecase.dto.ReadProblemUseCaseOut -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.mockito.Mockito import org.mockito.Mockito.`when` -import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType -import org.springframework.restdocs.RestDocumentationContextProvider +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post import org.springframework.restdocs.payload.PayloadDocumentation -import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation -import org.springframework.test.web.reactive.server.WebTestClient +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import org.springframework.web.util.UriComponentsBuilder class ProblemControllerTest : ControllerTestSpec() { - @Autowired - lateinit var problemController: ProblemController - companion object { private val BASE_URL = "/api/v1/problems" private val TAG = "ProblemController" } - @BeforeEach - fun setUp(restDocumentation: RestDocumentationContextProvider) { - webTestClient = WebTestClient - .bindToController(problemController) - .controllerAdvice(super.apiControllerExceptionHandler) - .configureClient() - .filter(WebTestClientRestDocumentation.documentationConfiguration(restDocumentation)) - .build() - } - @Test @DisplayName("[GET] /api/v1/problems?articleId=") fun browseProblems() { @@ -66,14 +52,14 @@ class ProblemControllerTest : ControllerTestSpec() { .thenReturn(useCaseOut) // when - this.webTestClient.get() - .uri(uri) - .accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk() - .expectBody().consumeWith( - WebTestClientRestDocumentation.document( + mockMvc.perform( + get(uri) + .contentType(MediaType.APPLICATION_JSON) + ).andExpect(status().is2xxSuccessful) + .andDo( + document( api.toIdentifier(), - ResourceDocumentation.resource( + resource( ResourceSnippetParameters.builder() .description("아티클 Id로 문제 목록 조회") .summary(api.toIdentifier()) @@ -126,14 +112,14 @@ class ProblemControllerTest : ControllerTestSpec() { .thenReturn(useCaseOut) // when - this.webTestClient.get() - .uri(uri, 1) - .accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk() - .expectBody().consumeWith( - WebTestClientRestDocumentation.document( + mockMvc.perform( + get(uri, problemId) + .contentType(MediaType.APPLICATION_JSON) + ).andExpect(status().is2xxSuccessful) + .andDo( + document( api.toIdentifier(), - ResourceDocumentation.resource( + resource( ResourceSnippetParameters.builder() .description("문제 Id로 문제 조회") .summary(api.toIdentifier()) @@ -188,16 +174,16 @@ class ProblemControllerTest : ControllerTestSpec() { `when`(checkProblemUseCase.execute(useCaseIn)).thenReturn(useCaseOut) // when - this.webTestClient.post() - .uri(uri, problemId) - .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_JSON) - .bodyValue(body) - .exchange().expectStatus().is2xxSuccessful() - .expectBody().consumeWith( - WebTestClientRestDocumentation.document( + mockMvc.perform( + post(uri, problemId) + .content(body) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk) + .andDo( + document( api.toIdentifier(), - ResourceDocumentation.resource( + resource( ResourceSnippetParameters.builder() .description("문제 Id로 문제 정답 확인") .summary(api.toIdentifier()) diff --git a/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt index d20e5d4b5..50b92bfe6 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt @@ -11,49 +11,26 @@ import com.few.api.domain.subscription.usecase.dto.* import com.few.api.web.controller.helper.* import com.few.api.web.support.ViewCategory import com.few.api.web.support.WorkBookStatus -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.mockito.Mockito.doNothing import org.mockito.Mockito.doReturn -import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType -import org.springframework.http.codec.json.Jackson2JsonDecoder -import org.springframework.http.codec.json.Jackson2JsonEncoder -import org.springframework.restdocs.RestDocumentationContextProvider import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post import org.springframework.restdocs.payload.PayloadDocumentation -import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation import org.springframework.security.test.context.support.WithUserDetails -import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.test.web.servlet.result.MockMvcResultMatchers import org.springframework.web.util.UriComponentsBuilder class SubscriptionControllerTest : ControllerTestSpec() { - @Autowired - lateinit var subscriptionController: SubscriptionController - companion object { private val BASE_URL = "/api/v1/" private val TAG = "WorkbookSubscriptionController" } - @BeforeEach - fun setUp(restDocumentation: RestDocumentationContextProvider) { - webTestClient = WebTestClient - .bindToController(subscriptionController) - .controllerAdvice(super.apiControllerExceptionHandler).httpMessageCodecs { - it.defaultCodecs().jackson2JsonEncoder(Jackson2JsonEncoder(objectMapper)) - it.defaultCodecs().jackson2JsonDecoder(Jackson2JsonDecoder(objectMapper)) - } - .configureClient() - .filter(WebTestClientRestDocumentation.documentationConfiguration(restDocumentation)) - .build() - } - @Test @DisplayName("[GET] /api/v1/subscriptions/workbooks") @WithUserDetails(userDetailsServiceBeanName = "testTokenUserDetailsService") diff --git a/api/src/test/kotlin/com/few/api/web/controller/workbook/WorkBookControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/workbook/WorkBookControllerTest.kt index 999e262ba..1e0717f1c 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/workbook/WorkBookControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/workbook/WorkBookControllerTest.kt @@ -13,19 +13,12 @@ import com.few.api.web.support.ViewCategory import com.few.api.web.support.WorkBookCategory import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get import com.few.data.common.code.CategoryType -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.mockito.Mockito.`when` -import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType -import org.springframework.http.codec.json.Jackson2JsonDecoder -import org.springframework.http.codec.json.Jackson2JsonEncoder -import org.springframework.restdocs.RestDocumentationContextProvider import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document import org.springframework.restdocs.payload.PayloadDocumentation -import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation -import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import org.springframework.web.util.UriComponentsBuilder import java.net.URL @@ -33,27 +26,11 @@ import java.time.LocalDateTime class WorkBookControllerTest : ControllerTestSpec() { - @Autowired - lateinit var workBookController: WorkBookController - companion object { private val BASE_URL = "/api/v1/workbooks" private val TAG = "WorkBookController" } - @BeforeEach - fun setUp(restDocumentation: RestDocumentationContextProvider) { - webTestClient = WebTestClient - .bindToController(workBookController) - .controllerAdvice(super.apiControllerExceptionHandler).httpMessageCodecs { - it.defaultCodecs().jackson2JsonEncoder(Jackson2JsonEncoder(objectMapper)) - it.defaultCodecs().jackson2JsonDecoder(Jackson2JsonDecoder(objectMapper)) - } - .configureClient() - .filter(WebTestClientRestDocumentation.documentationConfiguration(restDocumentation)) - .build() - } - @Test @DisplayName("[GET] /api/v1/workbooks/categories") fun browseWorkBookCategories() { @@ -230,14 +207,14 @@ class WorkBookControllerTest : ControllerTestSpec() { ) // when - this.webTestClient.get() - .uri(uri, 1) - .accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk() - .expectBody().consumeWith( - WebTestClientRestDocumentation.document( + mockMvc.perform( + get(uri, workbookId) + .contentType(MediaType.APPLICATION_JSON) + ).andExpect(status().is2xxSuccessful) + .andDo( + document( api.toIdentifier(), - ResourceDocumentation.resource( + resource( ResourceSnippetParameters.builder() .description("학습지 Id를 입력하여 학습지 정보를 조회합니다.") .summary(api.toIdentifier()) diff --git a/api/src/test/kotlin/com/few/api/web/controller/workbook/article/WorkBookArticleControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/workbook/article/WorkBookArticleControllerTest.kt index c3d610b72..4289d7856 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/workbook/article/WorkBookArticleControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/workbook/article/WorkBookArticleControllerTest.kt @@ -11,20 +11,13 @@ import com.few.api.web.controller.ControllerTestSpec import com.few.api.web.controller.description.Description import com.few.api.web.controller.helper.* import com.few.data.common.code.CategoryType -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.mockito.Mockito.`when` -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.http.codec.json.Jackson2JsonDecoder -import org.springframework.http.codec.json.Jackson2JsonEncoder -import org.springframework.restdocs.RestDocumentationContextProvider import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get import org.springframework.restdocs.payload.PayloadDocumentation -import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation import org.springframework.security.test.context.support.WithUserDetails -import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.test.web.servlet.result.MockMvcResultMatchers import org.springframework.web.util.UriComponentsBuilder import java.net.URL @@ -32,27 +25,11 @@ import java.time.LocalDateTime class WorkBookArticleControllerTest : ControllerTestSpec() { - @Autowired - lateinit var workBookArticleController: WorkBookArticleController - companion object { private val BASE_URL = "/api/v1/workbooks/{workbookId}/articles" private val TAG = "WorkBookArticleController" } - @BeforeEach - fun setUp(restDocumentation: RestDocumentationContextProvider) { - webTestClient = WebTestClient - .bindToController(workBookArticleController) - .controllerAdvice(super.apiControllerExceptionHandler).httpMessageCodecs { - it.defaultCodecs().jackson2JsonEncoder(Jackson2JsonEncoder(objectMapper)) - it.defaultCodecs().jackson2JsonDecoder(Jackson2JsonDecoder(objectMapper)) - } - .configureClient() - .filter(WebTestClientRestDocumentation.documentationConfiguration(restDocumentation)) - .build() - } - @Test @DisplayName("[GET] /api/v1/workbooks/{workbookId}/articles/{articleId}") @WithUserDetails(userDetailsServiceBeanName = "testTokenUserDetailsService") From f090c8c383880c4d78195fe738ec401478bf3bd2 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Thu, 8 Aug 2024 23:18:55 +0900 Subject: [PATCH 2/8] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=20=ED=86=B5=EC=9D=BC=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/admin/AdminControllerTest.kt | 144 +++++++++--------- .../controller/admin/ApiLogControllerTest.kt | 9 +- .../article/ArticleControllerTest.kt | 106 ++++++------- .../controller/member/MemberControllerTest.kt | 19 ++- .../problem/ProblemControllerTest.kt | 15 +- .../SubscriptionControllerTest.kt | 28 ++-- .../workbook/WorkBookControllerTest.kt | 84 +++++----- .../article/WorkBookArticleControllerTest.kt | 43 +++--- 8 files changed, 222 insertions(+), 226 deletions(-) diff --git a/api/src/test/kotlin/com/few/api/web/controller/admin/AdminControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/admin/AdminControllerTest.kt index 70f3c1d80..d4567fd68 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/admin/AdminControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/admin/AdminControllerTest.kt @@ -3,11 +3,9 @@ package com.few.api.web.controller.admin import com.epages.restdocs.apispec.ResourceDocumentation.resource import com.epages.restdocs.apispec.ResourceSnippetParameters import com.epages.restdocs.apispec.Schema -import com.few.api.domain.admin.document.usecase.* import com.few.api.domain.admin.document.usecase.dto.* import com.few.api.web.controller.ControllerTestSpec import com.few.api.web.controller.admin.request.* -import com.few.api.web.controller.admin.response.ImageSourceResponse import com.few.api.web.controller.description.Description import com.few.api.web.controller.helper.* import com.few.data.common.code.CategoryType @@ -24,12 +22,13 @@ import org.springframework.restdocs.payload.PayloadDocumentation import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import org.springframework.web.util.UriComponentsBuilder import java.net.URL +import java.util.stream.IntStream class AdminControllerTest : ControllerTestSpec() { companion object { - private val BASE_URL = "/api/v1/admin" - private val TAG = "AdminController" + private const val BASE_URL = "/api/v1/admin" + private const val TAG = "AdminController" } @Test @@ -44,9 +43,10 @@ class AdminControllerTest : ControllerTestSpec() { val description = "description" val request = AddWorkbookRequest(title, mainImageUrl, category, description) val body = objectMapper.writeValueAsString(request) - `when`(addWorkbookUseCase.execute(AddWorkbookUseCaseIn(title, mainImageUrl, category, description))).thenReturn( - AddWorkbookUseCaseOut(1L) - ) + + val useCaseIn = AddWorkbookUseCaseIn(title, mainImageUrl, category, description) + val useCaseOut = AddWorkbookUseCaseOut(1L) + `when`(addWorkbookUseCase.execute(useCaseIn)).thenReturn(useCaseOut) // when mockMvc.perform( @@ -83,76 +83,54 @@ class AdminControllerTest : ControllerTestSpec() { // given val api = "AddArticle" val uri = UriComponentsBuilder.newInstance().path("$BASE_URL/articles").build().toUriString() - val request = AddArticleRequest( - "writer@gmail.com", - URL("http://localhost:8080"), - "title", - CategoryType.fromCode(0)!!.name, - "md", - "content source", - listOf( - ProblemDto( - "title1", - listOf( - ProblemContentDto(1L, "content1"), - ProblemContentDto(2L, "content2"), - ProblemContentDto(3L, "content3"), - ProblemContentDto(4L, "content4") - ), - "1", - "explanation" - ), - ProblemDto( - "title2", - listOf( - ProblemContentDto(1L, "content1"), - ProblemContentDto(2L, "content2"), - ProblemContentDto(3L, "content3"), - ProblemContentDto(4L, "content4") - ), - "2", - "explanation" - ) + val writerEmail = "writer@gmail.com" + val articleImageURL = URL("http://localhost:8080") + val title = "title" + val category = CategoryType.fromCode(0)!!.name + val contentType = "md" + val contentSource = "content source" + val problemDTOs = IntStream.range(0, 2).mapToObj { + ProblemDto( + "title$it", + IntStream.range(0, 4).mapToObj { ProblemContentDto(it.toLong(), "content$it") }.toList(), + "$it", + "explanation$it" + ) + }.toList() + val problemDetails = problemDTOs.map { + ProblemDetail( + it.title, + it.contents.map { content -> ProblemContentDetail(content.number, content.content) }, + it.answer, + it.explanation ) + } + val request = AddArticleRequest( + writerEmail, + articleImageURL, + title, + category, + contentType, + contentSource, + problemDTOs ) val body = objectMapper.writeValueAsString(request) + val useCaseIn = AddArticleUseCaseIn( + writerEmail, + articleImageURL, + title, + category, + contentType, + contentSource, + problemDetails + ) + val useCaseOut = AddArticleUseCaseOut(1L) `when`( addArticleUseCase.execute( - AddArticleUseCaseIn( - "writer@gmail.com", - URL("http://localhost:8080"), - "title", - CategoryType.fromCode(0)!!.name, - "md", - "content source", - listOf( - ProblemDetail( - "title1", - listOf( - ProblemContentDetail(1L, "content1"), - ProblemContentDetail(2L, "content2"), - ProblemContentDetail(3L, "content3"), - ProblemContentDetail(4L, "content4") - ), - "1", - "explanation" - ), - ProblemDetail( - "title2", - listOf( - ProblemContentDetail(1L, "content1"), - ProblemContentDetail(2L, "content2"), - ProblemContentDetail(3L, "content3"), - ProblemContentDetail(4L, "content4") - ), - "2", - "explanation" - ) - ) - ) + useCaseIn ) - ).thenReturn(AddArticleUseCaseOut(1L)) + ).thenReturn(useCaseOut) // when mockMvc.perform( @@ -189,9 +167,14 @@ class AdminControllerTest : ControllerTestSpec() { // given val api = "MapArticle" val uri = UriComponentsBuilder.newInstance().path("$BASE_URL/relations/articles").build().toUriString() - val request = MapArticleRequest(1L, 1L, 1) + val workbookId = 1L + val articleId = 1L + val dayCol = 1 + val request = MapArticleRequest(workbookId, articleId, dayCol) val body = objectMapper.writeValueAsString(request) - doNothing().`when`(mapArticleUseCase).execute(MapArticleUseCaseIn(1L, 1L, 1)) + + val useCaseIn = MapArticleUseCaseIn(workbookId, articleId, dayCol) + doNothing().`when`(mapArticleUseCase).execute(useCaseIn) // when mockMvc.perform( @@ -221,7 +204,12 @@ class AdminControllerTest : ControllerTestSpec() { // given val api = "ConvertContent" val uri = UriComponentsBuilder.newInstance().path("$BASE_URL/utilities/conversion/content").build().toUriString() - val request = ConvertContentRequest(MockMultipartFile("content", "test.md", "text/markdown", "#test".toByteArray())) + val name = "content" + val originalFilename = "test.md" + val contentType = "text/markdown" + val content = "#test".toByteArray() + val request = ConvertContentRequest(MockMultipartFile(name, originalFilename, contentType, content)) + val useCaseOut = ConvertContentUseCaseOut("converted content", URL("http://localhost:8080/test.md")) val useCaseIn = ConvertContentUseCaseIn(request.content) `when`(convertContentUseCase.execute(useCaseIn)).thenReturn(useCaseOut) @@ -265,9 +253,13 @@ class AdminControllerTest : ControllerTestSpec() { // given val api = "PutImage" val uri = UriComponentsBuilder.newInstance().path("$BASE_URL/utilities/conversion/image").build().toUriString() - val request = ImageSourceRequest(source = MockMultipartFile("source", "test.jpg", "image/jpeg", "test".toByteArray())) - val response = ImageSourceResponse(URL("http://localhost:8080/test.jpg"), listOf("jpg", "webp")) - val useCaseOut = PutImageUseCaseOut(response.url, response.supportSuffix) + val name = "source" + val originalFilename = "test.jpg" + val contentType = "image/jpeg" + val content = "test".toByteArray() + val request = ImageSourceRequest(source = MockMultipartFile(name, originalFilename, contentType, content)) + + val useCaseOut = PutImageUseCaseOut(URL("http://localhost:8080/test.jpg"), listOf("jpg", "webp")) val useCaseIn = PutImageUseCaseIn(request.source) `when`(putImageUseCase.execute(useCaseIn)).thenReturn(useCaseOut) diff --git a/api/src/test/kotlin/com/few/api/web/controller/admin/ApiLogControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/admin/ApiLogControllerTest.kt index 09131b083..03da8a246 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/admin/ApiLogControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/admin/ApiLogControllerTest.kt @@ -20,8 +20,8 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status class ApiLogControllerTest : ControllerTestSpec() { companion object { - private val BASE_URL = "/api/v1/logs" - private val TAG = "ApiLogControllerTest" + private const val BASE_URL = "/api/v1/logs" + private const val TAG = "ApiLogControllerTest" } @Test @@ -32,14 +32,15 @@ class ApiLogControllerTest : ControllerTestSpec() { val history = objectMapper.writeValueAsString(mapOf("from" to "email", "to" to "readArticle")) val request = ApiLogRequest(history) - val content = objectMapper.writeValueAsString(request) + val body = objectMapper.writeValueAsString(request) + val useCaseIn = AddApiLogUseCaseIn(history) Mockito.doNothing().`when`(addApiLogUseCase).execute(useCaseIn) // When mockMvc.perform( post(BASE_URL) - .content(content) + .content(body) .contentType(MediaType.APPLICATION_JSON) ).andExpect( status().is2xxSuccessful diff --git a/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt index 3c6838e77..19ef02f59 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt @@ -21,12 +21,13 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import org.springframework.web.util.UriComponentsBuilder import java.net.URL import java.time.LocalDateTime +import java.util.stream.IntStream class ArticleControllerTest : ControllerTestSpec() { companion object { - private val BASE_URL = "/api/v1/articles" - private val TAG = "ArticleController" + private const val BASE_URL = "/api/v1/articles" + private const val TAG = "ArticleController" } /** @@ -37,35 +38,38 @@ class ArticleControllerTest : ControllerTestSpec() { fun readArticle() { // given val api = "ReadArticle" + val token = "thisisaccesstoken" val uri = UriComponentsBuilder.newInstance().path("$BASE_URL/{articleId}").build().toUriString() - // set usecase mock val articleId = 1L + val memberId = 1L + `when`(tokenResolver.resolveId(token)).thenReturn(memberId) - `when`(tokenResolver.resolveId("thisisaccesstoken")).thenReturn(memberId) - `when`(readArticleUseCase.execute(ReadArticleUseCaseIn(articleId, memberId))).thenReturn( - ReadArticleUseCaseOut( + val useCaseIn = ReadArticleUseCaseIn(articleId, memberId) + val useCaseOut = ReadArticleUseCaseOut( + id = 1L, + writer = WriterDetail( id = 1L, - writer = WriterDetail( - id = 1L, - name = "안나포", - url = URL("http://localhost:8080/api/v1/writers/1"), - imageUrl = URL("https://github.com/user-attachments/assets/28df9078-488c-49d6-9375-54ce5a250742") - ), - mainImageUrl = URL("https://github.com/YAPP-Github/24th-Web-Team-1-BE/assets/102807742/0643d805-5f3a-4563-8c48-2a7d51795326"), - title = "ETF(상장 지수 펀드)란? 모르면 손해라고?", - content = CategoryType.fromCode(0)!!.name, - problemIds = listOf(1L, 2L, 3L), - category = "경제", - createdAt = LocalDateTime.now(), - views = 1L - ) + name = "안나포", + url = URL("http://localhost:8080/api/v1/writers/1"), + imageUrl = URL("https://github.com/user-attachments/assets/28df9078-488c-49d6-9375-54ce5a250742") + ), + mainImageUrl = URL("https://github.com/YAPP-Github/24th-Web-Team-1-BE/assets/102807742/0643d805-5f3a-4563-8c48-2a7d51795326"), + title = "ETF(상장 지수 펀드)란? 모르면 손해라고?", + content = CategoryType.fromCode(0)!!.name, + problemIds = listOf(1L, 2L, 3L), + category = "경제", + createdAt = LocalDateTime.now(), + views = 1L + ) + `when`(readArticleUseCase.execute(useCaseIn)).thenReturn( + useCaseOut ) // when mockMvc.perform( get(uri, articleId) - .header("Authorization", "Bearer thisisaccesstoken") + .header("Authorization", "Bearer $token") .contentType(MediaType.APPLICATION_JSON) ).andExpect(status().is2xxSuccessful) .andDo( @@ -131,45 +135,41 @@ class ArticleControllerTest : ControllerTestSpec() { val prevArticleId = 1L val categoryCd: Byte = CategoryType.IT.code val uri = UriComponentsBuilder.newInstance() - .path("$BASE_URL") + .path(BASE_URL) .queryParam("prevArticleId", prevArticleId) .queryParam("categoryCd", categoryCd) .build() .toUriString() - // set usecase mock - `when`(readArticlesUseCase.execute(ReadArticlesUseCaseIn(prevArticleId, categoryCd))).thenReturn( - ReadArticlesUseCaseOut( - listOf( - ReadArticleUseCaseOut( + + val useCaseIn = ReadArticlesUseCaseIn(prevArticleId, categoryCd) + val useCaseOut = ReadArticlesUseCaseOut( + IntStream.range(0, 10).mapToObj { + ReadArticleUseCaseOut( + id = it.toLong(), + writer = WriterDetail( id = 1L, - writer = WriterDetail( - id = 1L, - name = "안나포", - url = URL("http://localhost:8080/api/v1/writers/1"), - imageUrl = URL("https://github.com/user-attachments/assets/28df9078-488c-49d6-9375-54ce5a250742") - ), - mainImageUrl = URL("https://github.com/YAPP-Github/24th-Web-Team-1-BE/assets/102807742/0643d805-5f3a-4563-8c48-2a7d51795326"), - title = "ETF(상장 지수 펀드)란? 모르면 손해라고?", - content = CategoryType.fromCode(0)!!.name, - problemIds = emptyList(), - category = CategoryType.IT.displayName, - createdAt = LocalDateTime.now(), - views = 9876543210L, - workbooks = listOf( - WorkbookDetail( - id = 1, - title = "워크북1 타이틀" - ), - WorkbookDetail( - id = 2, - title = "워크북2 타이틀" - ) + name = "writer$it", + url = URL("http://localhost:8080/api/v1/writers/$it"), + imageUrl = URL("http://localhost:8080/api/v1/writers/images/$it") + ), + mainImageUrl = URL("http://localhost:8080/api/v1/articles/main/images/$it"), + title = "title$it", + content = "content$it", + problemIds = emptyList(), + category = CategoryType.ECONOMY.displayName, + createdAt = LocalDateTime.now(), + views = it.toLong(), + workbooks = IntStream.range(0, 2).mapToObj { j -> + WorkbookDetail( + id = "$it$j".toLong(), + title = "workbook$it$j" ) - ) - ), - true - ) + }.toList() + ) + }.toList(), + true ) + `when`(readArticlesUseCase.execute(useCaseIn)).thenReturn(useCaseOut) // when mockMvc.perform( diff --git a/api/src/test/kotlin/com/few/api/web/controller/member/MemberControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/member/MemberControllerTest.kt index cc7273daa..4652ebf0f 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/member/MemberControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/member/MemberControllerTest.kt @@ -26,8 +26,8 @@ import org.springframework.web.util.UriComponentsBuilder class MemberControllerTest : ControllerTestSpec() { companion object { - private val BASE_URL = "/api/v1/members" - private val TAG = "MemberController" + private const val BASE_URL = "/api/v1/members" + private const val TAG = "MemberController" } @Test @@ -42,9 +42,9 @@ class MemberControllerTest : ControllerTestSpec() { val email = "test@gmail.com" val body = objectMapper.writeValueAsString(SaveMemberRequest(email = email)) - // set mock val useCaseIn = SaveMemberUseCaseIn(email = email) - `when`(saveMemberUseCase.execute(useCaseIn)).thenReturn(SaveMemberUseCaseOut(isSendAuthEmail = true)) + val useCaseOut = SaveMemberUseCaseOut(isSendAuthEmail = true) + `when`(saveMemberUseCase.execute(useCaseIn)).thenReturn(useCaseOut) // when mockMvc.perform( @@ -104,13 +104,12 @@ class MemberControllerTest : ControllerTestSpec() { rt = rt, refreshToken = tokenRequest.refreshToken ) - `when`(tokenUseCase.execute(useCaseIn)).thenReturn( - TokenUseCaseOut( - accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6NTcsIm1lbWJlclJvbGUiOiJbUk9MRV9VU0VSXSIsImlhdCI6MTcyMjI1NTE4MywiZXhwIjoxNzUzODEyNzgzfQ.1KXRim0MVvz1vxOQB_700XPCD9zPQtHNItF_A9upvA8", - refreshToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6NTcsIm1lbWJlclJvbGUiOiJbUk9MRV9VU0VSXSIsImlhdCI6MTcyMjI1NTE4MywiZXhwIjoxNzUzODEyNzgzfQ.1KXRim0MVvz1vxOQB_700XPCD9zPQtHNItF_A9upvA8", - isLogin = false - ) + val useCaseOut = TokenUseCaseOut( + accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6NTcsIm1lbWJlclJvbGUiOiJbUk9MRV9VU0VSXSIsImlhdCI6MTcyMjI1NTE4MywiZXhwIjoxNzUzODEyNzgzfQ.1KXRim0MVvz1vxOQB_700XPCD9zPQtHNItF_A9upvA8", + refreshToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6NTcsIm1lbWJlclJvbGUiOiJbUk9MRV9VU0VSXSIsImlhdCI6MTcyMjI1NTE4MywiZXhwIjoxNzUzODEyNzgzfQ.1KXRim0MVvz1vxOQB_700XPCD9zPQtHNItF_A9upvA8", + isLogin = false ) + `when`(tokenUseCase.execute(useCaseIn)).thenReturn(useCaseOut) // when mockMvc.perform( diff --git a/api/src/test/kotlin/com/few/api/web/controller/problem/ProblemControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/problem/ProblemControllerTest.kt index 7c28df804..afaed0955 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/problem/ProblemControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/problem/ProblemControllerTest.kt @@ -17,7 +17,6 @@ import com.few.api.domain.problem.usecase.dto.ReadProblemContentsUseCaseOutDetai import com.few.api.domain.problem.usecase.dto.ReadProblemUseCaseOut import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import org.mockito.Mockito import org.mockito.Mockito.`when` import org.springframework.http.MediaType import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document @@ -30,8 +29,8 @@ import org.springframework.web.util.UriComponentsBuilder class ProblemControllerTest : ControllerTestSpec() { companion object { - private val BASE_URL = "/api/v1/problems" - private val TAG = "ProblemController" + private const val BASE_URL = "/api/v1/problems" + private const val TAG = "ProblemController" } @Test @@ -48,8 +47,7 @@ class ProblemControllerTest : ControllerTestSpec() { val useCaseIn = BrowseProblemsUseCaseIn(articleId) val useCaseOut = BrowseProblemsUseCaseOut(listOf(1L, 2L, 3L)) - `when`(browseProblemsUseCase.execute(useCaseIn)) - .thenReturn(useCaseOut) + `when`(browseProblemsUseCase.execute(useCaseIn)).thenReturn(useCaseOut) // when mockMvc.perform( @@ -95,7 +93,6 @@ class ProblemControllerTest : ControllerTestSpec() { .build() .toUriString() - // set usecase mock val problemId = 1L val useCaseIn = ReadProblemUseCaseIn(problemId) val useCaseOut = ReadProblemUseCaseOut( @@ -108,8 +105,7 @@ class ProblemControllerTest : ControllerTestSpec() { ReadProblemContentsUseCaseOutDetail(4L, "투명성") ) ) - Mockito.`when`(readProblemUseCase.execute(useCaseIn)) - .thenReturn(useCaseOut) + `when`(readProblemUseCase.execute(useCaseIn)).thenReturn(useCaseOut) // when mockMvc.perform( @@ -160,11 +156,10 @@ class ProblemControllerTest : ControllerTestSpec() { val api = "CheckProblem" val uri = UriComponentsBuilder.newInstance() .path("$BASE_URL/{problemId}").build().toUriString() - - // set usecase mock val problemId = 1L val sub = "제출답" val body = objectMapper.writeValueAsString(CheckProblemRequest(sub = sub)) + val useCaseIn = CheckProblemUseCaseIn(problemId, sub = sub) val useCaseOut = CheckProblemUseCaseOut( explanation = "ETF는 일반적으로 낮은 운용 비용을 특징으로 합니다.이는 ETF가 보통 지수 추종(passive management) 방식으로 운용되기 때문입니다. 지수를 추종하는 전략은 액티브 매니지먼트(active management)에 비해 관리가 덜 복잡하고, 따라서 비용이 낮습니다.", diff --git a/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt index 50b92bfe6..2f9a76928 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt @@ -27,8 +27,8 @@ import org.springframework.web.util.UriComponentsBuilder class SubscriptionControllerTest : ControllerTestSpec() { companion object { - private val BASE_URL = "/api/v1/" - private val TAG = "WorkbookSubscriptionController" + private const val BASE_URL = "/api/v1/" + private const val TAG = "WorkbookSubscriptionController" } @Test @@ -37,6 +37,7 @@ class SubscriptionControllerTest : ControllerTestSpec() { fun browseSubscribeWorkbooks() { // given val api = "BrowseSubscribeWorkBooks" + val token = "thisisaccesstoken" val view = ViewCategory.MAIN_CARD val uri = UriComponentsBuilder.newInstance() .path("$BASE_URL/subscriptions/workbooks") @@ -44,7 +45,6 @@ class SubscriptionControllerTest : ControllerTestSpec() { .build() .toUriString() - // set usecase mock val memberId = 1L val useCaseIn = BrowseSubscribeWorkbooksUseCaseIn(memberId) val useCaseOut = BrowseSubscribeWorkbooksUseCaseOut( @@ -78,13 +78,12 @@ class SubscriptionControllerTest : ControllerTestSpec() { ) ) ) - doReturn(useCaseOut).`when`(browseSubscribeWorkbooksUseCase).execute(useCaseIn) // when mockMvc.perform( get(uri) - .header("Authorization", "Bearer thisisaccesstoken") + .header("Authorization", "Bearer $token") .contentType(MediaType.APPLICATION_JSON) ).andExpect(MockMvcResultMatchers.status().is2xxSuccessful) .andDo( @@ -140,13 +139,11 @@ class SubscriptionControllerTest : ControllerTestSpec() { fun subscribeWorkbook() { // given val api = "SubscribeWorkBook" + val token = "thisisaccesstoken" val uri = UriComponentsBuilder.newInstance() .path("$BASE_URL/workbooks/{workbookId}/subs") .build().toUriString() - val email = "test@gmail.com" - - // set usecase mock val memberId = 1L val workbookId = 1L val useCaseIn = SubscribeWorkbookUseCaseIn(workbookId = workbookId, memberId = memberId) @@ -155,7 +152,7 @@ class SubscriptionControllerTest : ControllerTestSpec() { // when mockMvc.perform( post(uri, workbookId) - .header("Authorization", "Bearer thisisaccesstoken") + .header("Authorization", "Bearer $token") .contentType(MediaType.APPLICATION_JSON) ).andExpect(MockMvcResultMatchers.status().is2xxSuccessful) .andDo( @@ -191,15 +188,15 @@ class SubscriptionControllerTest : ControllerTestSpec() { fun unsubscribeWorkbook() { // given val api = "UnsubscribeWorkBook" + val token = "thisisaccesstoken" val uri = UriComponentsBuilder.newInstance() .path("$BASE_URL/workbooks/{workbookId}/unsubs") .build() .toUriString() - // set usecase mock val workbookId = 1L val memberId = 1L - val opinion = "취소합니다." + val opinion = "cancel." val body = objectMapper.writeValueAsString( UnsubscribeWorkbookRequest(opinion = opinion) ) @@ -213,7 +210,7 @@ class SubscriptionControllerTest : ControllerTestSpec() { // when mockMvc.perform( post(uri, workbookId) - .header("Authorization", "Bearer thisisaccesstoken") + .header("Authorization", "Bearer $token") .contentType(MediaType.APPLICATION_JSON) .content(body) ).andExpect(MockMvcResultMatchers.status().is2xxSuccessful) @@ -250,17 +247,18 @@ class SubscriptionControllerTest : ControllerTestSpec() { fun deactivateAllSubscriptions() { // given val api = "UnsubscribeAll" + val token = "thisisaccesstoken" val uri = UriComponentsBuilder.newInstance() .path("$BASE_URL/subscriptions/unsubs") .build() .toUriString() - // set usecase mock - val memberId = 1L val opinion = "전체 수신거부합니다." val body = objectMapper.writeValueAsString( UnsubscribeWorkbookRequest(opinion = opinion) ) + + val memberId = 1L val useCaseIn = UnsubscribeAllUseCaseIn( memberId = memberId, opinion = opinion @@ -270,7 +268,7 @@ class SubscriptionControllerTest : ControllerTestSpec() { // when mockMvc.perform( post(uri) - .header("Authorization", "Bearer thisisaccesstoken") + .header("Authorization", "Bearer $token") .content(body) .contentType(MediaType.APPLICATION_JSON) ).andExpect(MockMvcResultMatchers.status().is2xxSuccessful) diff --git a/api/src/test/kotlin/com/few/api/web/controller/workbook/WorkBookControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/workbook/WorkBookControllerTest.kt index 1e0717f1c..d00278892 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/workbook/WorkBookControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/workbook/WorkBookControllerTest.kt @@ -27,8 +27,8 @@ import java.time.LocalDateTime class WorkBookControllerTest : ControllerTestSpec() { companion object { - private val BASE_URL = "/api/v1/workbooks" - private val TAG = "WorkBookController" + private const val BASE_URL = "/api/v1/workbooks" + private const val TAG = "WorkBookController" } @Test @@ -83,8 +83,8 @@ class WorkBookControllerTest : ControllerTestSpec() { fun browseWorkBooks() { // given val api = "BrowseWorkBooks" + val token = "thisisaccesstoken" val viewCategory = ViewCategory.MAIN_CARD - val memberId = 1L val uri = UriComponentsBuilder.newInstance() .path(BASE_URL) .queryParam("category", WorkBookCategory.ECONOMY.code) @@ -92,33 +92,35 @@ class WorkBookControllerTest : ControllerTestSpec() { .build() .toUriString() - // set usecase mock - `when`(tokenResolver.resolveId("thisisaccesstoken")).thenReturn(memberId) - `when`(browseWorkBooksUseCase.execute(BrowseWorkbooksUseCaseIn(WorkBookCategory.ECONOMY, viewCategory, memberId))).thenReturn( - BrowseWorkbooksUseCaseOut( - workbooks = listOf( - BrowseWorkBookDetail( - id = 1L, - mainImageUrl = URL("http://localhost:8080/api/v1/workbooks/1/image"), - title = "재태크, 투자 필수 용어 모음집", - description = "사회 초년생부터, 직장인, 은퇴자까지 모두가 알아야 할 기본적인 재태크, 투자 필수 용어 모음집 입니다.", - category = CategoryType.fromCode(0)!!.name, - createdAt = LocalDateTime.now(), - writerDetails = listOf( - WriterDetail(1L, "안나포", URL("http://localhost:8080/api/v1/users/1")), - WriterDetail(2L, "퓨퓨", URL("http://localhost:8080/api/v1/users/2")), - WriterDetail(3L, "프레소", URL("http://localhost:8080/api/v1/users/3")) - ), - subscriptionCount = 100 - ) + val memberId = 1L + `when`(tokenResolver.resolveId(token)).thenReturn(memberId) + + val useCaseIn = + BrowseWorkbooksUseCaseIn(WorkBookCategory.ECONOMY, viewCategory, memberId) + val useCaseOut = BrowseWorkbooksUseCaseOut( + workbooks = listOf( + BrowseWorkBookDetail( + id = 1L, + mainImageUrl = URL("http://localhost:8080/api/v1/workbooks/1/image"), + title = "재태크, 투자 필수 용어 모음집", + description = "사회 초년생부터, 직장인, 은퇴자까지 모두가 알아야 할 기본적인 재태크, 투자 필수 용어 모음집 입니다.", + category = CategoryType.fromCode(0)!!.name, + createdAt = LocalDateTime.now(), + writerDetails = listOf( + WriterDetail(1L, "안나포", URL("http://localhost:8080/api/v1/users/1")), + WriterDetail(2L, "퓨퓨", URL("http://localhost:8080/api/v1/users/2")), + WriterDetail(3L, "프레소", URL("http://localhost:8080/api/v1/users/3")) + ), + subscriptionCount = 100 ) ) ) + `when`(browseWorkBooksUseCase.execute(useCaseIn)).thenReturn(useCaseOut) // when mockMvc.perform( get(uri) - .header("Authorization", "Bearer thisisaccesstoken") + .header("Authorization", "Bearer $token") .contentType(MediaType.APPLICATION_JSON) ).andExpect( status().is2xxSuccessful @@ -187,24 +189,30 @@ class WorkBookControllerTest : ControllerTestSpec() { .path("$BASE_URL/{workbookId}") .build() .toUriString() - // set usecase mock + val workbookId = 1L - `when`(readWorkbookUseCase.execute(ReadWorkbookUseCaseIn(workbookId))).thenReturn( - ReadWorkbookUseCaseOut( - id = 1L, - mainImageUrl = URL("http://localhost:8080/api/v1/workbooks/1/image"), - title = "재태크, 투자 필수 용어 모음집", - description = "사회 초년생부터, 직장인, 은퇴자까지 모두가 알아야 할 기본적인 재태크, 투자 필수 용어 모음집 입니다.", - category = CategoryType.fromCode(0)!!.name, - createdAt = LocalDateTime.now(), - writers = listOf( - WriterDetail(1L, "안나포", URL("http://localhost:8080/api/v1/users/1")), - WriterDetail(2L, "퓨퓨", URL("http://localhost:8080/api/v1/users/2")), - WriterDetail(3L, "프레소", URL("http://localhost:8080/api/v1/users/3")) - ), - articles = listOf(ArticleDetail(1L, "ISA(개인종합자산관리계좌)란?"), ArticleDetail(2L, "ISA(개인종합자산관리계좌)란? ISA(개인종합자산관리계좌)란? ISA(개인종합자산관리계좌)란? ISA(개인종합자산관리계좌)란? ISA(개인종합자산관리계좌)란? ISA(개인종합자산관리계좌)란?")) + val useCaseIn = ReadWorkbookUseCaseIn(workbookId) + val useCaseOut = ReadWorkbookUseCaseOut( + id = 1L, + mainImageUrl = URL("http://localhost:8080/api/v1/workbooks/1/image"), + title = "재태크, 투자 필수 용어 모음집", + description = "사회 초년생부터, 직장인, 은퇴자까지 모두가 알아야 할 기본적인 재태크, 투자 필수 용어 모음집 입니다.", + category = CategoryType.fromCode(0)!!.name, + createdAt = LocalDateTime.now(), + writers = listOf( + WriterDetail(1L, "안나포", URL("http://localhost:8080/api/v1/users/1")), + WriterDetail(2L, "퓨퓨", URL("http://localhost:8080/api/v1/users/2")), + WriterDetail(3L, "프레소", URL("http://localhost:8080/api/v1/users/3")) + ), + articles = listOf( + ArticleDetail(1L, "ISA(개인종합자산관리계좌)란?"), + ArticleDetail( + 2L, + "ISA(개인종합자산관리계좌)란? ISA(개인종합자산관리계좌)란? ISA(개인종합자산관리계좌)란? ISA(개인종합자산관리계좌)란? ISA(개인종합자산관리계좌)란? ISA(개인종합자산관리계좌)란?" + ) ) ) + `when`(readWorkbookUseCase.execute(useCaseIn)).thenReturn(useCaseOut) // when mockMvc.perform( diff --git a/api/src/test/kotlin/com/few/api/web/controller/workbook/article/WorkBookArticleControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/workbook/article/WorkBookArticleControllerTest.kt index 4289d7856..0ce3c75c2 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/workbook/article/WorkBookArticleControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/workbook/article/WorkBookArticleControllerTest.kt @@ -26,8 +26,8 @@ import java.time.LocalDateTime class WorkBookArticleControllerTest : ControllerTestSpec() { companion object { - private val BASE_URL = "/api/v1/workbooks/{workbookId}/articles" - private val TAG = "WorkBookArticleController" + private const val BASE_URL = "/api/v1/workbooks/{workbookId}/articles" + private const val TAG = "WorkBookArticleController" } @Test @@ -36,36 +36,39 @@ class WorkBookArticleControllerTest : ControllerTestSpec() { fun readWorkBookArticle() { // given val api = "ReadWorkBookArticle" + val token = "thisisaccesstoken" val uri = UriComponentsBuilder.newInstance() .path("$BASE_URL/{articleId}") .build() .toUriString() - // set usecase mock + + val memberId = 1L + `when`(tokenResolver.resolveId(token)).thenReturn(memberId) + val workbookId = 1L val articleId = 1L - val memberId = 1L - `when`(tokenResolver.resolveId("thisisaccesstoken")).thenReturn(memberId) - `when`(readWorkBookArticleUseCase.execute(ReadWorkBookArticleUseCaseIn(workbookId, articleId, memberId = memberId))).thenReturn( - ReadWorkBookArticleOut( + val useCaseIn = + ReadWorkBookArticleUseCaseIn(workbookId, articleId, memberId = memberId) + val useCaseOut = ReadWorkBookArticleOut( + id = 1L, + writer = WriterDetail( id = 1L, - writer = WriterDetail( - id = 1L, - name = "안나포", - url = URL("http://localhost:8080/api/v1/writers/1") - ), - title = "ETF(상장 지수 펀드)란? 모르면 손해라고?", - content = "content", - problemIds = listOf(1L, 2L, 3L), - category = CategoryType.fromCode(0)!!.name, - createdAt = LocalDateTime.now(), - day = 1L - ) + name = "안나포", + url = URL("http://localhost:8080/api/v1/writers/1") + ), + title = "ETF(상장 지수 펀드)란? 모르면 손해라고?", + content = "content", + problemIds = listOf(1L, 2L, 3L), + category = CategoryType.fromCode(0)!!.name, + createdAt = LocalDateTime.now(), + day = 1L ) + `when`(readWorkBookArticleUseCase.execute(useCaseIn)).thenReturn(useCaseOut) // when mockMvc.perform( get(uri, workbookId, articleId) - .header("Authorization", "Bearer thisisaccesstoken") + .header("Authorization", "Bearer $token") ).andExpect(MockMvcResultMatchers.status().is2xxSuccessful) .andDo( MockMvcRestDocumentation.document( From c9274e9580885f5dfd02ec9b6b6f82f677c2160c Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 9 Aug 2024 01:09:31 +0900 Subject: [PATCH 3/8] =?UTF-8?q?refactor:=20=EC=9C=A0=EC=A6=88=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=86=B5?= =?UTF-8?q?=EC=9D=BC=20=ED=98=95=EC=8B=9D=20=ED=86=B5=EC=9D=BC=EB=90=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../article/usecase/ReadArticleUseCaseTest.kt | 45 ++-- .../member/usecase/SaveMemberUseCaseTest.kt | 36 ++- .../domain/member/usecase/TokenUseCaseTest.kt | 85 ++++--- .../usecase/BrowseProblemsUseCaseTest.kt | 7 +- .../usecase/CheckProblemUseCaseTest.kt | 26 +- .../problem/usecase/ReadProblemUseCaseTest.kt | 20 +- .../BrowseSubscribeWorkbooksUseCaseTest.kt | 4 +- .../usecase/SubscribeWorkbookUseCaseTest.kt | 32 ++- .../usecase/BrowseWorkbooksUseCaseTest.kt | 223 ++++++++++++------ .../usecase/ReadWorkbookUseCaseTest.kt | 6 +- 10 files changed, 310 insertions(+), 174 deletions(-) diff --git a/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt index d8300cec2..b3f38a430 100644 --- a/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt @@ -9,6 +9,7 @@ import com.few.api.domain.article.service.dto.ReadWriterOutDto import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseIn import com.few.api.repo.dao.article.ArticleDao import com.few.api.repo.dao.article.record.SelectArticleRecord +import com.few.data.common.code.CategoryType import io.github.oshai.kotlinlogging.KotlinLogging import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec @@ -26,7 +27,6 @@ class ReadArticleUseCaseTest : BehaviorSpec({ lateinit var useCase: ReadArticleUseCase lateinit var articleViewHisAsyncHandler: ArticleViewHisAsyncHandler lateinit var articleViewCountHandler: ArticleViewCountHandler - val useCaseIn = ReadArticleUseCaseIn(articleId = 1L, memberId = 1L) beforeContainer { articleDao = mockk() @@ -44,28 +44,39 @@ class ReadArticleUseCaseTest : BehaviorSpec({ } given("아티클 조회 요청이 온 상황에서") { + val articleId = 1L + val memberId = 1L + val useCaseIn = ReadArticleUseCaseIn(articleId, memberId) + `when`("아티클과 작가가 존재할 경우") { - val record = SelectArticleRecord( - articleId = 1L, - writerId = 1L, - mainImageURL = URL("https://jh-labs.tistory.com/"), - title = "title", - category = (10).toByte(), - content = "content", + val writerId = 1L + val mainImageURL = URL("http://localhost:8080/image/main/1") + val title = "title" + val category = CategoryType.ECONOMY.code + val content = "content" + every { articleDao.selectArticleRecord(any()) } returns SelectArticleRecord( + articleId = articleId, + writerId = writerId, + mainImageURL = mainImageURL, + title = title, + category = category, + content = content, createdAt = LocalDateTime.now() ) - val writerSvcOutDto = ReadWriterOutDto( - writerId = 1L, - name = "hunca", - url = URL("https://jh-labs.tistory.com/"), - imageUrl = URL("https://github.com/user-attachments/assets/28df9078-488c-49d6-9375-54ce5a250742") + + val writerName = "hunca" + val writerProfileImageURL = URL("http://localhost:8080/image/profile/1") + every { readArticleWriterRecordService.execute(any()) } returns ReadWriterOutDto( + writerId = writerId, + name = writerName, + url = mainImageURL, + imageUrl = writerProfileImageURL ) - val probSvcOutDto = BrowseArticleProblemsOutDto(problemIds = listOf(1, 2, 3)) - every { articleDao.selectArticleRecord(any()) } returns record - every { readArticleWriterRecordService.execute(any()) } returns writerSvcOutDto - every { browseArticleProblemsService.execute(any()) } returns probSvcOutDto + every { browseArticleProblemsService.execute(any()) } returns BrowseArticleProblemsOutDto(problemIds = listOf(1, 2, 3)) + every { articleViewCountHandler.browseArticleViewCount(any()) } returns 1L + every { articleViewHisAsyncHandler.addArticleViewHis(any(), any(), any()) } answers { log.debug { "Inserting article view history asynchronously" } } diff --git a/api/src/test/kotlin/com/few/api/domain/member/usecase/SaveMemberUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/member/usecase/SaveMemberUseCaseTest.kt index b70c2947a..112d8e8e1 100644 --- a/api/src/test/kotlin/com/few/api/domain/member/usecase/SaveMemberUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/member/usecase/SaveMemberUseCaseTest.kt @@ -17,7 +17,6 @@ class SaveMemberUseCaseTest : BehaviorSpec({ lateinit var sendAuthEmailService: SendAuthEmailService lateinit var idEncryption: IdEncryption lateinit var useCase: SaveMemberUseCase - val useCaseIn = SaveMemberUseCaseIn(email = "test@gmail.com") beforeContainer { memberDao = mockk() @@ -27,10 +26,17 @@ class SaveMemberUseCaseTest : BehaviorSpec({ } given("회원가입/로그인 요청이 온 상황에서") { + val email = "test@gmail.com" + val useCaseIn = SaveMemberUseCaseIn(email = email) + `when`("요청의 이메일이 가입 이력이 없는 경우") { every { memberDao.selectMemberByEmail(any(SelectMemberByEmailNotConsiderDeletedAtQuery::class)) } returns null + every { memberDao.insertMember(any()) } returns 1L - every { idEncryption.encrypt(any()) } returns "encryptedToken" + + val token = "encryptedToken" + every { idEncryption.encrypt(any()) } returns token + every { sendAuthEmailService.send(any()) } returns Unit then("인증 이메일 발송 성공 응답을 반환한다") { @@ -43,11 +49,15 @@ class SaveMemberUseCaseTest : BehaviorSpec({ } `when`("요청의 이메일이 가입되어 있는 경우") { + val memberId = 1L every { memberDao.selectMemberByEmail(any(SelectMemberByEmailNotConsiderDeletedAtQuery::class)) } returns MemberIdAndIsDeletedRecord( - memberId = 1L, + memberId = memberId, isDeleted = false ) - every { idEncryption.encrypt(any()) } returns "encryptedToken" + + val token = "encryptedToken" + every { idEncryption.encrypt(any()) } returns token + every { sendAuthEmailService.send(any()) } returns Unit then("인증 이메일 발송 성공 응답을 반환한다") { @@ -60,12 +70,16 @@ class SaveMemberUseCaseTest : BehaviorSpec({ } `when`("요청의 이메일이 삭제된 회원인 경우") { + val memberId = 1L every { memberDao.selectMemberByEmail(any(SelectMemberByEmailNotConsiderDeletedAtQuery::class)) } returns MemberIdAndIsDeletedRecord( - memberId = 1L, + memberId = memberId, isDeleted = true ) - every { memberDao.updateMemberType(any(UpdateDeletedMemberTypeCommand::class)) } returns 1L - every { idEncryption.encrypt(any()) } returns "encryptedToken" + + every { memberDao.updateMemberType(any(UpdateDeletedMemberTypeCommand::class)) } returns memberId + + val token = "encryptedToken" + every { idEncryption.encrypt(any()) } returns token then("인증 이메일 발송 성공 응답을 반환한다") { useCase.execute(useCaseIn) @@ -79,8 +93,12 @@ class SaveMemberUseCaseTest : BehaviorSpec({ `when`("인증 이메일 발송에 실패한 경우") { every { memberDao.selectMemberByEmail(any(SelectMemberByEmailNotConsiderDeletedAtQuery::class)) } returns null - every { memberDao.insertMember(any()) } returns 1L - every { idEncryption.encrypt(any()) } returns "encryptedToken" + + val memberId = 1L + every { memberDao.insertMember(any()) } returns memberId + + val token = "encryptedToken" + every { idEncryption.encrypt(any()) } returns token every { sendAuthEmailService.send(any()) } throws Exception() diff --git a/api/src/test/kotlin/com/few/api/domain/member/usecase/TokenUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/member/usecase/TokenUseCaseTest.kt index f4b8d4e7a..5069f022e 100644 --- a/api/src/test/kotlin/com/few/api/domain/member/usecase/TokenUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/member/usecase/TokenUseCaseTest.kt @@ -29,49 +29,68 @@ class TokenUseCaseTest : BehaviorSpec({ useCase = TokenUseCase(tokenGenerator, tokenResolver, memberDao, idEncryption) } - given("토큰 요청이 온 상황에서") { + given("리프레시 토큰이 포함된 요청이 온 상황에서") { + val oldRefreshToken = "refreshToken" + val useCaseIn = TokenUseCaseIn( + token = null, + refreshToken = oldRefreshToken, + at = null, + rt = null + ) + `when`("요청에 refreshToken이 포함되어 있는 경우") { - every { tokenResolver.resolveId(any()) } returns 1L - every { tokenResolver.resolveEmail(any()) } returns "test@gmail.com" + val memberId = 1L + every { tokenResolver.resolveId(any()) } returns memberId + + val email = "test@gmail.com" + every { tokenResolver.resolveEmail(any()) } returns email + + val accessToken = "newAccessToken" + val refreshToken = "newRefreshToken" every { tokenGenerator.generateAuthToken(any(), any(), any()) } returns AuthToken( - accessToken = "accessToken", - refreshToken = "refreshToken" + accessToken = accessToken, + refreshToken = refreshToken ) + then("새로운 토큰을 반환한다") { - useCase.execute( - TokenUseCaseIn( - token = null, - refreshToken = "refreshToken", - at = null, - rt = null - ) - ) + useCase.execute(useCaseIn) + verify(exactly = 1) { tokenResolver.resolveId(any()) } verify(exactly = 1) { tokenResolver.resolveEmail(any()) } verify(exactly = 1) { tokenGenerator.generateAuthToken(any(), any(), any()) } } } + } - `when`("요청에 로그인을 하려는 멤버의 token이 포함되어 있는 경우") { - every { idEncryption.decrypt(any()) } returns "1" + given("멤버 아이디 정보를 암호화한 토큰이 포함된 요청이 온 상황에서") { + val encryptedIdToken = "token" + val useCaseIn = TokenUseCaseIn( + token = encryptedIdToken, + refreshToken = null, + at = null, + rt = null + ) + + `when`("로그인하려는 멤버의 토큰이 포함되어 있는 경우") { + val decryptedId = "1" + every { idEncryption.decrypt(any()) } returns decryptedId + + val accessToken = "newAccessToken" + val refreshToken = "newRefreshToken" every { tokenGenerator.generateAuthToken(any(), any(), any(), any(), any()) } returns AuthToken( - accessToken = "accessToken", - refreshToken = "refreshToken" + accessToken = accessToken, + refreshToken = refreshToken ) + + val email = "test@gmail.com" every { memberDao.selectMemberEmailAndType(any()) } returns MemberEmailAndTypeRecord( - email = "test@gmail.com", + email = email, memberType = MemberType.NORMAL ) then("새로운 토큰을 반환한다") { - useCase.execute( - TokenUseCaseIn( - token = "token", - refreshToken = null, - at = null, - rt = null - ) - ) + useCase.execute(useCaseIn) + verify(exactly = 1) { idEncryption.decrypt(any()) } verify(exactly = 1) { tokenGenerator.generateAuthToken( @@ -86,12 +105,14 @@ class TokenUseCaseTest : BehaviorSpec({ } } - `when`("요청에 회원가입을 완료 하려는 멤버의 token이 포함되어 있는 경우") { + `when`("회원가입을 완료 하려는 멤버의 토큰이 포함되어 있는 경우") { every { idEncryption.decrypt(any()) } returns "1" + every { tokenGenerator.generateAuthToken(any(), any(), any(), any(), any()) } returns AuthToken( accessToken = "accessToken", refreshToken = "refreshToken" ) + every { memberDao.selectMemberEmailAndType(any()) } returns MemberEmailAndTypeRecord( email = "test@gmail.com", memberType = MemberType.PREAUTH @@ -100,14 +121,8 @@ class TokenUseCaseTest : BehaviorSpec({ every { memberDao.updateMemberType(any(UpdateMemberTypeCommand::class)) } returns Unit then("새로운 토큰을 반환한다") { - useCase.execute( - TokenUseCaseIn( - token = "token", - refreshToken = null, - at = null, - rt = null - ) - ) + useCase.execute(useCaseIn) + verify(exactly = 1) { idEncryption.decrypt(any()) } verify(exactly = 1) { tokenGenerator.generateAuthToken( diff --git a/api/src/test/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCaseTest.kt index ca54ea69e..ac39f45dc 100644 --- a/api/src/test/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCaseTest.kt @@ -13,7 +13,6 @@ class BrowseProblemsUseCaseTest : BehaviorSpec({ lateinit var problemDao: ProblemDao lateinit var useCase: BrowseProblemsUseCase - val useCaseIn = BrowseProblemsUseCaseIn(articleId = 1L) beforeContainer { problemDao = mockk() @@ -21,9 +20,11 @@ class BrowseProblemsUseCaseTest : BehaviorSpec({ } given("특정 아티클에 대한") { + val articleId = 1L + val useCaseIn = BrowseProblemsUseCaseIn(articleId = articleId) + `when`("문제가 존재할 경우") { - val problemIdsRecord = ProblemIdsRecord(listOf(1, 2, 3)) - every { problemDao.selectProblemsByArticleId(any()) } returns problemIdsRecord + every { problemDao.selectProblemsByArticleId(any()) } returns ProblemIdsRecord(listOf(1, 2, 3)) then("문제번호가 정상적으로 조회된다") { useCase.execute(useCaseIn) diff --git a/api/src/test/kotlin/com/few/api/domain/problem/usecase/CheckProblemUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/problem/usecase/CheckProblemUseCaseTest.kt index 5bdd418b4..1450f07e9 100644 --- a/api/src/test/kotlin/com/few/api/domain/problem/usecase/CheckProblemUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/problem/usecase/CheckProblemUseCaseTest.kt @@ -24,14 +24,16 @@ class CheckProblemUseCaseTest : BehaviorSpec({ } given("문제 정답 확인 요청이 온 상황에서") { + val problemId = 1L + val submissionVal = "1" + val useCaseIn = CheckProblemUseCaseIn(problemId = problemId, sub = submissionVal) + `when`("제출 값과 문제 정답이 같을 경우") { - val submissionVal = "1" val answer = submissionVal - val useCaseIn = CheckProblemUseCaseIn(problemId = 1L, sub = submissionVal) - val answerRecord = SelectProblemAnswerRecord(id = 1L, answer = answer, explanation = "해설입니다.") + every { problemDao.selectProblemAnswer(any()) } returns SelectProblemAnswerRecord(id = problemId, answer = answer, explanation = "해설입니다.") - every { problemDao.selectProblemAnswer(any()) } returns answerRecord - every { submitHistoryDao.insertSubmitHistory(any()) } returns 1L + val problemSubmitHistoryId = 1L + every { submitHistoryDao.insertSubmitHistory(any()) } returns problemSubmitHistoryId then("문제가 정답처리 된다") { val useCaseOut = useCase.execute(useCaseIn) @@ -43,13 +45,15 @@ class CheckProblemUseCaseTest : BehaviorSpec({ } `when`("제출 값과 문제 정답이 다를 경우") { - val submissionVal = "1" val answer = "2" - val useCaseIn = CheckProblemUseCaseIn(problemId = 1L, sub = submissionVal) - val answerRecord = SelectProblemAnswerRecord(id = 1L, answer = answer, explanation = "해설입니다.") + every { problemDao.selectProblemAnswer(any()) } returns SelectProblemAnswerRecord( + id = problemId, + answer = answer, + explanation = "해설입니다." + ) - every { problemDao.selectProblemAnswer(any()) } returns answerRecord - every { submitHistoryDao.insertSubmitHistory(any()) } returns 1L + val problemSubmitHistoryId = 1L + every { submitHistoryDao.insertSubmitHistory(any()) } returns problemSubmitHistoryId then("문제가 오답처리 된다") { val useCaseOut = useCase.execute(useCaseIn) @@ -61,8 +65,6 @@ class CheckProblemUseCaseTest : BehaviorSpec({ } `when`("존재하지 않는 문제일 경우") { - val useCaseIn = CheckProblemUseCaseIn(problemId = 1L, sub = "1") - every { problemDao.selectProblemAnswer(any()) } returns null then("예외가 발생한다") { diff --git a/api/src/test/kotlin/com/few/api/domain/problem/usecase/ReadProblemUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/problem/usecase/ReadProblemUseCaseTest.kt index d97f6490c..f1ba360b3 100644 --- a/api/src/test/kotlin/com/few/api/domain/problem/usecase/ReadProblemUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/problem/usecase/ReadProblemUseCaseTest.kt @@ -11,13 +11,13 @@ import io.kotest.core.spec.style.BehaviorSpec import io.mockk.every import io.mockk.mockk import io.mockk.verify +import java.util.stream.IntStream class ReadProblemUseCaseTest : BehaviorSpec({ lateinit var problemDao: ProblemDao lateinit var contentsJsonMapper: ContentsJsonMapper lateinit var useCase: ReadProblemUseCase - val useCaseIn = ReadProblemUseCaseIn(problemId = 1L) beforeContainer { problemDao = mockk() @@ -26,17 +26,17 @@ class ReadProblemUseCaseTest : BehaviorSpec({ } given("문제를 조회할 상황에서") { + val problemId = 1L + val useCaseIn = ReadProblemUseCaseIn(problemId = problemId) + `when`("문제가 존재할 경우") { - val problemRecord = SelectProblemRecord(id = 1L, title = "title", contents = "{}") - val contents = Contents( - listOf( - Content(number = 1, content = "{}"), - Content(number = 2, content = "{}") - ) - ) + val title = "title" + val problemContents = "{}" + every { problemDao.selectProblemContents(any()) } returns SelectProblemRecord(id = problemId, title = title, contents = problemContents) - every { problemDao.selectProblemContents(any()) } returns problemRecord - every { contentsJsonMapper.toObject(any()) } returns contents + every { contentsJsonMapper.toObject(any()) } returns Contents( + IntStream.range(1, 3).mapToObj { Content(number = it.toLong(), content = "{}") }.toList() + ) then("정상적으로 실행되어야 한다") { useCase.execute(useCaseIn) diff --git a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCaseTest.kt index d5018a7f4..e65655518 100644 --- a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCaseTest.kt @@ -27,6 +27,9 @@ class BrowseSubscribeWorkbooksUseCaseTest : BehaviorSpec({ } given("사용자 구독 정보 조회 요청이 온 상황에서") { + val memberId = 1L + val useCaseIn = BrowseSubscribeWorkbooksUseCaseIn(memberId = memberId) + `when`("사용자의 구독 정보가 있는 경우") { every { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } returns listOf( MemberWorkbookSubscriptionStatusRecord( @@ -58,7 +61,6 @@ class BrowseSubscribeWorkbooksUseCaseTest : BehaviorSpec({ every { objectMapper.writeValueAsString(any()) } returns "{\"articleId\":1}" andThen "{\"articleId\":2}" then("사용자의 구독 정보를 조회한다") { - val useCaseIn = BrowseSubscribeWorkbooksUseCaseIn(memberId = 1L) useCase.execute(useCaseIn) verify(exactly = 1) { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } diff --git a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCaseTest.kt index 116344038..8f843685f 100644 --- a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCaseTest.kt @@ -21,7 +21,6 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ lateinit var applicationEventPublisher: ApplicationEventPublisher lateinit var useCase: SubscribeWorkbookUseCase val workbookId = 1L - val useCaseIn = SubscribeWorkbookUseCaseIn(workbookId = workbookId, memberId = 1L) beforeContainer { subscriptionDao = mockk() @@ -30,11 +29,16 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ } given("구독 요청이 온 상황에서") { - `when`("subscriptionStatus가 null일 경우") { - val event = WorkbookSubscriptionEvent(workbookId) + val workbookId = 1L + val memberId = 1L + val useCaseIn = SubscribeWorkbookUseCaseIn(workbookId = workbookId, memberId = memberId) + `when`("subscriptionStatus가 null일 경우") { every { subscriptionDao.selectTopWorkbookSubscriptionStatus(any()) } returns null + every { subscriptionDao.insertWorkbookSubscription(any()) } just Runs + + val event = WorkbookSubscriptionEvent(workbookId) every { applicationEventPublisher.publishEvent(event) } answers { log.debug { "Mocking applicationEventPublisher.publishEvent(any()) was called" } } @@ -51,13 +55,18 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ `when`("구독을 취소한 경우") { val day = 2 - val lastDay = 3 - val subscriptionStatusRecord = WorkbookSubscriptionStatus(workbookId = workbookId, isActiveSub = false, day) - val event = WorkbookSubscriptionEvent(workbookId) + every { subscriptionDao.selectTopWorkbookSubscriptionStatus(any()) } returns WorkbookSubscriptionStatus( + workbookId = workbookId, + isActiveSub = false, + day + ) - every { subscriptionDao.selectTopWorkbookSubscriptionStatus(any()) } returns subscriptionStatusRecord + val lastDay = 3 every { subscriptionDao.countWorkbookMappedArticles(any()) } returns lastDay + every { subscriptionDao.reSubscribeWorkbookSubscription(any()) } just Runs + + val event = WorkbookSubscriptionEvent(workbookId) every { applicationEventPublisher.publishEvent(event) } answers { log.debug { "Mocking applicationEventPublisher.publishEvent(any()) was called" } } @@ -74,13 +83,14 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ `when`("이미 구독하고 있을 경우") { val day = 2 - val lastDay = 3 - val subscriptionStatusRecord = WorkbookSubscriptionStatus(workbookId = workbookId, isActiveSub = true, day) - val event = WorkbookSubscriptionEvent(workbookId) + every { subscriptionDao.selectTopWorkbookSubscriptionStatus(any()) } returns WorkbookSubscriptionStatus(workbookId = workbookId, isActiveSub = true, day) - every { subscriptionDao.selectTopWorkbookSubscriptionStatus(any()) } returns subscriptionStatusRecord + val lastDay = 3 every { subscriptionDao.countWorkbookMappedArticles(any()) } returns lastDay + every { subscriptionDao.reSubscribeWorkbookSubscription(any()) } just Runs + + val event = WorkbookSubscriptionEvent(workbookId) every { applicationEventPublisher.publishEvent(event) } answers { log.debug { "Mocking applicationEventPublisher.publishEvent(any()) was called" } } diff --git a/api/src/test/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCaseTest.kt index 2d93b4ace..d1b435c2a 100644 --- a/api/src/test/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCaseTest.kt @@ -14,6 +14,7 @@ import com.few.api.repo.dao.workbook.WorkbookDao import com.few.api.repo.dao.workbook.record.SelectWorkBookRecordWithSubscriptionCount import com.few.api.web.support.ViewCategory import com.few.api.web.support.WorkBookCategory +import com.few.data.common.code.CategoryType import io.kotest.core.spec.style.BehaviorSpec import io.mockk.every import io.mockk.mockk @@ -21,6 +22,7 @@ import io.mockk.verify import java.net.URL import java.time.LocalDateTime +import java.util.stream.IntStream class BrowseWorkbooksUseCaseTest : BehaviorSpec({ lateinit var workbookDao: WorkbookDao @@ -38,8 +40,77 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ BrowseWorkbooksUseCase(workbookDao, workbookMemberService, workbookSubscribeService, workbookOrderDelegatorExecutor) } - given("다수 워크북 조회 요청이 온 상황에서") { - `when`("카테고리가 지정되어 있을 경우") { + given("로그인 안된 상황에서 카테고리를 지정하여 다수 워크북 조회 요청이 온 상황에서") { + val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = null, memberId = null) + + `when`("경제로 지정되어 있을 경우") { + every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns IntStream.range(1, 3).mapToObj { + SelectWorkBookRecordWithSubscriptionCount( + id = it.toLong(), + title = "workbook title$it", + mainImageUrl = URL("http://localhost:8080/image/main/$it"), + category = CategoryType.ECONOMY.code, + description = "workbook$it description", + createdAt = LocalDateTime.now(), + subscriptionCount = it.toLong() + ) + }.toList() + + every { workbookMemberService.browseWorkbookWriterRecords(any()) } returns mapOf( + 1L to listOf( + WriterMappedWorkbookOutDto( + writerId = 1L, + name = "writer1", + url = URL("http://localhost:8080/image/writer/1"), + workbookId = 1L + ) + ), + 2L to listOf( + WriterMappedWorkbookOutDto( + writerId = 2L, + name = "writer2", + url = URL("http://localhost:8080/image/writer/2"), + workbookId = 2L + ) + ) + ) + + every { + workbookOrderDelegatorExecutor.execute(any()) + } returns IntStream.range(1, 3).mapToObj { + BrowseWorkBookDetail( + id = it.toLong(), + title = "workbook title$it", + mainImageUrl = URL("http://localhost:8080/image/main/$it"), + category = WorkBookCategory.ECONOMY.displayName, + description = "workbook$it description", + createdAt = LocalDateTime.now(), + writerDetails = listOf( + WriterDetail( + id = it.toLong(), + name = "writer$it", + url = URL("http://localhost:8080/image/writer/$it") + ) + ), + subscriptionCount = it.toLong() + ) + }.toList() + + then("경제 카테고리의 워크북이 조회된다") { + useCase.execute(useCaseIn) + + verify(exactly = 1) { workbookDao.browseWorkBookWithSubscriptionCount(any()) } + verify(exactly = 1) { workbookMemberService.browseWorkbookWriterRecords(any()) } + verify(exactly = 0) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } + verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any()) } + } + } + } + + given("로그인 안된 상황에서 메인 뷰에서 카테고리를 지정하여 다수 워크북 조회 요청이 온 상황에서") { + val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = ViewCategory.MAIN_CARD, memberId = null) + + `when`("경제로 지정되어 있을 경우") { every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns listOf( SelectWorkBookRecordWithSubscriptionCount( id = 1L, @@ -81,7 +152,7 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ ) every { - workbookOrderDelegatorExecutor.execute(any()) + workbookOrderDelegatorExecutor.execute(any(BasicWorkbookOrderDelegator::class)) } returns listOf( BrowseWorkBookDetail( id = 1L, @@ -101,55 +172,48 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ ) ) - then("지정한 카테고리의 워크북이 조회된다") { - val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = null, memberId = null) + then("경제 카테고리의 워크북이 조회된다") { useCase.execute(useCaseIn) verify(exactly = 1) { workbookDao.browseWorkBookWithSubscriptionCount(any()) } verify(exactly = 1) { workbookMemberService.browseWorkbookWriterRecords(any()) } verify(exactly = 0) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } - verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any()) } + verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any(BasicWorkbookOrderDelegator::class)) } } } } - given("메인에서 다수 워크북 조회 요청이 온 상황에서") { - `when`("로그인이 되어 있을 경우") { - every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns listOf( - SelectWorkBookRecordWithSubscriptionCount( - id = 1L, - title = "workbook title", - mainImageUrl = URL("https://jh-labs.tistory.com/"), - category = (10).toByte(), - description = "workbook description", - createdAt = LocalDateTime.now(), - subscriptionCount = 10 - ), + given("로그인 된 상황에서 카테고리를 지정하여 다수 워크북 조회 요청이 온 상황에서") { + val memberId = 1L + val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = null, memberId = memberId) + + `when`("경제로 지정되어 있을 경우") { + every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns IntStream.range(1, 3).mapToObj { SelectWorkBookRecordWithSubscriptionCount( - id = 2L, - title = "workbook title", - mainImageUrl = URL("https://jh-labs.tistory.com/"), - category = (10).toByte(), - description = "workbook description", + id = it.toLong(), + title = "workbook title$it", + mainImageUrl = URL("http://localhost:8080/image/main/$it"), + category = CategoryType.ECONOMY.code, + description = "workbook$it description", createdAt = LocalDateTime.now(), - subscriptionCount = 10 + subscriptionCount = it.toLong() ) - ) + }.toList() every { workbookMemberService.browseWorkbookWriterRecords(any()) } returns mapOf( 1L to listOf( WriterMappedWorkbookOutDto( writerId = 1L, - name = "hunca", - url = URL("https://jh-labs.tistory.com/"), + name = "writer1", + url = URL("http://localhost:8080/image/writer/1"), workbookId = 1L ) ), 2L to listOf( WriterMappedWorkbookOutDto( writerId = 2L, - name = "hunca", - url = URL("https://jh-labs.tistory.com/"), + name = "writer2", + url = URL("http://localhost:8080/image/writer/2"), workbookId = 2L ) ) @@ -171,58 +235,53 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ ) every { - workbookOrderDelegatorExecutor.execute(any(AuthMainViewWorkbookOrderDelegator::class)) - } returns listOf( + workbookOrderDelegatorExecutor.execute(any()) + } returns IntStream.range(1, 3).mapToObj { BrowseWorkBookDetail( - id = 1L, - title = "workbook title", - mainImageUrl = URL("https://jh-labs.tistory.com/"), + id = it.toLong(), + title = "workbook title$it", + mainImageUrl = URL("http://localhost:8080/image/main/$it"), category = WorkBookCategory.ECONOMY.displayName, - description = "workbook description", + description = "workbook$it description", createdAt = LocalDateTime.now(), writerDetails = listOf( WriterDetail( - id = 1L, - name = "hunca", - url = URL("https://jh-labs.tistory.com/") + id = it.toLong(), + name = "writer$it", + url = URL("http://localhost:8080/image/writer/$it") ) ), - subscriptionCount = 10 + subscriptionCount = it.toLong() ) - ) + }.toList() - then("인증 메인뷰 워크북 정렬이 실행된 결과가 반환된다") { - val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = ViewCategory.MAIN_CARD, memberId = 1L) + then("경제 카테고리의 워크북이 조회된다") { useCase.execute(useCaseIn) verify(exactly = 1) { workbookDao.browseWorkBookWithSubscriptionCount(any()) } verify(exactly = 1) { workbookMemberService.browseWorkbookWriterRecords(any()) } - verify(exactly = 1) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } - verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any(AuthMainViewWorkbookOrderDelegator::class)) } + verify(exactly = 0) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } + verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any()) } } } + } - `when`("로그인이 되어 있지 않은 경우") { - every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns listOf( + given("로그인 된 상황에서 메인 뷰에서 카테고리를 지정하여 다수 워크북 조회 요청이 온 상황에서") { + val memberId = 1L + val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = ViewCategory.MAIN_CARD, memberId = memberId) + + `when`("경제로 지정되어 있을 경우") { + every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns IntStream.range(1, 3).mapToObj { SelectWorkBookRecordWithSubscriptionCount( - id = 1L, - title = "workbook title", - mainImageUrl = URL("https://jh-labs.tistory.com/"), - category = (10).toByte(), - description = "workbook description", + id = it.toLong(), + title = "workbook title$it", + mainImageUrl = URL("http://localhost:8080/image/main/$it"), + category = CategoryType.ECONOMY.code, + description = "workbook$it description", createdAt = LocalDateTime.now(), - subscriptionCount = 10 - ), - SelectWorkBookRecordWithSubscriptionCount( - id = 2L, - title = "workbook title", - mainImageUrl = URL("https://jh-labs.tistory.com/"), - category = (10).toByte(), - description = "workbook description", - createdAt = LocalDateTime.now(), - subscriptionCount = 10 + subscriptionCount = it.toLong() ) - ) + }.toList() every { workbookMemberService.browseWorkbookWriterRecords(any()) } returns mapOf( 1L to listOf( @@ -244,34 +303,48 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ ) every { - workbookOrderDelegatorExecutor.execute(any(BasicWorkbookOrderDelegator::class)) + workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } returns listOf( + BrowseMemberSubscribeWorkbooksOutDto( + workbookId = 1L, + isActiveSub = true, + currentDay = 1 + ), + BrowseMemberSubscribeWorkbooksOutDto( + workbookId = 2L, + isActiveSub = false, + currentDay = 1 + ) + ) + + every { + workbookOrderDelegatorExecutor.execute(any(AuthMainViewWorkbookOrderDelegator::class)) + } returns IntStream.range(1, 3).mapToObj { BrowseWorkBookDetail( - id = 1L, - title = "workbook title", - mainImageUrl = URL("https://jh-labs.tistory.com/"), + id = it.toLong(), + title = "workbook title$it", + mainImageUrl = URL("http://localhost:8080/image/main/$it"), category = WorkBookCategory.ECONOMY.displayName, - description = "workbook description", + description = "workbook$it description", createdAt = LocalDateTime.now(), writerDetails = listOf( WriterDetail( - id = 1L, - name = "hunca", + id = it.toLong(), + name = "writer$it", url = URL("https://jh-labs.tistory.com/") ) ), - subscriptionCount = 10 + subscriptionCount = it.toLong() ) - ) + }.toList() - then("지정한 카테고리의 워크북이 조회된다") { - val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = ViewCategory.MAIN_CARD, memberId = null) + then("경제 카테고리의 워크북이 조회된다") { useCase.execute(useCaseIn) verify(exactly = 1) { workbookDao.browseWorkBookWithSubscriptionCount(any()) } verify(exactly = 1) { workbookMemberService.browseWorkbookWriterRecords(any()) } - verify(exactly = 0) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } - verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any(BasicWorkbookOrderDelegator::class)) } + verify(exactly = 1) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } + verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any(AuthMainViewWorkbookOrderDelegator::class)) } } } } diff --git a/api/src/test/kotlin/com/few/api/domain/workbook/usecase/ReadWorkbookUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/workbook/usecase/ReadWorkbookUseCaseTest.kt index 2099903d2..9f74a8821 100644 --- a/api/src/test/kotlin/com/few/api/domain/workbook/usecase/ReadWorkbookUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/workbook/usecase/ReadWorkbookUseCaseTest.kt @@ -21,7 +21,6 @@ class ReadWorkbookUseCaseTest : BehaviorSpec({ lateinit var workbookArticleService: WorkbookArticleService lateinit var workbookMemberService: WorkbookMemberService lateinit var useCase: ReadWorkbookUseCase - val useCaseIn = ReadWorkbookUseCaseIn(workbookId = 1L) beforeContainer { workbookDao = mockk() @@ -31,6 +30,9 @@ class ReadWorkbookUseCaseTest : BehaviorSpec({ } given("워크북 조회 요청이 온 상황에서") { + val workbookId = 1L + val useCaseIn = ReadWorkbookUseCaseIn(workbookId = workbookId) + `when`("워크북과 작가가 존재할 경우") { every { workbookDao.selectWorkBook(any()) } returns SelectWorkBookRecord( id = 1L, @@ -40,6 +42,7 @@ class ReadWorkbookUseCaseTest : BehaviorSpec({ description = "workbook description", createdAt = LocalDateTime.now() ) + every { workbookArticleService.browseWorkbookArticles(any()) } returns listOf( WorkBookArticleOutDto( articleId = 1L, @@ -51,6 +54,7 @@ class ReadWorkbookUseCaseTest : BehaviorSpec({ createdAt = LocalDateTime.now() ) ) + every { workbookMemberService.browseWriterRecords(any()) } returns listOf( WriterOutDto( writerId = 1L, From 175ed66845cd67688677e3c503ae8bd163771554 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 9 Aug 2024 17:11:41 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A6=88=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B3=B4?= =?UTF-8?q?=EA=B0=95=20=EB=B0=8F=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/domain/member/usecase/TokenUseCase.kt | 5 +- .../BrowseSubscribeWorkbooksUseCase.kt | 3 +- .../usecase/SubscribeWorkbookUseCase.kt | 2 +- .../usecase/UnsubscribeAllUseCase.kt | 2 - .../usecase/UnsubscribeWorkbookUseCase.kt | 3 +- .../resources/messages/article.properties | 1 + .../article/usecase/ReadArticleUseCaseTest.kt | 66 +++- .../member/usecase/SaveMemberUseCaseTest.kt | 28 +- .../domain/member/usecase/TokenUseCaseTest.kt | 85 +++-- .../usecase/BrowseProblemsUseCaseTest.kt | 15 +- .../usecase/CheckProblemUseCaseTest.kt | 18 +- .../problem/usecase/ReadProblemUseCaseTest.kt | 21 +- .../BrowseSubscribeWorkbooksUseCaseTest.kt | 149 ++++++++- .../usecase/SubscribeWorkbookUseCaseTest.kt | 26 +- .../usecase/UnsubscribeAllUseCaseTest.kt | 54 ++++ .../usecase/UnsubscribeWorkbookUseCaseTest.kt | 54 ++++ .../usecase/BrowseWorkbooksUseCaseTest.kt | 306 ++++++++---------- .../usecase/ReadWorkbookUseCaseTest.kt | 57 ++-- 18 files changed, 600 insertions(+), 295 deletions(-) create mode 100644 api/src/test/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeAllUseCaseTest.kt create mode 100644 api/src/test/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeWorkbookUseCaseTest.kt diff --git a/api/src/main/kotlin/com/few/api/domain/member/usecase/TokenUseCase.kt b/api/src/main/kotlin/com/few/api/domain/member/usecase/TokenUseCase.kt index 73e60b24e..991e310a6 100644 --- a/api/src/main/kotlin/com/few/api/domain/member/usecase/TokenUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/member/usecase/TokenUseCase.kt @@ -24,6 +24,8 @@ class TokenUseCase( @Transactional fun execute(useCaseIn: TokenUseCaseIn): TokenUseCaseOut { + var isLogin = true + /** refreshToken이 요청에 포함되어 있으면 refreshToken을 통해 memberId를 추출하여 새로운 토큰을 발급 */ var _memberId: Long? = null var _memberEmail: String? = null @@ -59,6 +61,7 @@ class TokenUseCase( ?: throw NotFoundException("member.notfound.id") if (memberEmailAndTypeRecord.memberType == MemberType.PREAUTH) { + isLogin = false UpdateMemberTypeCommand( id = memberId, memberType = MemberType.NORMAL @@ -79,7 +82,7 @@ class TokenUseCase( return TokenUseCaseOut( accessToken = token.accessToken, refreshToken = token.refreshToken, - isLogin = false + isLogin = isLogin ) } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt index 3c93b376b..6a039f5f2 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt @@ -13,6 +13,7 @@ import com.few.api.repo.dao.subscription.query.SelectAllMemberWorkbookInActiveSu import com.few.api.web.support.WorkBookStatus import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional +import org.webjars.NotFoundException @Component class BrowseSubscribeWorkbooksUseCase( @@ -41,7 +42,7 @@ class BrowseSubscribeWorkbooksUseCase( val workbookSubscriptionCurrentArticleIdRecords = subscriptionRecords.associate { it -> val articleId = ReadArticleIdByWorkbookIdAndDayDto(it.workbookId, it.currentDay).let { subscriptionArticleService.readArticleIdByWorkbookIdAndDay(it) - } ?: throw IllegalArgumentException("articleId is null") + } ?: throw NotFoundException("article.notfound.workbookIdAndCurrentDay") it.workbookId to articleId } diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCase.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCase.kt index 76bdb0425..69449cc32 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCase.kt @@ -49,7 +49,7 @@ class SubscribeWorkbookUseCase( subscriptionDao.reSubscribeWorkbookSubscription(command) } - /** 이미 구독한 히스토리가 있고 구독이 취소되지 않은 경우 */ + /** 구독 중인 경우 */ else -> { throw SubscribeIllegalArgumentException("subscribe.state.subscribed") } diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeAllUseCase.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeAllUseCase.kt index 1887d9453..48f57e687 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeAllUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeAllUseCase.kt @@ -1,6 +1,5 @@ package com.few.api.domain.subscription.usecase -import com.few.api.domain.subscription.service.MemberService import com.few.api.domain.subscription.usecase.dto.UnsubscribeAllUseCaseIn import com.few.api.repo.dao.subscription.SubscriptionDao import com.few.api.repo.dao.subscription.command.UpdateDeletedAtInAllSubscriptionCommand @@ -10,7 +9,6 @@ import org.springframework.transaction.annotation.Transactional @Component class UnsubscribeAllUseCase( private val subscriptionDao: SubscriptionDao, - private val memberService: MemberService, ) { @Transactional diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeWorkbookUseCase.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeWorkbookUseCase.kt index ba5afb038..057193e47 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeWorkbookUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeWorkbookUseCase.kt @@ -1,6 +1,5 @@ package com.few.api.domain.subscription.usecase -import com.few.api.domain.subscription.service.MemberService import com.few.api.repo.dao.subscription.SubscriptionDao import com.few.api.repo.dao.subscription.command.UpdateDeletedAtInWorkbookSubscriptionCommand import com.few.api.domain.subscription.usecase.dto.UnsubscribeWorkbookUseCaseIn @@ -10,9 +9,9 @@ import org.springframework.transaction.annotation.Transactional @Component class UnsubscribeWorkbookUseCase( private val subscriptionDao: SubscriptionDao, - private val memberService: MemberService, ) { + // todo add test @Transactional fun execute(useCaseIn: UnsubscribeWorkbookUseCaseIn) { // TODO: request sending email diff --git a/api/src/main/resources/messages/article.properties b/api/src/main/resources/messages/article.properties index e1f4acb6e..b24e04b0c 100644 --- a/api/src/main/resources/messages/article.properties +++ b/api/src/main/resources/messages/article.properties @@ -1,4 +1,5 @@ article.notfound.id=\u0061\u0072\u0074\u0069\u0063\u006c\u0065\u002e\u006e\u006f\u0074\u0066\u006f\u0075\u006e\u0064\u002e\u0069\u0064 article.notfound.articleidworkbookid=\u0061\u0072\u0074\u0069\u0063\u006c\u0065\u002e\u006e\u006f\u0074\u0066\u006f\u0075\u006e\u0064\u002e\u0061\u0072\u0074\u0069\u0063\u006c\u0065\u0069\u0064\u0077\u006f\u0072\u006b\u0062\u006f\u006f\u006b\u0069 +article.notfound.workbookIdAndCurrentDay=\u0061\u0072\u0074\u0069\u0063\u006c\u0065\u002e\u006e\u006f\u0074\u0066\u006f\u0075\u006e\u0064\u002e\u0077\u006f\u0072\u006b\u0062\u006f\u006f\u006b\u0049\u0064\u0041\u006e\u0064\u0043\u0075\u0072\u0072\u0065\u006e\u0074\u0044\u0061\u0079 article.invalid.category=\uc874\uc7ac\ud558\uc9c0\u0020\uc54a\ub294\u0020\uc544\ud2f0\ud074\u0020\uce74\ud14c\uace0\ub9ac\uc785\ub2c8\ub2e4\u000d articlemaincard.notfound.id=\u0061\u0072\u0074\u0069\u0063\u006c\u0065\u0020\u006d\u0061\u0069\u006e\u0020\u0063\u0061\u0072\u0064\uac00\u0020\uc874\uc7ac\ud558\uc9c0\u0020\uc54a\uc2b5\ub2c8\ub2e4\u000d diff --git a/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt index b3f38a430..439bade14 100644 --- a/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt @@ -13,6 +13,7 @@ import com.few.data.common.code.CategoryType import io.github.oshai.kotlinlogging.KotlinLogging import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe import io.mockk.* import java.net.URL @@ -43,12 +44,12 @@ class ReadArticleUseCaseTest : BehaviorSpec({ ) } - given("아티클 조회 요청이 온 상황에서") { + given("로그인 여부와 상관없이 아티클 조회 요청이 온 상황에서") { val articleId = 1L val memberId = 1L val useCaseIn = ReadArticleUseCaseIn(articleId, memberId) - `when`("아티클과 작가가 존재할 경우") { + `when`("요청한 아티클과 작가가 존재할 경우") { val writerId = 1L val mainImageURL = URL("http://localhost:8080/image/main/1") val title = "title" @@ -64,8 +65,8 @@ class ReadArticleUseCaseTest : BehaviorSpec({ createdAt = LocalDateTime.now() ) - val writerName = "hunca" - val writerProfileImageURL = URL("http://localhost:8080/image/profile/1") + val writerName = "writer" + val writerProfileImageURL = URL("http://localhost:8080/image/writer/1") every { readArticleWriterRecordService.execute(any()) } returns ReadWriterOutDto( writerId = writerId, name = writerName, @@ -73,16 +74,30 @@ class ReadArticleUseCaseTest : BehaviorSpec({ imageUrl = writerProfileImageURL ) - every { browseArticleProblemsService.execute(any()) } returns BrowseArticleProblemsOutDto(problemIds = listOf(1, 2, 3)) + val problemIds = listOf(1L, 2L, 3L) + every { browseArticleProblemsService.execute(any()) } returns BrowseArticleProblemsOutDto(problemIds = problemIds) - every { articleViewCountHandler.browseArticleViewCount(any()) } returns 1L + val views = 1L + every { articleViewCountHandler.browseArticleViewCount(any()) } returns views every { articleViewHisAsyncHandler.addArticleViewHis(any(), any(), any()) } answers { log.debug { "Inserting article view history asynchronously" } } - then("아티클이 정상 조회된다") { - useCase.execute(useCaseIn) + then("아티클과 연관된 정보를 조회한다") { + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.id shouldBe articleId + useCaseOut.writer.id shouldBe writerId + useCaseOut.writer.name shouldBe writerName + useCaseOut.writer.url shouldBe mainImageURL + useCaseOut.writer.imageUrl shouldBe writerProfileImageURL + useCaseOut.mainImageUrl shouldBe mainImageURL + useCaseOut.title shouldBe title + useCaseOut.content shouldBe content + useCaseOut.problemIds shouldBe problemIds + useCaseOut.category shouldBe CategoryType.ECONOMY.displayName + useCaseOut.views shouldBe views + useCaseOut.workbooks shouldBe emptyList() verify(exactly = 1) { articleDao.selectArticleRecord(any()) } verify(exactly = 1) { readArticleWriterRecordService.execute(any()) } @@ -92,13 +107,46 @@ class ReadArticleUseCaseTest : BehaviorSpec({ } } - `when`("존재하지 않는 아티클일 경우") { + `when`("요청한 아티클이 존재하지 않을 경우") { every { articleDao.selectArticleRecord(any()) } returns null then("예외가 발생한다") { shouldThrow { useCase.execute(useCaseIn) } verify(exactly = 1) { articleDao.selectArticleRecord(any()) } + verify(exactly = 0) { readArticleWriterRecordService.execute(any()) } + verify(exactly = 0) { browseArticleProblemsService.execute(any()) } + verify(exactly = 0) { articleViewCountHandler.browseArticleViewCount(any()) } + verify(exactly = 0) { articleViewHisAsyncHandler.addArticleViewHis(any(), any(), any()) } + } + } + + `when`("요청한 아티클의 작가가 존재하지 않을 경우") { + val writerId = 1L + val mainImageURL = URL("http://localhost:8080/image/main/1") + val title = "title" + val category = CategoryType.ECONOMY.code + val content = "content" + every { articleDao.selectArticleRecord(any()) } returns SelectArticleRecord( + articleId = articleId, + writerId = writerId, + mainImageURL = mainImageURL, + title = title, + category = category, + content = content, + createdAt = LocalDateTime.now() + ) + + every { readArticleWriterRecordService.execute(any()) } returns null + + then("예외가 발생한다") { + shouldThrow { useCase.execute(useCaseIn) } + + verify(exactly = 1) { articleDao.selectArticleRecord(any()) } + verify(exactly = 1) { readArticleWriterRecordService.execute(any()) } + verify(exactly = 0) { browseArticleProblemsService.execute(any()) } + verify(exactly = 0) { articleViewCountHandler.browseArticleViewCount(any()) } + verify(exactly = 0) { articleViewHisAsyncHandler.addArticleViewHis(any(), any(), any()) } } } } diff --git a/api/src/test/kotlin/com/few/api/domain/member/usecase/SaveMemberUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/member/usecase/SaveMemberUseCaseTest.kt index 112d8e8e1..50aad9d5e 100644 --- a/api/src/test/kotlin/com/few/api/domain/member/usecase/SaveMemberUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/member/usecase/SaveMemberUseCaseTest.kt @@ -7,10 +7,13 @@ import com.few.api.repo.dao.member.command.UpdateDeletedMemberTypeCommand import com.few.api.repo.dao.member.query.SelectMemberByEmailNotConsiderDeletedAtQuery import com.few.api.repo.dao.member.record.MemberIdAndIsDeletedRecord import com.few.email.service.member.SendAuthEmailService +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk import io.mockk.verify +import org.mockito.ArgumentMatchers.any class SaveMemberUseCaseTest : BehaviorSpec({ lateinit var memberDao: MemberDao @@ -40,11 +43,14 @@ class SaveMemberUseCaseTest : BehaviorSpec({ every { sendAuthEmailService.send(any()) } returns Unit then("인증 이메일 발송 성공 응답을 반환한다") { - useCase.execute(useCaseIn) + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.isSendAuthEmail shouldBe true verify(exactly = 1) { memberDao.selectMemberByEmail(any(SelectMemberByEmailNotConsiderDeletedAtQuery::class)) } verify(exactly = 1) { memberDao.insertMember(any()) } + verify(exactly = 0) { memberDao.updateMemberType(any(UpdateDeletedMemberTypeCommand::class)) } verify(exactly = 1) { idEncryption.encrypt(any()) } + verify(exactly = 1) { sendAuthEmailService.send(any()) } } } @@ -61,11 +67,14 @@ class SaveMemberUseCaseTest : BehaviorSpec({ every { sendAuthEmailService.send(any()) } returns Unit then("인증 이메일 발송 성공 응답을 반환한다") { - useCase.execute(useCaseIn) + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.isSendAuthEmail shouldBe true verify(exactly = 1) { memberDao.selectMemberByEmail(any(SelectMemberByEmailNotConsiderDeletedAtQuery::class)) } verify(exactly = 0) { memberDao.insertMember(any()) } + verify(exactly = 0) { memberDao.updateMemberType(any(UpdateDeletedMemberTypeCommand::class)) } verify(exactly = 1) { idEncryption.encrypt(any()) } + verify(exactly = 1) { sendAuthEmailService.send(any()) } } } @@ -81,13 +90,17 @@ class SaveMemberUseCaseTest : BehaviorSpec({ val token = "encryptedToken" every { idEncryption.encrypt(any()) } returns token + every { sendAuthEmailService.send(any()) } returns Unit + then("인증 이메일 발송 성공 응답을 반환한다") { - useCase.execute(useCaseIn) + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.isSendAuthEmail shouldBe true verify(exactly = 1) { memberDao.selectMemberByEmail(any(SelectMemberByEmailNotConsiderDeletedAtQuery::class)) } verify(exactly = 0) { memberDao.insertMember(any()) } verify(exactly = 1) { memberDao.updateMemberType(any(UpdateDeletedMemberTypeCommand::class)) } verify(exactly = 1) { idEncryption.encrypt(any()) } + verify(exactly = 1) { sendAuthEmailService.send(any()) } } } @@ -103,11 +116,12 @@ class SaveMemberUseCaseTest : BehaviorSpec({ every { sendAuthEmailService.send(any()) } throws Exception() then("인증 이메일 발송 실패 응답을 반환한다") { - useCase.execute(useCaseIn) + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.isSendAuthEmail shouldBe false - verify(exactly = 1) { memberDao.selectMemberByEmail(any(SelectMemberByEmailNotConsiderDeletedAtQuery::class)) } - verify(exactly = 1) { memberDao.insertMember(any()) } - verify(exactly = 1) { idEncryption.encrypt(any()) } + shouldThrow { + sendAuthEmailService.send(any()) + } } } } diff --git a/api/src/test/kotlin/com/few/api/domain/member/usecase/TokenUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/member/usecase/TokenUseCaseTest.kt index 5069f022e..9f0133b0d 100644 --- a/api/src/test/kotlin/com/few/api/domain/member/usecase/TokenUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/member/usecase/TokenUseCaseTest.kt @@ -9,7 +9,9 @@ import com.few.api.security.token.AuthToken import com.few.api.security.token.TokenGenerator import com.few.api.security.token.TokenResolver import com.few.data.common.code.MemberType +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk import io.mockk.verify @@ -29,7 +31,7 @@ class TokenUseCaseTest : BehaviorSpec({ useCase = TokenUseCase(tokenGenerator, tokenResolver, memberDao, idEncryption) } - given("리프레시 토큰이 포함된 요청이 온 상황에서") { + given("리프레시 토큰이 포함된 토큰 갱신 요청이 온 상황에서") { val oldRefreshToken = "refreshToken" val useCaseIn = TokenUseCaseIn( token = null, @@ -38,7 +40,7 @@ class TokenUseCaseTest : BehaviorSpec({ rt = null ) - `when`("요청에 refreshToken이 포함되어 있는 경우") { + `when`("유효한 리프레시 토큰인 경우") { val memberId = 1L every { tokenResolver.resolveId(any()) } returns memberId @@ -53,16 +55,31 @@ class TokenUseCaseTest : BehaviorSpec({ ) then("새로운 토큰을 반환한다") { - useCase.execute(useCaseIn) + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.accessToken shouldBe accessToken + useCaseOut.refreshToken shouldBe refreshToken + useCaseOut.isLogin shouldBe true verify(exactly = 1) { tokenResolver.resolveId(any()) } verify(exactly = 1) { tokenResolver.resolveEmail(any()) } verify(exactly = 1) { tokenGenerator.generateAuthToken(any(), any(), any()) } } } + + `when`("유효하지 않은 리프레시 토큰인 경우") { + every { tokenResolver.resolveId(any()) } throws IllegalStateException() + + then("예외를 반환한다") { + shouldThrow { useCase.execute(useCaseIn) } + + verify(exactly = 1) { tokenResolver.resolveId(any()) } + verify(exactly = 0) { tokenResolver.resolveEmail(any()) } + verify(exactly = 0) { tokenGenerator.generateAuthToken(any(), any(), any()) } + } + } } - given("멤버 아이디 정보를 암호화한 토큰이 포함된 요청이 온 상황에서") { + given("멤버 아이디 정보를 암호화한 토큰이 포함된 토큰 갱신 요청이 온 상황에서") { val encryptedIdToken = "token" val useCaseIn = TokenUseCaseIn( token = encryptedIdToken, @@ -71,7 +88,7 @@ class TokenUseCaseTest : BehaviorSpec({ rt = null ) - `when`("로그인하려는 멤버의 토큰이 포함되어 있는 경우") { + `when`("멤버 인증 토큰이 유효하고 인증을 위한 요청인 경우") { val decryptedId = "1" every { idEncryption.decrypt(any()) } returns decryptedId @@ -89,52 +106,60 @@ class TokenUseCaseTest : BehaviorSpec({ ) then("새로운 토큰을 반환한다") { - useCase.execute(useCaseIn) + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.accessToken shouldBe accessToken + useCaseOut.refreshToken shouldBe refreshToken + useCaseOut.isLogin shouldBe true verify(exactly = 1) { idEncryption.decrypt(any()) } - verify(exactly = 1) { - tokenGenerator.generateAuthToken( - any(), - any(), - any(), - any(), - any() - ) - } verify(exactly = 1) { memberDao.selectMemberEmailAndType(any()) } + verify(exactly = 0) { memberDao.updateMemberType(any(UpdateMemberTypeCommand::class)) } + verify(exactly = 1) { tokenGenerator.generateAuthToken(any(), any(), any(), any(), any()) } } } - `when`("회원가입을 완료 하려는 멤버의 토큰이 포함되어 있는 경우") { - every { idEncryption.decrypt(any()) } returns "1" + `when`("멤버 인증 토큰이 유효하고 가입을 위한 요청인 경우") { + val decryptedId = "1" + every { idEncryption.decrypt(any()) } returns decryptedId + val accessToken = "newAccessToken" + val refreshToken = "newRefreshToken" every { tokenGenerator.generateAuthToken(any(), any(), any(), any(), any()) } returns AuthToken( - accessToken = "accessToken", - refreshToken = "refreshToken" + accessToken = accessToken, + refreshToken = refreshToken ) + val email = "test@gmail.com" every { memberDao.selectMemberEmailAndType(any()) } returns MemberEmailAndTypeRecord( - email = "test@gmail.com", + email = email, memberType = MemberType.PREAUTH ) every { memberDao.updateMemberType(any(UpdateMemberTypeCommand::class)) } returns Unit then("새로운 토큰을 반환한다") { - useCase.execute(useCaseIn) + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.accessToken shouldBe accessToken + useCaseOut.refreshToken shouldBe refreshToken + useCaseOut.isLogin shouldBe false verify(exactly = 1) { idEncryption.decrypt(any()) } - verify(exactly = 1) { - tokenGenerator.generateAuthToken( - any(), - any(), - any(), - any(), - any() - ) - } verify(exactly = 1) { memberDao.selectMemberEmailAndType(any()) } verify(exactly = 1) { memberDao.updateMemberType(any(UpdateMemberTypeCommand::class)) } + verify(exactly = 1) { tokenGenerator.generateAuthToken(any(), any(), any(), any(), any()) } + } + } + + `when`("유효하지 않은 멤버 인증 토큰인 경우") { + every { idEncryption.decrypt(any()) } throws IllegalStateException() + + then("예외를 반환한다") { + shouldThrow { useCase.execute(useCaseIn) } + + verify(exactly = 1) { idEncryption.decrypt(any()) } + verify(exactly = 0) { memberDao.selectMemberEmailAndType(any()) } + verify(exactly = 0) { memberDao.updateMemberType(any(UpdateMemberTypeCommand::class)) } + verify(exactly = 0) { tokenGenerator.generateAuthToken(any(), any(), any(), any(), any()) } } } } diff --git a/api/src/test/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCaseTest.kt index ac39f45dc..6a75891cb 100644 --- a/api/src/test/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCaseTest.kt @@ -5,6 +5,7 @@ import com.few.api.repo.dao.problem.ProblemDao import com.few.api.repo.dao.problem.record.ProblemIdsRecord import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk import io.mockk.verify @@ -19,21 +20,23 @@ class BrowseProblemsUseCaseTest : BehaviorSpec({ useCase = BrowseProblemsUseCase(problemDao) } - given("특정 아티클에 대한") { + given("특정 아티클에 대한 문제 조회 요청이 온 상황에서") { val articleId = 1L val useCaseIn = BrowseProblemsUseCaseIn(articleId = articleId) - `when`("문제가 존재할 경우") { - every { problemDao.selectProblemsByArticleId(any()) } returns ProblemIdsRecord(listOf(1, 2, 3)) + `when`("아티클의 문제가 존재할 경우") { + val problemIds = listOf(1L, 2L, 3L) + every { problemDao.selectProblemsByArticleId(any()) } returns ProblemIdsRecord(problemIds) - then("문제번호가 정상적으로 조회된다") { - useCase.execute(useCaseIn) + then("문제 목록을 반환한다") { + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.problemIds shouldBe problemIds verify(exactly = 1) { problemDao.selectProblemsByArticleId(any()) } } } - `when`("문제가 존재하지 않을 경우") { + `when`("아티클의 문제가 존재하지 않을 경우") { every { problemDao.selectProblemsByArticleId(any()) } returns null then("예외가 발생한다") { diff --git a/api/src/test/kotlin/com/few/api/domain/problem/usecase/CheckProblemUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/problem/usecase/CheckProblemUseCaseTest.kt index 1450f07e9..035bada61 100644 --- a/api/src/test/kotlin/com/few/api/domain/problem/usecase/CheckProblemUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/problem/usecase/CheckProblemUseCaseTest.kt @@ -23,22 +23,25 @@ class CheckProblemUseCaseTest : BehaviorSpec({ useCase = CheckProblemUseCase(problemDao, submitHistoryDao) } - given("문제 정답 확인 요청이 온 상황에서") { + given("특정 문제에 대한 정답 확인 요청이 온 상황에서") { val problemId = 1L val submissionVal = "1" val useCaseIn = CheckProblemUseCaseIn(problemId = problemId, sub = submissionVal) `when`("제출 값과 문제 정답이 같을 경우") { val answer = submissionVal - every { problemDao.selectProblemAnswer(any()) } returns SelectProblemAnswerRecord(id = problemId, answer = answer, explanation = "해설입니다.") + val explanation = "해설입니다." + every { problemDao.selectProblemAnswer(any()) } returns SelectProblemAnswerRecord(id = problemId, answer = answer, explanation = explanation) val problemSubmitHistoryId = 1L every { submitHistoryDao.insertSubmitHistory(any()) } returns problemSubmitHistoryId then("문제가 정답처리 된다") { val useCaseOut = useCase.execute(useCaseIn) - useCaseOut.isSolved shouldBe true + useCaseOut.answer shouldBe answer + useCaseOut.explanation shouldBe explanation + verify(exactly = 1) { problemDao.selectProblemAnswer(any()) } verify(exactly = 1) { submitHistoryDao.insertSubmitHistory(any()) } } @@ -46,10 +49,11 @@ class CheckProblemUseCaseTest : BehaviorSpec({ `when`("제출 값과 문제 정답이 다를 경우") { val answer = "2" + val explanation = "해설입니다." every { problemDao.selectProblemAnswer(any()) } returns SelectProblemAnswerRecord( id = problemId, answer = answer, - explanation = "해설입니다." + explanation = explanation ) val problemSubmitHistoryId = 1L @@ -57,8 +61,10 @@ class CheckProblemUseCaseTest : BehaviorSpec({ then("문제가 오답처리 된다") { val useCaseOut = useCase.execute(useCaseIn) - useCaseOut.isSolved shouldBe false + useCaseOut.answer shouldBe answer + useCaseOut.explanation shouldBe explanation + verify(exactly = 1) { problemDao.selectProblemAnswer(any()) } verify(exactly = 1) { submitHistoryDao.insertSubmitHistory(any()) } } @@ -69,7 +75,9 @@ class CheckProblemUseCaseTest : BehaviorSpec({ then("예외가 발생한다") { shouldThrow { useCase.execute(useCaseIn) } + verify(exactly = 1) { problemDao.selectProblemAnswer(any()) } + verify(exactly = 0) { submitHistoryDao.insertSubmitHistory(any()) } } } } diff --git a/api/src/test/kotlin/com/few/api/domain/problem/usecase/ReadProblemUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/problem/usecase/ReadProblemUseCaseTest.kt index f1ba360b3..ff4e7d8e9 100644 --- a/api/src/test/kotlin/com/few/api/domain/problem/usecase/ReadProblemUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/problem/usecase/ReadProblemUseCaseTest.kt @@ -8,6 +8,7 @@ import com.few.api.repo.dao.problem.support.Contents import com.few.api.repo.dao.problem.support.ContentsJsonMapper import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk import io.mockk.verify @@ -25,7 +26,7 @@ class ReadProblemUseCaseTest : BehaviorSpec({ useCase = ReadProblemUseCase(problemDao, contentsJsonMapper) } - given("문제를 조회할 상황에서") { + given("특정 문제를 조회하는 요청이 온 상황에서") { val problemId = 1L val useCaseIn = ReadProblemUseCaseIn(problemId = problemId) @@ -34,12 +35,21 @@ class ReadProblemUseCaseTest : BehaviorSpec({ val problemContents = "{}" every { problemDao.selectProblemContents(any()) } returns SelectProblemRecord(id = problemId, title = title, contents = problemContents) + val contentCount = 2 every { contentsJsonMapper.toObject(any()) } returns Contents( - IntStream.range(1, 3).mapToObj { Content(number = it.toLong(), content = "{}") }.toList() + IntStream.range(1, 1 + contentCount) + .mapToObj { Content(number = it.toLong(), content = "{}") }.toList() ) - then("정상적으로 실행되어야 한다") { - useCase.execute(useCaseIn) + then("문제 정보를 반환한다") { + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.id shouldBe problemId + useCaseOut.title shouldBe title + useCaseOut.contents.size shouldBe contentCount + useCaseOut.contents.forEachIndexed { index, content -> + content.number shouldBe (index + 1).toLong() + content.content shouldBe "{}" + } verify(exactly = 1) { problemDao.selectProblemContents(any()) } verify(exactly = 1) { contentsJsonMapper.toObject(any()) } @@ -49,10 +59,11 @@ class ReadProblemUseCaseTest : BehaviorSpec({ `when`("문제가 존재하지 않을 경우") { every { problemDao.selectProblemContents(any()) } returns null - then("예외가 발생해야 한다") { + then("예외가 발생한다") { shouldThrow { useCase.execute(useCaseIn) } verify(exactly = 1) { problemDao.selectProblemContents(any()) } + verify(exactly = 0) { contentsJsonMapper.toObject(any()) } } } } diff --git a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCaseTest.kt index e65655518..b7bb374df 100644 --- a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCaseTest.kt @@ -5,8 +5,10 @@ import com.few.api.domain.subscription.service.SubscriptionArticleService import com.few.api.domain.subscription.usecase.dto.BrowseSubscribeWorkbooksUseCaseIn import com.few.api.repo.dao.subscription.SubscriptionDao import com.few.api.repo.dao.subscription.record.MemberWorkbookSubscriptionStatusRecord +import com.few.api.web.support.WorkBookStatus import io.github.oshai.kotlinlogging.KotlinLogging import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk import io.mockk.verify @@ -26,48 +28,167 @@ class BrowseSubscribeWorkbooksUseCaseTest : BehaviorSpec({ useCase = BrowseSubscribeWorkbooksUseCase(subscriptionDao, subscriptionArticleService, objectMapper) } - given("사용자 구독 정보 조회 요청이 온 상황에서") { + given("멤버의 구독 워크북 정보 조회 요청이 온 상황에서") { val memberId = 1L val useCaseIn = BrowseSubscribeWorkbooksUseCaseIn(memberId = memberId) - `when`("사용자의 구독 정보가 있는 경우") { + `when`("멤버의 구독 워크북 정보가 존재할 경우") { + val inactiveWorkbookId = 1L + val inactiveWorkbookCurrentDay = 2 + val inactiveWorkbookTotalDay = 3 every { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } returns listOf( MemberWorkbookSubscriptionStatusRecord( - workbookId = 1L, + workbookId = inactiveWorkbookId, isActiveSub = false, - currentDay = 1, - totalDay = 3 + currentDay = inactiveWorkbookCurrentDay, + totalDay = inactiveWorkbookTotalDay ) ) + val activeWorkbookId = 2L + val activeWorkbookCurrentDay = 1 + val activeWorkbookTotalDay = 3 every { subscriptionDao.selectAllActiveWorkbookSubscriptionStatus(any()) } returns listOf( MemberWorkbookSubscriptionStatusRecord( - workbookId = 2L, + workbookId = activeWorkbookId, isActiveSub = true, - currentDay = 2, - totalDay = 3 + currentDay = activeWorkbookCurrentDay, + totalDay = activeWorkbookTotalDay ) + ) every { subscriptionArticleService.readArticleIdByWorkbookIdAndDay(any()) - } returns 1L andThen 2L + } returns inactiveWorkbookId andThen activeWorkbookId + val activeWorkbookSubscriptionCount = 1 + val inactiveWorkbookSubscriptionCount = 2 every { subscriptionDao.countAllWorkbookSubscription(any()) } returns mapOf( - 1L to 1, - 2L to 2 + inactiveWorkbookId to inactiveWorkbookSubscriptionCount, + activeWorkbookId to activeWorkbookSubscriptionCount ) - every { objectMapper.writeValueAsString(any()) } returns "{\"articleId\":1}" andThen "{\"articleId\":2}" + every { objectMapper.writeValueAsString(any()) } returns "{\"articleId\":$inactiveWorkbookId}" andThen "{\"articleId\":$activeWorkbookId}" + + then("멤버의 구독 워크북 정보를 반환한다") { + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.workbooks.size shouldBe 2 + + val inActiveSubscriptionWorkbook = useCaseOut.workbooks[0] + inActiveSubscriptionWorkbook.workbookId shouldBe inactiveWorkbookId + inActiveSubscriptionWorkbook.isActiveSub shouldBe WorkBookStatus.DONE + inActiveSubscriptionWorkbook.currentDay shouldBe inactiveWorkbookCurrentDay + inActiveSubscriptionWorkbook.totalDay shouldBe inactiveWorkbookTotalDay + inActiveSubscriptionWorkbook.totalSubscriber shouldBe inactiveWorkbookSubscriptionCount + inActiveSubscriptionWorkbook.articleInfo shouldBe "{\"articleId\":$inactiveWorkbookId}" - then("사용자의 구독 정보를 조회한다") { - useCase.execute(useCaseIn) + val activeSubscriptionWorkbook = useCaseOut.workbooks[1] + activeSubscriptionWorkbook.workbookId shouldBe activeWorkbookId + activeSubscriptionWorkbook.isActiveSub shouldBe WorkBookStatus.ACTIVE + activeSubscriptionWorkbook.currentDay shouldBe activeWorkbookCurrentDay + activeSubscriptionWorkbook.totalDay shouldBe activeWorkbookTotalDay + activeSubscriptionWorkbook.totalSubscriber shouldBe activeWorkbookSubscriptionCount + activeSubscriptionWorkbook.articleInfo shouldBe "{\"articleId\":$activeWorkbookId}" verify(exactly = 1) { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } verify(exactly = 1) { subscriptionDao.selectAllActiveWorkbookSubscriptionStatus(any()) } + verify(exactly = 2) { subscriptionArticleService.readArticleIdByWorkbookIdAndDay(any()) } verify(exactly = 1) { subscriptionDao.countAllWorkbookSubscription(any()) } verify(exactly = 2) { objectMapper.writeValueAsString(any()) } } } + + `when`("멤버의 구독 비활성 워크북 정보만 존재할 경우") { + val inactiveWorkbookId = 1L + val inactiveWorkbookCurrentDay = 2 + val inactiveWorkbookTotalDay = 3 + every { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } returns listOf( + MemberWorkbookSubscriptionStatusRecord( + workbookId = inactiveWorkbookId, + isActiveSub = false, + currentDay = inactiveWorkbookCurrentDay, + totalDay = inactiveWorkbookTotalDay + ) + ) + + every { subscriptionDao.selectAllActiveWorkbookSubscriptionStatus(any()) } returns emptyList() + + every { + subscriptionArticleService.readArticleIdByWorkbookIdAndDay(any()) + } returns inactiveWorkbookId + + val inactiveWorkbookSubscriptionCount = 2 + every { subscriptionDao.countAllWorkbookSubscription(any()) } returns mapOf( + inactiveWorkbookId to inactiveWorkbookSubscriptionCount + ) + + every { objectMapper.writeValueAsString(any()) } returns "{\"articleId\":$inactiveWorkbookId}" + + then("멤버의 구독 비활성 워크북 정보를 반환한다") { + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.workbooks.size shouldBe 1 + + val inActiveSubscriptionWorkbook = useCaseOut.workbooks[0] + inActiveSubscriptionWorkbook.workbookId shouldBe inactiveWorkbookId + inActiveSubscriptionWorkbook.isActiveSub shouldBe WorkBookStatus.DONE + inActiveSubscriptionWorkbook.currentDay shouldBe inactiveWorkbookCurrentDay + inActiveSubscriptionWorkbook.totalDay shouldBe inactiveWorkbookTotalDay + inActiveSubscriptionWorkbook.totalSubscriber shouldBe inactiveWorkbookSubscriptionCount + inActiveSubscriptionWorkbook.articleInfo shouldBe "{\"articleId\":$inactiveWorkbookId}" + + verify(exactly = 1) { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } + verify(exactly = 1) { subscriptionDao.selectAllActiveWorkbookSubscriptionStatus(any()) } + verify(exactly = 1) { subscriptionArticleService.readArticleIdByWorkbookIdAndDay(any()) } + verify(exactly = 1) { subscriptionDao.countAllWorkbookSubscription(any()) } + verify(exactly = 1) { objectMapper.writeValueAsString(any()) } + } + } + + `when`("멤버의 구독 활성 워크북 정보만 존재할 경우") { + every { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } returns emptyList() + + val activeWorkbookId = 1L + val activeWorkbookCurrentDay = 2 + val activeWorkbookTotalDay = 3 + every { subscriptionDao.selectAllActiveWorkbookSubscriptionStatus(any()) } returns listOf( + MemberWorkbookSubscriptionStatusRecord( + workbookId = activeWorkbookId, + isActiveSub = false, + currentDay = activeWorkbookCurrentDay, + totalDay = activeWorkbookTotalDay + ) + ) + + every { + subscriptionArticleService.readArticleIdByWorkbookIdAndDay(any()) + } returns activeWorkbookId + + val activeWorkbookSubscriptionCount = 1 + every { subscriptionDao.countAllWorkbookSubscription(any()) } returns mapOf( + activeWorkbookId to activeWorkbookSubscriptionCount + ) + + every { objectMapper.writeValueAsString(any()) } returns "{\"articleId\":$activeWorkbookId}" + + then("멤버의 구독 활성 워크북 정보를 반환한다") { + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.workbooks.size shouldBe 1 + + val inActiveSubscriptionWorkbook = useCaseOut.workbooks[0] + inActiveSubscriptionWorkbook.workbookId shouldBe activeWorkbookId + inActiveSubscriptionWorkbook.isActiveSub shouldBe WorkBookStatus.DONE + inActiveSubscriptionWorkbook.currentDay shouldBe activeWorkbookCurrentDay + inActiveSubscriptionWorkbook.totalDay shouldBe activeWorkbookTotalDay + inActiveSubscriptionWorkbook.totalSubscriber shouldBe activeWorkbookSubscriptionCount + inActiveSubscriptionWorkbook.articleInfo shouldBe "{\"articleId\":$activeWorkbookId}" + + verify(exactly = 1) { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } + verify(exactly = 1) { subscriptionDao.selectAllActiveWorkbookSubscriptionStatus(any()) } + verify(exactly = 1) { subscriptionArticleService.readArticleIdByWorkbookIdAndDay(any()) } + verify(exactly = 1) { subscriptionDao.countAllWorkbookSubscription(any()) } + verify(exactly = 1) { objectMapper.writeValueAsString(any()) } + } + } } }) \ No newline at end of file diff --git a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCaseTest.kt index 8f843685f..6b86b8eb5 100644 --- a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCaseTest.kt @@ -20,7 +20,6 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ lateinit var subscriptionDao: SubscriptionDao lateinit var applicationEventPublisher: ApplicationEventPublisher lateinit var useCase: SubscribeWorkbookUseCase - val workbookId = 1L beforeContainer { subscriptionDao = mockk() @@ -28,12 +27,12 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ useCase = SubscribeWorkbookUseCase(subscriptionDao, applicationEventPublisher) } - given("구독 요청이 온 상황에서") { + given("멤버의 워크북 구독 요청이 온 상황에서") { val workbookId = 1L val memberId = 1L val useCaseIn = SubscribeWorkbookUseCaseIn(workbookId = workbookId, memberId = memberId) - `when`("subscriptionStatus가 null일 경우") { + `when`("멤버의 구독 히스토리가 없는 경우") { every { subscriptionDao.selectTopWorkbookSubscriptionStatus(any()) } returns null every { subscriptionDao.insertWorkbookSubscription(any()) } just Runs @@ -43,9 +42,10 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ log.debug { "Mocking applicationEventPublisher.publishEvent(any()) was called" } } - then("신규 구독을 추가한다") { + then("구독한다") { useCase.execute(useCaseIn) + verify(exactly = 1) { subscriptionDao.selectTopWorkbookSubscriptionStatus(any()) } verify(exactly = 1) { subscriptionDao.insertWorkbookSubscription(any()) } verify(exactly = 0) { subscriptionDao.countWorkbookMappedArticles(any()) } verify(exactly = 0) { subscriptionDao.reSubscribeWorkbookSubscription(any()) } @@ -53,7 +53,7 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ } } - `when`("구독을 취소한 경우") { + `when`("이미 구독한 히스토리가 있고 구독이 취소된 경우") { val day = 2 every { subscriptionDao.selectTopWorkbookSubscriptionStatus(any()) } returns WorkbookSubscriptionStatus( workbookId = workbookId, @@ -74,6 +74,7 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ then("재구독한다") { useCase.execute(useCaseIn) + verify(exactly = 1) { subscriptionDao.selectTopWorkbookSubscriptionStatus(any()) } verify(exactly = 0) { subscriptionDao.insertWorkbookSubscription(any()) } verify(exactly = 1) { subscriptionDao.countWorkbookMappedArticles(any()) } verify(exactly = 1) { subscriptionDao.reSubscribeWorkbookSubscription(any()) } @@ -81,27 +82,18 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ } } - `when`("이미 구독하고 있을 경우") { + `when`("구독 중인 경우") { val day = 2 every { subscriptionDao.selectTopWorkbookSubscriptionStatus(any()) } returns WorkbookSubscriptionStatus(workbookId = workbookId, isActiveSub = true, day) - val lastDay = 3 - every { subscriptionDao.countWorkbookMappedArticles(any()) } returns lastDay - - every { subscriptionDao.reSubscribeWorkbookSubscription(any()) } just Runs - - val event = WorkbookSubscriptionEvent(workbookId) - every { applicationEventPublisher.publishEvent(event) } answers { - log.debug { "Mocking applicationEventPublisher.publishEvent(any()) was called" } - } - then("예외가 발생한다") { shouldThrow { useCase.execute(useCaseIn) } + verify(exactly = 1) { subscriptionDao.selectTopWorkbookSubscriptionStatus(any()) } verify(exactly = 0) { subscriptionDao.insertWorkbookSubscription(any()) } verify(exactly = 0) { subscriptionDao.countWorkbookMappedArticles(any()) } verify(exactly = 0) { subscriptionDao.reSubscribeWorkbookSubscription(any()) } - verify(exactly = 0) { applicationEventPublisher.publishEvent(event) } + verify(exactly = 0) { applicationEventPublisher.publishEvent(WorkbookSubscriptionEvent(workbookId)) } } } } diff --git a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeAllUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeAllUseCaseTest.kt new file mode 100644 index 000000000..37b7ea8dd --- /dev/null +++ b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeAllUseCaseTest.kt @@ -0,0 +1,54 @@ +package com.few.api.domain.subscription.usecase + +import com.few.api.domain.subscription.usecase.dto.UnsubscribeAllUseCaseIn +import com.few.api.repo.dao.subscription.SubscriptionDao +import io.github.oshai.kotlinlogging.KotlinLogging +import io.kotest.core.spec.style.BehaviorSpec +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import io.mockk.just +import io.mockk.Runs +class UnsubscribeAllUseCaseTest : BehaviorSpec({ + val log = KotlinLogging.logger {} + + lateinit var subscriptionDao: SubscriptionDao + lateinit var useCase: UnsubscribeAllUseCase + + beforeContainer { + subscriptionDao = mockk() + useCase = UnsubscribeAllUseCase(subscriptionDao) + } + + given("구독 취소 의견이 포함된 전체 구독 취소 요청이 온 상황에서") { + val memberId = 1L + val opinion = "취소합니다." + val useCaseIn = UnsubscribeAllUseCaseIn(memberId = memberId, opinion = opinion) + + `when`("멤버의 구독 히스토리가 있는 경우") { + every { subscriptionDao.updateDeletedAtInAllSubscription(any()) } just Runs + + then("의견을 저장하고 구독 전체를 취소한다") { + useCase.execute(useCaseIn) + + verify(exactly = 1) { subscriptionDao.updateDeletedAtInAllSubscription(any()) } + } + } + } + + given("구독 취소 의견이 포함되지 않은 전체 구독 취소 요청이 온 상황에서") { + val memberId = 1L + val opinion = "" + val useCaseIn = UnsubscribeAllUseCaseIn(memberId = memberId, opinion = opinion) + + `when`("멤버의 구독 히스토리가 있는 경우") { + every { subscriptionDao.updateDeletedAtInAllSubscription(any()) } just Runs + + then("의견을 cancel로 저장하고 구독 전체를 취소한다") { + useCase.execute(useCaseIn) + + verify(exactly = 1) { subscriptionDao.updateDeletedAtInAllSubscription(any()) } + } + } + } +}) \ No newline at end of file diff --git a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeWorkbookUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeWorkbookUseCaseTest.kt new file mode 100644 index 000000000..94224f0bb --- /dev/null +++ b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeWorkbookUseCaseTest.kt @@ -0,0 +1,54 @@ +package com.few.api.domain.subscription.usecase + +import com.few.api.domain.subscription.usecase.dto.UnsubscribeWorkbookUseCaseIn +import com.few.api.repo.dao.subscription.SubscriptionDao +import io.github.oshai.kotlinlogging.KotlinLogging +import io.kotest.core.spec.style.BehaviorSpec +import io.mockk.* +import org.junit.jupiter.api.Assertions.* + +class UnsubscribeWorkbookUseCaseTest : BehaviorSpec({ + val log = KotlinLogging.logger {} + + lateinit var subscriptionDao: SubscriptionDao + lateinit var useCase: UnsubscribeWorkbookUseCase + + beforeContainer { + subscriptionDao = mockk() + useCase = UnsubscribeWorkbookUseCase(subscriptionDao) + } + + given("구독 취소 의견이 포함된 특정 워크북 구독 취소 요청이 온 상황에서") { + val memberId = 1L + val workbookId = 1L + val opinion = "취소합니다." + val useCaseIn = UnsubscribeWorkbookUseCaseIn(memberId = memberId, workbookId = workbookId, opinion = opinion) + + `when`("멤버의 특정 워크북 구독 히스토리가 있는 경우") { + every { subscriptionDao.updateDeletedAtInWorkbookSubscription(any()) } just Runs + + then("의견을 저장하고 구독 전체를 취소한다") { + useCase.execute(useCaseIn) + + verify(exactly = 1) { subscriptionDao.updateDeletedAtInWorkbookSubscription(any()) } + } + } + } + + given("구독 취소 의견이 포함되지 않은 특정 워크북 구독 취소 요청이 온 상황에서") { + val memberId = 1L + val workbookId = 1L + val opinion = "" + val useCaseIn = UnsubscribeWorkbookUseCaseIn(memberId = memberId, workbookId = workbookId, opinion = opinion) + + `when`("멤버의 특정 워크북 구독 히스토리가 있는 경우") { + every { subscriptionDao.updateDeletedAtInWorkbookSubscription(any()) } just Runs + + then("의견을 cancel로 저장하고 특정 위크북에 대한 구독을 취소한다") { + useCase.execute(useCaseIn) + + verify(exactly = 1) { subscriptionDao.updateDeletedAtInWorkbookSubscription(any()) } + } + } + } +}) \ No newline at end of file diff --git a/api/src/test/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCaseTest.kt index d1b435c2a..7735fdb62 100644 --- a/api/src/test/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCaseTest.kt @@ -16,6 +16,7 @@ import com.few.api.web.support.ViewCategory import com.few.api.web.support.WorkBookCategory import com.few.data.common.code.CategoryType import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk import io.mockk.verify @@ -40,11 +41,13 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ BrowseWorkbooksUseCase(workbookDao, workbookMemberService, workbookSubscribeService, workbookOrderDelegatorExecutor) } - given("로그인 안된 상황에서 카테고리를 지정하여 다수 워크북 조회 요청이 온 상황에서") { - val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = null, memberId = null) + given("메인 뷰에서 로그인 된 상태로 카테고리를 지정하여 다수 워크북 조회 요청이 온 상황에서") { + val memberId = 1L + val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = ViewCategory.MAIN_CARD, memberId = memberId) - `when`("경제로 지정되어 있을 경우") { - every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns IntStream.range(1, 3).mapToObj { + `when`("특정 카테고리로 지정되어 있을 경우") { + val workbookCount = 2 + every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns IntStream.range(1, 1 + workbookCount).mapToObj { SelectWorkBookRecordWithSubscriptionCount( id = it.toLong(), title = "workbook title$it", @@ -60,24 +63,39 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ 1L to listOf( WriterMappedWorkbookOutDto( writerId = 1L, - name = "writer1", - url = URL("http://localhost:8080/image/writer/1"), + name = "hunca", + url = URL("https://jh-labs.tistory.com/"), workbookId = 1L ) ), 2L to listOf( WriterMappedWorkbookOutDto( writerId = 2L, - name = "writer2", - url = URL("http://localhost:8080/image/writer/2"), + name = "hunca", + url = URL("https://jh-labs.tistory.com/"), workbookId = 2L ) ) ) every { - workbookOrderDelegatorExecutor.execute(any()) - } returns IntStream.range(1, 3).mapToObj { + workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) + } returns listOf( + BrowseMemberSubscribeWorkbooksOutDto( + workbookId = 1L, + isActiveSub = true, + currentDay = 1 + ), + BrowseMemberSubscribeWorkbooksOutDto( + workbookId = 2L, + isActiveSub = false, + currentDay = 1 + ) + ) + + every { + workbookOrderDelegatorExecutor.execute(any(AuthMainViewWorkbookOrderDelegator::class)) + } returns IntStream.range(1, 1 + workbookCount).mapToObj { BrowseWorkBookDetail( id = it.toLong(), title = "workbook title$it", @@ -89,7 +107,7 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ WriterDetail( id = it.toLong(), name = "writer$it", - url = URL("http://localhost:8080/image/writer/$it") + url = URL("https://jh-labs.tistory.com/") ) ), subscriptionCount = it.toLong() @@ -97,83 +115,99 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ }.toList() then("경제 카테고리의 워크북이 조회된다") { - useCase.execute(useCaseIn) + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.workbooks.size shouldBe workbookCount + + useCaseOut.workbooks.forEachIndexed { index, browseWorkBookDetail -> + browseWorkBookDetail.id shouldBe (index + 1).toLong() + browseWorkBookDetail.title shouldBe "workbook title${index + 1}" + browseWorkBookDetail.mainImageUrl shouldBe URL("http://localhost:8080/image/main/${index + 1}") + browseWorkBookDetail.category shouldBe WorkBookCategory.ECONOMY.displayName + browseWorkBookDetail.description shouldBe "workbook${index + 1} description" + browseWorkBookDetail.writerDetails.size shouldBe 1 + browseWorkBookDetail.writerDetails[0].id shouldBe (index + 1).toLong() + browseWorkBookDetail.writerDetails[0].name shouldBe "writer${index + 1}" + browseWorkBookDetail.writerDetails[0].url shouldBe URL("https://jh-labs.tistory.com/") + browseWorkBookDetail.subscriptionCount shouldBe (index + 1).toLong() + } verify(exactly = 1) { workbookDao.browseWorkBookWithSubscriptionCount(any()) } verify(exactly = 1) { workbookMemberService.browseWorkbookWriterRecords(any()) } - verify(exactly = 0) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } - verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any()) } + verify(exactly = 1) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } + verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any(AuthMainViewWorkbookOrderDelegator::class)) } } } } - given("로그인 안된 상황에서 메인 뷰에서 카테고리를 지정하여 다수 워크북 조회 요청이 온 상황에서") { + given("메인 뷰에서 로그인 안된 상태로 카테고리를 지정하여 다수 워크북 조회 요청이 온 상황에서") { val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = ViewCategory.MAIN_CARD, memberId = null) - `when`("경제로 지정되어 있을 경우") { - every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns listOf( - SelectWorkBookRecordWithSubscriptionCount( - id = 1L, - title = "workbook title", - mainImageUrl = URL("https://jh-labs.tistory.com/"), - category = (10).toByte(), - description = "workbook description", - createdAt = LocalDateTime.now(), - subscriptionCount = 10 - ), - SelectWorkBookRecordWithSubscriptionCount( - id = 2L, - title = "workbook title", - mainImageUrl = URL("https://jh-labs.tistory.com/"), - category = (10).toByte(), - description = "workbook description", - createdAt = LocalDateTime.now(), - subscriptionCount = 10 - ) - ) - - every { workbookMemberService.browseWorkbookWriterRecords(any()) } returns mapOf( - 1L to listOf( - WriterMappedWorkbookOutDto( - writerId = 1L, - name = "hunca", - url = URL("https://jh-labs.tistory.com/"), - workbookId = 1L + `when`("특정 카테고리로 지정되어 있을 경우") { + val workbookCount = 2 + every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns + IntStream.range(1, 1 + workbookCount).mapToObj { + SelectWorkBookRecordWithSubscriptionCount( + id = it.toLong(), + title = "workbook title$it", + mainImageUrl = URL("http://localhost:8080/image/main/$it"), + category = CategoryType.ECONOMY.code, + description = "workbook$it description", + createdAt = LocalDateTime.now(), + subscriptionCount = it.toLong() ) - ), - 2L to listOf( + }.toList() + + val workbookWriterRecords = HashMap>() + for (i in 1..workbookCount) { + workbookWriterRecords[i.toLong()] = listOf( WriterMappedWorkbookOutDto( - writerId = 2L, - name = "hunca", - url = URL("https://jh-labs.tistory.com/"), - workbookId = 2L + writerId = i.toLong(), + name = "writer$i", + url = URL("http://localhost:8080/image/writer/$i"), + workbookId = i.toLong() ) ) - ) + } + every { workbookMemberService.browseWorkbookWriterRecords(any()) } returns workbookWriterRecords every { workbookOrderDelegatorExecutor.execute(any(BasicWorkbookOrderDelegator::class)) - } returns listOf( - BrowseWorkBookDetail( - id = 1L, - title = "workbook title", - mainImageUrl = URL("https://jh-labs.tistory.com/"), - category = WorkBookCategory.ECONOMY.displayName, - description = "workbook description", - createdAt = LocalDateTime.now(), - writerDetails = listOf( - WriterDetail( - id = 1L, - name = "hunca", - url = URL("https://jh-labs.tistory.com/") - ) - ), - subscriptionCount = 10 - ) - ) + } returns + IntStream.range(1, 1 + workbookCount).mapToObj { + BrowseWorkBookDetail( + id = it.toLong(), + title = "workbook title$it", + mainImageUrl = URL("http://localhost:8080/image/main/$it"), + category = WorkBookCategory.ECONOMY.displayName, + description = "workbook$it description", + createdAt = LocalDateTime.now(), + writerDetails = listOf( + WriterDetail( + id = it.toLong(), + name = "writer$it", + url = URL("http://localhost:8080/image/writer/$it") + ) + ), + subscriptionCount = it.toLong() + ) + }.toList() then("경제 카테고리의 워크북이 조회된다") { - useCase.execute(useCaseIn) + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.workbooks.size shouldBe workbookCount + + useCaseOut.workbooks.forEachIndexed { index, browseWorkBookDetail -> + browseWorkBookDetail.id shouldBe (index + 1).toLong() + browseWorkBookDetail.title shouldBe "workbook title${index + 1}" + browseWorkBookDetail.mainImageUrl shouldBe URL("http://localhost:8080/image/main/${index + 1}") + browseWorkBookDetail.category shouldBe WorkBookCategory.ECONOMY.displayName + browseWorkBookDetail.description shouldBe "workbook${index + 1} description" + browseWorkBookDetail.writerDetails.size shouldBe 1 + browseWorkBookDetail.writerDetails[0].id shouldBe (index + 1).toLong() + browseWorkBookDetail.writerDetails[0].name shouldBe "writer${index + 1}" + browseWorkBookDetail.writerDetails[0].url shouldBe URL("http://localhost:8080/image/writer/${index + 1}") + browseWorkBookDetail.subscriptionCount shouldBe (index + 1).toLong() + } verify(exactly = 1) { workbookDao.browseWorkBookWithSubscriptionCount(any()) } verify(exactly = 1) { workbookMemberService.browseWorkbookWriterRecords(any()) } @@ -183,12 +217,15 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ } } - given("로그인 된 상황에서 카테고리를 지정하여 다수 워크북 조회 요청이 온 상황에서") { - val memberId = 1L - val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = null, memberId = memberId) + given("메인 뷰가 아닌 상태로 카테고리를 지정하여 다수 워크북 조회 요청이 온 상황에서") { + val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = null, memberId = null) - `when`("경제로 지정되어 있을 경우") { - every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns IntStream.range(1, 3).mapToObj { + val workbookCount = 2 + `when`("특정 카테고리로 지정되어 있을 경우") { + every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns IntStream.range( + 1, + 1 + workbookCount + ).mapToObj { SelectWorkBookRecordWithSubscriptionCount( id = it.toLong(), title = "workbook title$it", @@ -220,23 +257,8 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ ) every { - workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) - } returns listOf( - BrowseMemberSubscribeWorkbooksOutDto( - workbookId = 1L, - isActiveSub = true, - currentDay = 1 - ), - BrowseMemberSubscribeWorkbooksOutDto( - workbookId = 2L, - isActiveSub = false, - currentDay = 1 - ) - ) - - every { - workbookOrderDelegatorExecutor.execute(any()) - } returns IntStream.range(1, 3).mapToObj { + workbookOrderDelegatorExecutor.execute(any(BasicWorkbookOrderDelegator::class)) + } returns IntStream.range(1, 1 + workbookCount).mapToObj { BrowseWorkBookDetail( id = it.toLong(), title = "workbook title$it", @@ -256,95 +278,25 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ }.toList() then("경제 카테고리의 워크북이 조회된다") { - useCase.execute(useCaseIn) + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.workbooks.size shouldBe workbookCount + useCaseOut.workbooks.forEachIndexed { index, browseWorkBookDetail -> + browseWorkBookDetail.id shouldBe (index + 1).toLong() + browseWorkBookDetail.title shouldBe "workbook title${index + 1}" + browseWorkBookDetail.mainImageUrl shouldBe URL("http://localhost:8080/image/main/${index + 1}") + browseWorkBookDetail.category shouldBe WorkBookCategory.ECONOMY.displayName + browseWorkBookDetail.description shouldBe "workbook${index + 1} description" + browseWorkBookDetail.writerDetails.size shouldBe 1 + browseWorkBookDetail.writerDetails[0].id shouldBe (index + 1).toLong() + browseWorkBookDetail.writerDetails[0].name shouldBe "writer${index + 1}" + browseWorkBookDetail.writerDetails[0].url shouldBe URL("http://localhost:8080/image/writer/${index + 1}") + browseWorkBookDetail.subscriptionCount shouldBe (index + 1).toLong() + } verify(exactly = 1) { workbookDao.browseWorkBookWithSubscriptionCount(any()) } verify(exactly = 1) { workbookMemberService.browseWorkbookWriterRecords(any()) } verify(exactly = 0) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } - verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any()) } - } - } - } - - given("로그인 된 상황에서 메인 뷰에서 카테고리를 지정하여 다수 워크북 조회 요청이 온 상황에서") { - val memberId = 1L - val useCaseIn = BrowseWorkbooksUseCaseIn(category = WorkBookCategory.ECONOMY, viewCategory = ViewCategory.MAIN_CARD, memberId = memberId) - - `when`("경제로 지정되어 있을 경우") { - every { workbookDao.browseWorkBookWithSubscriptionCount(any()) } returns IntStream.range(1, 3).mapToObj { - SelectWorkBookRecordWithSubscriptionCount( - id = it.toLong(), - title = "workbook title$it", - mainImageUrl = URL("http://localhost:8080/image/main/$it"), - category = CategoryType.ECONOMY.code, - description = "workbook$it description", - createdAt = LocalDateTime.now(), - subscriptionCount = it.toLong() - ) - }.toList() - - every { workbookMemberService.browseWorkbookWriterRecords(any()) } returns mapOf( - 1L to listOf( - WriterMappedWorkbookOutDto( - writerId = 1L, - name = "hunca", - url = URL("https://jh-labs.tistory.com/"), - workbookId = 1L - ) - ), - 2L to listOf( - WriterMappedWorkbookOutDto( - writerId = 2L, - name = "hunca", - url = URL("https://jh-labs.tistory.com/"), - workbookId = 2L - ) - ) - ) - - every { - workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) - } returns listOf( - BrowseMemberSubscribeWorkbooksOutDto( - workbookId = 1L, - isActiveSub = true, - currentDay = 1 - ), - BrowseMemberSubscribeWorkbooksOutDto( - workbookId = 2L, - isActiveSub = false, - currentDay = 1 - ) - ) - - every { - workbookOrderDelegatorExecutor.execute(any(AuthMainViewWorkbookOrderDelegator::class)) - } returns IntStream.range(1, 3).mapToObj { - BrowseWorkBookDetail( - id = it.toLong(), - title = "workbook title$it", - mainImageUrl = URL("http://localhost:8080/image/main/$it"), - category = WorkBookCategory.ECONOMY.displayName, - description = "workbook$it description", - createdAt = LocalDateTime.now(), - writerDetails = listOf( - WriterDetail( - id = it.toLong(), - name = "writer$it", - url = URL("https://jh-labs.tistory.com/") - ) - ), - subscriptionCount = it.toLong() - ) - }.toList() - - then("경제 카테고리의 워크북이 조회된다") { - useCase.execute(useCaseIn) - - verify(exactly = 1) { workbookDao.browseWorkBookWithSubscriptionCount(any()) } - verify(exactly = 1) { workbookMemberService.browseWorkbookWriterRecords(any()) } - verify(exactly = 1) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } - verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any(AuthMainViewWorkbookOrderDelegator::class)) } + verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any(BasicWorkbookOrderDelegator::class)) } } } } diff --git a/api/src/test/kotlin/com/few/api/domain/workbook/usecase/ReadWorkbookUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/workbook/usecase/ReadWorkbookUseCaseTest.kt index 9f74a8821..add8efdbc 100644 --- a/api/src/test/kotlin/com/few/api/domain/workbook/usecase/ReadWorkbookUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/workbook/usecase/ReadWorkbookUseCaseTest.kt @@ -7,8 +7,10 @@ import com.few.api.domain.workbook.service.dto.WriterOutDto import com.few.api.domain.workbook.usecase.dto.ReadWorkbookUseCaseIn import com.few.api.repo.dao.workbook.WorkbookDao import com.few.api.repo.dao.workbook.record.SelectWorkBookRecord +import com.few.data.common.code.CategoryType import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk import io.mockk.verify @@ -29,42 +31,61 @@ class ReadWorkbookUseCaseTest : BehaviorSpec({ useCase = ReadWorkbookUseCase(workbookDao, workbookArticleService, workbookMemberService) } - given("워크북 조회 요청이 온 상황에서") { + given("특정 워크북 조회 요청이 온 상황에서") { val workbookId = 1L val useCaseIn = ReadWorkbookUseCaseIn(workbookId = workbookId) - `when`("워크북과 작가가 존재할 경우") { + `when`("워크북이 존재할 경우") { + val workbookId = 1L + val title = "workbook title" + val workBookMainImageUrl = URL("http://localhost:8080/image/main/1") + val category = CategoryType.ECONOMY.code + val workbookDescription = "workbook description" every { workbookDao.selectWorkBook(any()) } returns SelectWorkBookRecord( - id = 1L, - title = "workbook title", - mainImageUrl = URL("https://jh-labs.tistory.com/"), - category = (10).toByte(), - description = "workbook description", + id = workbookId, + title = title, + mainImageUrl = workBookMainImageUrl, + category = category, + description = workbookDescription, createdAt = LocalDateTime.now() ) + val articleId = 1L + val articleWriterId = 1L + val articleMainImageUrl = URL("http://localhost:8080/image/main/1") + val articleTitle = "article title" + val articleContent = "article content" every { workbookArticleService.browseWorkbookArticles(any()) } returns listOf( WorkBookArticleOutDto( - articleId = 1L, - writerId = 1L, - mainImageURL = URL("https://jh-labs.tistory.com/"), - title = "article title", - category = (10).toByte(), - content = "article description", + articleId = articleId, + writerId = articleWriterId, + mainImageURL = articleMainImageUrl, + title = articleTitle, + category = category, + content = articleContent, createdAt = LocalDateTime.now() ) ) + val writerName = "writer" + val writerUrl = URL("http://localhost:8080/writer/1") every { workbookMemberService.browseWriterRecords(any()) } returns listOf( WriterOutDto( - writerId = 1L, - name = "hunca", - url = URL("https://jh-labs.tistory.com/") + writerId = articleWriterId, + name = writerName, + url = writerUrl ) ) - then("워크북 정상 조회된다") { - useCase.execute(useCaseIn) + then("워크북과 관련된 정보를 반환한다") { + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.id shouldBe workbookId + useCaseOut.title shouldBe title + useCaseOut.mainImageUrl shouldBe workBookMainImageUrl + useCaseOut.category shouldBe CategoryType.convertToDisplayName(category) + useCaseOut.description shouldBe workbookDescription + useCaseOut.writers.size shouldBe 1 + useCaseOut.articles.size shouldBe 1 verify(exactly = 1) { workbookDao.selectWorkBook(any()) } verify(exactly = 1) { workbookArticleService.browseWorkbookArticles(any()) } From 25544a41265c1688748a50189d8760866ab98b69 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 9 Aug 2024 17:55:39 +0900 Subject: [PATCH 5/8] =?UTF-8?q?refactor:=20workbook=20=EB=AA=A8=EB=8D=B8?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=97=AC=20=EC=A0=95=EB=A0=AC=20?= =?UTF-8?q?=EC=88=98=ED=96=89=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/BrowseWorkbooksUseCase.kt | 46 +++++++++++++++---- .../model/BasicWorkbookOrderDelegator.kt | 11 ----- .../usecase/model/MemberSubscribedWorkbook.kt | 7 +++ .../domain/workbook/usecase/model/WorkBook.kt | 21 +++++++++ .../usecase/model/WorkbookOrderDelegator.kt | 11 ----- .../service/WorkbookOrderDelegatorExecutor.kt | 13 ------ .../AuthMainViewWorkbookOrderDelegator.kt | 27 ++++++----- .../order/BasicWorkbookOrderDelegator.kt | 11 +++++ .../service/order/WorkbookOrderDelegator.kt | 11 +++++ .../order/WorkbookOrderDelegatorExecutor.kt | 12 +++++ .../usecase/BrowseWorkbooksUseCaseTest.kt | 46 +++++++++++++------ 11 files changed, 145 insertions(+), 71 deletions(-) delete mode 100644 api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/BasicWorkbookOrderDelegator.kt create mode 100644 api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/MemberSubscribedWorkbook.kt create mode 100644 api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/WorkBook.kt delete mode 100644 api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/WorkbookOrderDelegator.kt delete mode 100644 api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/WorkbookOrderDelegatorExecutor.kt rename api/src/main/kotlin/com/few/api/domain/workbook/usecase/{model => service/order}/AuthMainViewWorkbookOrderDelegator.kt (65%) create mode 100644 api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/BasicWorkbookOrderDelegator.kt create mode 100644 api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/WorkbookOrderDelegator.kt create mode 100644 api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/WorkbookOrderDelegatorExecutor.kt diff --git a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCase.kt b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCase.kt index 2bf5deb4e..61f10409f 100644 --- a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCase.kt @@ -8,9 +8,10 @@ import com.few.api.domain.workbook.usecase.dto.BrowseWorkBookDetail import com.few.api.domain.workbook.usecase.dto.BrowseWorkbooksUseCaseIn import com.few.api.domain.workbook.usecase.dto.BrowseWorkbooksUseCaseOut import com.few.api.domain.workbook.usecase.dto.WriterDetail -import com.few.api.domain.workbook.usecase.model.BasicWorkbookOrderDelegator -import com.few.api.domain.workbook.usecase.model.AuthMainViewWorkbookOrderDelegator -import com.few.api.domain.workbook.usecase.service.WorkbookOrderDelegatorExecutor +import com.few.api.domain.workbook.usecase.model.* +import com.few.api.domain.workbook.usecase.service.order.AuthMainViewWorkbookOrderDelegator +import com.few.api.domain.workbook.usecase.service.order.BasicWorkbookOrderDelegator +import com.few.api.domain.workbook.usecase.service.order.WorkbookOrderDelegatorExecutor import com.few.api.repo.dao.workbook.WorkbookDao import com.few.api.repo.dao.workbook.query.BrowseWorkBookQueryWithSubscriptionCount import com.few.api.web.support.ViewCategory @@ -56,7 +57,7 @@ class BrowseWorkbooksUseCase( } val workbookDetails = workbookRecords.map { record -> - BrowseWorkBookDetail( + WorkBook( id = record.id, mainImageUrl = record.mainImageUrl, title = record.title, @@ -64,7 +65,7 @@ class BrowseWorkbooksUseCase( category = CategoryType.convertToDisplayName(record.category), createdAt = record.createdAt, writerDetails = writerRecords[record.id]?.map { - WriterDetail( + WorkBookWriter( id = it.writerId, name = it.name, url = it.url @@ -84,8 +85,14 @@ class BrowseWorkbooksUseCase( WorkBookOrderStrategy.MAIN_VIEW_AUTH -> { BrowseMemberSubscribeWorkbooksInDto(useCaseIn.memberId!!).let { dto -> workbookSubscribeService.browseMemberSubscribeWorkbooks(dto) - }.let { memberSubscribeWorkbooks -> - AuthMainViewWorkbookOrderDelegator(workbookDetails, memberSubscribeWorkbooks) + }.map { + MemberSubscribedWorkbook( + workbookId = it.workbookId, + isActiveSub = it.isActiveSub, + currentDay = it.currentDay + ) + }.let { subscribedWorkbooks -> + AuthMainViewWorkbookOrderDelegator(workbookDetails, subscribedWorkbooks) } } WorkBookOrderStrategy.MAIN_VIEW_UNAUTH -> { @@ -96,8 +103,27 @@ class BrowseWorkbooksUseCase( workbookOrderDelegatorExecutor.execute(delegator) } - return BrowseWorkbooksUseCaseOut( - workbooks = orderedWorkbooks - ) + orderedWorkbooks.map { workBook -> + BrowseWorkBookDetail( + id = workBook.id, + mainImageUrl = workBook.mainImageUrl, + title = workBook.title, + description = workBook.description, + category = workBook.category, + createdAt = workBook.createdAt, + writerDetails = workBook.writerDetails.map { + WriterDetail( + id = it.id, + name = it.name, + url = it.url + ) + }, + subscriptionCount = workBook.subscriptionCount + ) + }.let { + return BrowseWorkbooksUseCaseOut( + workbooks = it + ) + } } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/BasicWorkbookOrderDelegator.kt b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/BasicWorkbookOrderDelegator.kt deleted file mode 100644 index 505e4aa1a..000000000 --- a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/BasicWorkbookOrderDelegator.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.few.api.domain.workbook.usecase.model - -import com.few.api.domain.workbook.usecase.dto.BrowseWorkBookDetail - -class BasicWorkbookOrderDelegator( - private val workbooks: List, -) : WorkbookOrderDelegator { - override fun order(): List { - return workbooks - } -} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/MemberSubscribedWorkbook.kt b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/MemberSubscribedWorkbook.kt new file mode 100644 index 000000000..23ec8ceb2 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/MemberSubscribedWorkbook.kt @@ -0,0 +1,7 @@ +package com.few.api.domain.workbook.usecase.model + +data class MemberSubscribedWorkbook( + val workbookId: Long, + val isActiveSub: Boolean, + val currentDay: Int, +) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/WorkBook.kt b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/WorkBook.kt new file mode 100644 index 000000000..a769073a0 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/WorkBook.kt @@ -0,0 +1,21 @@ +package com.few.api.domain.workbook.usecase.model + +import java.net.URL +import java.time.LocalDateTime + +data class WorkBook( + val id: Long, + val mainImageUrl: URL, + val title: String, + val description: String, + val category: String, + val createdAt: LocalDateTime, + val writerDetails: List, + val subscriptionCount: Long, +) + +data class WorkBookWriter( + val id: Long, + val name: String, + val url: URL, +) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/WorkbookOrderDelegator.kt b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/WorkbookOrderDelegator.kt deleted file mode 100644 index ffd458bc8..000000000 --- a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/WorkbookOrderDelegator.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.few.api.domain.workbook.usecase.model - -import com.few.api.domain.workbook.usecase.dto.BrowseWorkBookDetail - -interface WorkbookOrderDelegator { - - /** - * 워크북을 정렬합니다. - * */ - fun order(): List -} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/WorkbookOrderDelegatorExecutor.kt b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/WorkbookOrderDelegatorExecutor.kt deleted file mode 100644 index 050d5bbfb..000000000 --- a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/WorkbookOrderDelegatorExecutor.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.few.api.domain.workbook.usecase.service - -import com.few.api.domain.workbook.usecase.dto.BrowseWorkBookDetail -import com.few.api.domain.workbook.usecase.model.WorkbookOrderDelegator -import org.springframework.stereotype.Service - -@Service -class WorkbookOrderDelegatorExecutor { - - fun execute(delegator: WorkbookOrderDelegator): List { - return delegator.order() - } -} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/AuthMainViewWorkbookOrderDelegator.kt b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/AuthMainViewWorkbookOrderDelegator.kt similarity index 65% rename from api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/AuthMainViewWorkbookOrderDelegator.kt rename to api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/AuthMainViewWorkbookOrderDelegator.kt index 5608522e7..fe44f1f59 100644 --- a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/model/AuthMainViewWorkbookOrderDelegator.kt +++ b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/AuthMainViewWorkbookOrderDelegator.kt @@ -1,30 +1,33 @@ -package com.few.api.domain.workbook.usecase.model +package com.few.api.domain.workbook.usecase.service.order -import com.few.api.domain.workbook.service.dto.BrowseMemberSubscribeWorkbooksOutDto -import com.few.api.domain.workbook.usecase.dto.BrowseWorkBookDetail +import com.few.api.domain.workbook.usecase.model.MemberSubscribedWorkbook +import com.few.api.domain.workbook.usecase.model.WorkBook class AuthMainViewWorkbookOrderDelegator( /** * @see com.few.api.repo.dao.workbook.WorkbookDao.browseWorkBookWithSubscriptionCount */ - private val workbooks: List, - private val memberSubscribeWorkbooks: List, + private val workbooks: List, + private val memberSubscribedWorkbooks: List, ) : WorkbookOrderDelegator { /** * 메인 화면에 보여질 워크북을 정렬합니다. * 1. 활성화된 구독 워크북을 먼저 보여줍니다. + * - 구독 워크북 정렬 기준은 currentDay를 기준으로 내림차순입니다. * 2. 구독 기록이 없는 워크북을 보여줍니다. * 3. 비활성화된 구독 워크북을 보여줍니다. */ - override fun order(): List { + override fun order(): List { val allWorkbookIds = workbooks.associate { it.id to false }.toMutableMap() - val activeSubWorkbookIds = memberSubscribeWorkbooks.filter { it.isActiveSub }.sortedByDescending { - it.currentDay - }.map { it.workbookId } - val inActiveSubWorkbookIds = memberSubscribeWorkbooks.filter { !it.isActiveSub }.map { it.workbookId } + val activeSubWorkbookIds = + memberSubscribedWorkbooks.filter { it.isActiveSub }.sortedByDescending { + it.currentDay + }.map { it.workbookId } + val inActiveSubWorkbookIds = + memberSubscribedWorkbooks.filter { !it.isActiveSub }.map { it.workbookId } - val orderedWorkbooks = mutableListOf() + val orderedWorkbooks = mutableListOf() /** * 활성화된 구독 워크북을 먼저 보여줍니다. @@ -39,7 +42,7 @@ class AuthMainViewWorkbookOrderDelegator( /** * 비활성화된 구독 워크북을 모아둡니다. */ - val lastAddWorkbooks = mutableListOf() + val lastAddWorkbooks = mutableListOf() inActiveSubWorkbookIds.forEach { inActiveSubWorkbookId -> workbooks.find { it.id == inActiveSubWorkbookId }?.let { lastAddWorkbooks.add(it) diff --git a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/BasicWorkbookOrderDelegator.kt b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/BasicWorkbookOrderDelegator.kt new file mode 100644 index 000000000..daae07090 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/BasicWorkbookOrderDelegator.kt @@ -0,0 +1,11 @@ +package com.few.api.domain.workbook.usecase.service.order + +import com.few.api.domain.workbook.usecase.model.WorkBook + +class BasicWorkbookOrderDelegator( + private val workbooks: List, +) : WorkbookOrderDelegator { + override fun order(): List { + return workbooks + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/WorkbookOrderDelegator.kt b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/WorkbookOrderDelegator.kt new file mode 100644 index 000000000..1da8614c7 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/WorkbookOrderDelegator.kt @@ -0,0 +1,11 @@ +package com.few.api.domain.workbook.usecase.service.order + +import com.few.api.domain.workbook.usecase.model.WorkBook + +interface WorkbookOrderDelegator { + + /** + * 워크북을 정렬합니다. + * */ + fun order(): List +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/WorkbookOrderDelegatorExecutor.kt b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/WorkbookOrderDelegatorExecutor.kt new file mode 100644 index 000000000..cf37cd2e2 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/service/order/WorkbookOrderDelegatorExecutor.kt @@ -0,0 +1,12 @@ +package com.few.api.domain.workbook.usecase.service.order + +import com.few.api.domain.workbook.usecase.model.WorkBook +import org.springframework.stereotype.Service + +@Service +class WorkbookOrderDelegatorExecutor { + + fun execute(delegator: WorkbookOrderDelegator): List { + return delegator.order() + } +} \ No newline at end of file diff --git a/api/src/test/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCaseTest.kt index 7735fdb62..bef68a2e2 100644 --- a/api/src/test/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCaseTest.kt @@ -4,12 +4,12 @@ import com.few.api.domain.workbook.service.WorkbookMemberService import com.few.api.domain.workbook.service.WorkbookSubscribeService import com.few.api.domain.workbook.service.dto.BrowseMemberSubscribeWorkbooksOutDto import com.few.api.domain.workbook.service.dto.WriterMappedWorkbookOutDto -import com.few.api.domain.workbook.usecase.dto.BrowseWorkBookDetail import com.few.api.domain.workbook.usecase.dto.BrowseWorkbooksUseCaseIn -import com.few.api.domain.workbook.usecase.dto.WriterDetail -import com.few.api.domain.workbook.usecase.model.AuthMainViewWorkbookOrderDelegator -import com.few.api.domain.workbook.usecase.model.BasicWorkbookOrderDelegator -import com.few.api.domain.workbook.usecase.service.WorkbookOrderDelegatorExecutor +import com.few.api.domain.workbook.usecase.model.WorkBook +import com.few.api.domain.workbook.usecase.model.WorkBookWriter +import com.few.api.domain.workbook.usecase.service.order.AuthMainViewWorkbookOrderDelegator +import com.few.api.domain.workbook.usecase.service.order.BasicWorkbookOrderDelegator +import com.few.api.domain.workbook.usecase.service.order.WorkbookOrderDelegatorExecutor import com.few.api.repo.dao.workbook.WorkbookDao import com.few.api.repo.dao.workbook.record.SelectWorkBookRecordWithSubscriptionCount import com.few.api.web.support.ViewCategory @@ -96,7 +96,7 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ every { workbookOrderDelegatorExecutor.execute(any(AuthMainViewWorkbookOrderDelegator::class)) } returns IntStream.range(1, 1 + workbookCount).mapToObj { - BrowseWorkBookDetail( + WorkBook( id = it.toLong(), title = "workbook title$it", mainImageUrl = URL("http://localhost:8080/image/main/$it"), @@ -104,7 +104,7 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ description = "workbook$it description", createdAt = LocalDateTime.now(), writerDetails = listOf( - WriterDetail( + WorkBookWriter( id = it.toLong(), name = "writer$it", url = URL("https://jh-labs.tistory.com/") @@ -134,7 +134,13 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ verify(exactly = 1) { workbookDao.browseWorkBookWithSubscriptionCount(any()) } verify(exactly = 1) { workbookMemberService.browseWorkbookWriterRecords(any()) } verify(exactly = 1) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } - verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any(AuthMainViewWorkbookOrderDelegator::class)) } + verify(exactly = 1) { + workbookOrderDelegatorExecutor.execute( + any( + AuthMainViewWorkbookOrderDelegator::class + ) + ) + } } } } @@ -174,7 +180,7 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ workbookOrderDelegatorExecutor.execute(any(BasicWorkbookOrderDelegator::class)) } returns IntStream.range(1, 1 + workbookCount).mapToObj { - BrowseWorkBookDetail( + WorkBook( id = it.toLong(), title = "workbook title$it", mainImageUrl = URL("http://localhost:8080/image/main/$it"), @@ -182,7 +188,7 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ description = "workbook$it description", createdAt = LocalDateTime.now(), writerDetails = listOf( - WriterDetail( + WorkBookWriter( id = it.toLong(), name = "writer$it", url = URL("http://localhost:8080/image/writer/$it") @@ -212,7 +218,13 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ verify(exactly = 1) { workbookDao.browseWorkBookWithSubscriptionCount(any()) } verify(exactly = 1) { workbookMemberService.browseWorkbookWriterRecords(any()) } verify(exactly = 0) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } - verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any(BasicWorkbookOrderDelegator::class)) } + verify(exactly = 1) { + workbookOrderDelegatorExecutor.execute( + any( + BasicWorkbookOrderDelegator::class + ) + ) + } } } } @@ -259,7 +271,7 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ every { workbookOrderDelegatorExecutor.execute(any(BasicWorkbookOrderDelegator::class)) } returns IntStream.range(1, 1 + workbookCount).mapToObj { - BrowseWorkBookDetail( + WorkBook( id = it.toLong(), title = "workbook title$it", mainImageUrl = URL("http://localhost:8080/image/main/$it"), @@ -267,7 +279,7 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ description = "workbook$it description", createdAt = LocalDateTime.now(), writerDetails = listOf( - WriterDetail( + WorkBookWriter( id = it.toLong(), name = "writer$it", url = URL("http://localhost:8080/image/writer/$it") @@ -296,7 +308,13 @@ class BrowseWorkbooksUseCaseTest : BehaviorSpec({ verify(exactly = 1) { workbookDao.browseWorkBookWithSubscriptionCount(any()) } verify(exactly = 1) { workbookMemberService.browseWorkbookWriterRecords(any()) } verify(exactly = 0) { workbookSubscribeService.browseMemberSubscribeWorkbooks(any()) } - verify(exactly = 1) { workbookOrderDelegatorExecutor.execute(any(BasicWorkbookOrderDelegator::class)) } + verify(exactly = 1) { + workbookOrderDelegatorExecutor.execute( + any( + BasicWorkbookOrderDelegator::class + ) + ) + } } } } From 474748fc3f0df0e436ea762c867ed0dd477e0021 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 9 Aug 2024 17:56:11 +0900 Subject: [PATCH 6/8] =?UTF-8?q?test:=20AuthMainViewWorkbookOrderDelegatorT?= =?UTF-8?q?est=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthMainViewWorkbookOrderDelegatorTest.kt | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 api/src/test/kotlin/com/few/api/domain/workbook/usecase/service/order/AuthMainViewWorkbookOrderDelegatorTest.kt diff --git a/api/src/test/kotlin/com/few/api/domain/workbook/usecase/service/order/AuthMainViewWorkbookOrderDelegatorTest.kt b/api/src/test/kotlin/com/few/api/domain/workbook/usecase/service/order/AuthMainViewWorkbookOrderDelegatorTest.kt new file mode 100644 index 000000000..e1402a943 --- /dev/null +++ b/api/src/test/kotlin/com/few/api/domain/workbook/usecase/service/order/AuthMainViewWorkbookOrderDelegatorTest.kt @@ -0,0 +1,191 @@ +package com.few.api.domain.workbook.usecase.service.order + +import com.few.api.domain.workbook.usecase.model.MemberSubscribedWorkbook +import com.few.api.domain.workbook.usecase.model.WorkBook +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import java.net.URL +import java.time.LocalDateTime +import java.util.stream.IntStream +import kotlin.streams.toList + +class AuthMainViewWorkbookOrderDelegatorTest { + + @Test + fun `워크북과 멤버 구독 워크북이 모두 주어지는 경우`() { + // given + val totalWorkbookCount = 10 + val workbooksOrderBySubscriptionCount = IntStream.range(1, 1 + totalWorkbookCount) + .mapToObj { + WorkBook( + it.toLong(), + URL("http://localhost:8080/$it"), + "title$it", + "description$it", + "category$it", + LocalDateTime.now(), + emptyList(), + (1 + totalWorkbookCount) - it.toLong() + ) + } + .toList() + + /** + * 1 : inactive, current day 5 + * 2 : active, current day 4 + * 3 : inactive, current day 3 + * 4 : active, current day 2 + * 5 : inactive, current day 1 + */ + val activeWorkbookIds = listOf(2, 4) + val inActiveList = listOf(1, 3, 5) + val totalMemberSubscribedWorkbookCount = 5 + val memberSubscribedWorkbooksReverseOrderByCurrentDay = IntStream.range(1, 1 + totalMemberSubscribedWorkbookCount) + .mapToObj { + MemberSubscribedWorkbook( + it.toLong(), + it % 2 == 0, + (1 + totalMemberSubscribedWorkbookCount) - it + ) + } + .toList() + + val notSubscribeWorkbookIds = workbooksOrderBySubscriptionCount.filter { !activeWorkbookIds.contains(it.id.toInt()) && !inActiveList.contains(it.id.toInt()) } + .map { it.id.toInt() } + + // when + val delegator = AuthMainViewWorkbookOrderDelegator( + workbooksOrderBySubscriptionCount, + memberSubscribedWorkbooksReverseOrderByCurrentDay + ) + + // then + val orderedWorkbooks = delegator.order() + assertEquals(totalWorkbookCount, orderedWorkbooks.size) + + val expectedOrderedWorkbookIds = activeWorkbookIds + notSubscribeWorkbookIds + inActiveList + for (i in expectedOrderedWorkbookIds.indices) { + assertEquals(expectedOrderedWorkbookIds[i].toLong(), orderedWorkbooks[i].id) + } + } + + @Test + fun `워크북과 멤버 구독 워크북만 주어지는 경우`() { + // given + val totalWorkbookCount = 10 + val workbooksOrderBySubscriptionCount = IntStream.range(1, 1 + totalWorkbookCount) + .mapToObj { + WorkBook( + it.toLong(), + URL("http://localhost:8080/$it"), + "title$it", + "description$it", + "category$it", + LocalDateTime.now(), + emptyList(), + (1 + totalWorkbookCount) - it.toLong() + ) + } + .toList() + + /** + * 1 : active, current day 5 + * 2 : active, current day 4 + * 3 : active, current day 3 + * 4 : active, current day 2 + * 5 : active, current day 1 + */ + val totalMemberSubscribedWorkbookCount = 5 + val activeWorkbookIds = IntStream.range(1, 1 + totalMemberSubscribedWorkbookCount).toList() + val memberSubscribedWorkbooksReverseOrderByCurrentDay = IntStream.range(1, 1 + totalMemberSubscribedWorkbookCount) + .mapToObj { + MemberSubscribedWorkbook( + it.toLong(), + true, + (1 + totalMemberSubscribedWorkbookCount) - it + ) + } + .toList() + + val notSubscribeWorkbookIds = workbooksOrderBySubscriptionCount.filter { + !activeWorkbookIds.contains( + it.id.toInt() + ) + }.map { it.id.toInt() } + + // when + val delegator = AuthMainViewWorkbookOrderDelegator( + workbooksOrderBySubscriptionCount, + memberSubscribedWorkbooksReverseOrderByCurrentDay + ) + + // then + val orderedWorkbooks = delegator.order() + assertEquals(totalWorkbookCount, orderedWorkbooks.size) + + val expectedOrderedWorkbookIds = activeWorkbookIds + notSubscribeWorkbookIds + for (i in expectedOrderedWorkbookIds.indices) { + assertEquals(expectedOrderedWorkbookIds[i].toLong(), orderedWorkbooks[i].id) + } + } + + @Test + fun `워크북과 멤버 구독 완료 워크북만 주어지는 경우`() { + // given + val totalWorkbookCount = 10 + val workbooksOrderBySubscriptionCount = IntStream.range(1, 1 + totalWorkbookCount) + .mapToObj { + WorkBook( + it.toLong(), + URL("http://localhost:8080/$it"), + "title$it", + "description$it", + "category$it", + LocalDateTime.now(), + emptyList(), + (1 + totalWorkbookCount) - it.toLong() + ) + } + .toList() + + /** + * 1 : inactive, current day 5 + * 2 : inactive, current day 4 + * 3 : inactive, current day 3 + * 4 : inactive, current day 2 + * 5 : inactive, current day 1 + */ + val totalMemberSubscribedWorkbookCount = 5 + val inActiveWorkbookIds = IntStream.range(1, 1 + totalMemberSubscribedWorkbookCount).toList() + val memberSubscribedWorkbooksReverseOrderByCurrentDay = IntStream.range(1, 1 + totalMemberSubscribedWorkbookCount) + .mapToObj { + MemberSubscribedWorkbook( + it.toLong(), + false, + (1 + totalMemberSubscribedWorkbookCount) - it + ) + } + .toList() + + val notSubscribeWorkbookIds = workbooksOrderBySubscriptionCount.filter { + !inActiveWorkbookIds.contains( + it.id.toInt() + ) + }.map { it.id.toInt() } + + // when + val delegator = AuthMainViewWorkbookOrderDelegator( + workbooksOrderBySubscriptionCount, + memberSubscribedWorkbooksReverseOrderByCurrentDay + ) + + // then + val orderedWorkbooks = delegator.order() + assertEquals(totalWorkbookCount, orderedWorkbooks.size) + + val expectedOrderedWorkbookIds = notSubscribeWorkbookIds + inActiveWorkbookIds + for (i in expectedOrderedWorkbookIds.indices) { + assertEquals(expectedOrderedWorkbookIds[i].toLong(), orderedWorkbooks[i].id) + } + } +} \ No newline at end of file From 2937be3db497e1854b2d4132b78f491917361677 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 9 Aug 2024 19:11:39 +0900 Subject: [PATCH 7/8] =?UTF-8?q?refactor:=20=EB=88=84=EB=9D=BD=EB=90=9C=20e?= =?UTF-8?q?xplain=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repo/dao/article/ArticleMainCardDao.kt | 10 +- .../repo/dao/subscription/SubscriptionDao.kt | 12 +- .../few/api/repo/dao/workbook/WorkbookDao.kt | 7 +- .../article/ArticleDaoExplainGenerateTest.kt | 11 ++ .../ArticleMainCardDaoExplainGenerateTest.kt | 110 ++++++++++++++++++ .../ArticleViewCountDaoExplainGenerateTest.kt | 39 +++++++ .../member/MemberDaoExplainGenerateTest.kt | 11 +- .../SubscriptionDaoExplainGenerateTest.kt | 30 +++++ .../WorkbookDaoExplainGenerateTest.kt | 16 +++ 9 files changed, 231 insertions(+), 15 deletions(-) create mode 100644 api-repo/src/test/kotlin/com/few/api/repo/explain/article/ArticleMainCardDaoExplainGenerateTest.kt diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleMainCardDao.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleMainCardDao.kt index 8ec2dcef4..7dd1511f8 100644 --- a/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleMainCardDao.kt +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleMainCardDao.kt @@ -23,7 +23,7 @@ class ArticleMainCardDao( .toSet() } - private fun selectArticleMainCardsRecordQuery(articleIds: Set) = dslContext.select( + fun selectArticleMainCardsRecordQuery(articleIds: Set) = dslContext.select( ARTICLE_MAIN_CARD.ID.`as`(ArticleMainCardRecord::articleId.name), ARTICLE_MAIN_CARD.TITLE.`as`(ArticleMainCardRecord::articleTitle.name), ARTICLE_MAIN_CARD.MAIN_IMAGE_URL.`as`(ArticleMainCardRecord::mainImageUrl.name), @@ -46,9 +46,9 @@ class ArticleMainCardDao( * NOTE - The query performed in this function do not save the workbook. */ fun insertArticleMainCard(command: ArticleMainCardExcludeWorkbookCommand) = - insertArticleMainCardQuery(command).execute() + insertArticleMainCardCommand(command).execute() - fun insertArticleMainCardQuery(command: ArticleMainCardExcludeWorkbookCommand) = dslContext + fun insertArticleMainCardCommand(command: ArticleMainCardExcludeWorkbookCommand) = dslContext .insertInto( ARTICLE_MAIN_CARD, ARTICLE_MAIN_CARD.ID, @@ -79,9 +79,9 @@ class ArticleMainCardDao( ) fun updateArticleMainCardSetWorkbook(command: UpdateArticleMainCardWorkbookCommand) = - updateArticleMainCardSetWorkbookQuery(command).execute() + updateArticleMainCardSetWorkbookCommand(command).execute() - fun updateArticleMainCardSetWorkbookQuery(command: UpdateArticleMainCardWorkbookCommand) = dslContext + fun updateArticleMainCardSetWorkbookCommand(command: UpdateArticleMainCardWorkbookCommand) = dslContext .update(ARTICLE_MAIN_CARD) .set(ARTICLE_MAIN_CARD.WORKBOOKS, JSON.valueOf(articleMainCardMapper.toJsonStr(command.workbooks))) .where(ARTICLE_MAIN_CARD.ID.eq(command.articleId)) diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/SubscriptionDao.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/SubscriptionDao.kt index 4ef9984b0..e4ddd84d1 100644 --- a/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/SubscriptionDao.kt +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/SubscriptionDao.kt @@ -32,22 +32,28 @@ class SubscriptionDao( .set(SUBSCRIPTION.TARGET_WORKBOOK_ID, command.workbookId) fun reSubscribeWorkbookSubscription(command: InsertWorkbookSubscriptionCommand) { + reSubscribeWorkBookSubscriptionCommand(command) + .execute() + } + + fun reSubscribeWorkBookSubscriptionCommand(command: InsertWorkbookSubscriptionCommand) = dslContext.update(SUBSCRIPTION) .set(SUBSCRIPTION.DELETED_AT, null as LocalDateTime?) .set(SUBSCRIPTION.UNSUBS_OPINION, null as String?) .where(SUBSCRIPTION.MEMBER_ID.eq(command.memberId)) .and(SUBSCRIPTION.TARGET_WORKBOOK_ID.eq(command.workbookId)) + + fun updateDeletedAtInWorkbookSubscription(command: UpdateDeletedAtInWorkbookSubscriptionCommand) { + updateDeletedAtInWorkbookSubscriptionCommand(command) .execute() } - fun updateDeletedAtInWorkbookSubscription(command: UpdateDeletedAtInWorkbookSubscriptionCommand) { + fun updateDeletedAtInWorkbookSubscriptionCommand(command: UpdateDeletedAtInWorkbookSubscriptionCommand) = dslContext.update(SUBSCRIPTION) .set(SUBSCRIPTION.DELETED_AT, LocalDateTime.now()) .set(SUBSCRIPTION.UNSUBS_OPINION, command.opinion) .where(SUBSCRIPTION.MEMBER_ID.eq(command.memberId)) .and(SUBSCRIPTION.TARGET_WORKBOOK_ID.eq(command.workbookId)) - .execute() - } fun selectTopWorkbookSubscriptionStatus(query: SelectAllWorkbookSubscriptionStatusNotConsiderDeletedAtQuery): WorkbookSubscriptionStatus? { return selectTopWorkbookSubscriptionStatusQuery(query) diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/WorkbookDao.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/WorkbookDao.kt index ace5b2e89..16462a44f 100644 --- a/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/WorkbookDao.kt +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/WorkbookDao.kt @@ -56,12 +56,15 @@ class WorkbookDao( .set(Workbook.WORKBOOK.DESCRIPTION, command.description) fun mapWorkBookToArticle(command: MapWorkBookToArticleCommand) { + mapWorkBookToArticleCommand(command) + .execute() + } + + fun mapWorkBookToArticleCommand(command: MapWorkBookToArticleCommand) = dslContext.insertInto(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE) .set(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE.WORKBOOK_ID, command.workbookId) .set(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE.ARTICLE_ID, command.articleId) .set(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE.DAY_COL, command.dayCol) - .execute() - } /** * category에 따라서 조회된 구독자 수가 포함된 Workbook 목록을 반환한다. diff --git a/api-repo/src/test/kotlin/com/few/api/repo/explain/article/ArticleDaoExplainGenerateTest.kt b/api-repo/src/test/kotlin/com/few/api/repo/explain/article/ArticleDaoExplainGenerateTest.kt index 6b52e402e..7c7d2b88a 100644 --- a/api-repo/src/test/kotlin/com/few/api/repo/explain/article/ArticleDaoExplainGenerateTest.kt +++ b/api-repo/src/test/kotlin/com/few/api/repo/explain/article/ArticleDaoExplainGenerateTest.kt @@ -134,4 +134,15 @@ class ArticleDaoExplainGenerateTest : JooqTestSpec() { ResultGenerator.execute(query, explain, "selectArticleIdByWorkbookIdAndDayQueryExplain") } + + @Test + fun selectArticleContentsQueryExplain() { + val query = setOf(1L).let { + articleDao.selectArticleContentsQuery(it) + } + + val explain = dslContext.explain(query).toString() + + ResultGenerator.execute(query, explain, "selectArticleContentsQueryExplain") + } } \ No newline at end of file diff --git a/api-repo/src/test/kotlin/com/few/api/repo/explain/article/ArticleMainCardDaoExplainGenerateTest.kt b/api-repo/src/test/kotlin/com/few/api/repo/explain/article/ArticleMainCardDaoExplainGenerateTest.kt new file mode 100644 index 000000000..de27d1285 --- /dev/null +++ b/api-repo/src/test/kotlin/com/few/api/repo/explain/article/ArticleMainCardDaoExplainGenerateTest.kt @@ -0,0 +1,110 @@ +package com.few.api.repo.explain.article + +import com.few.api.repo.dao.article.ArticleMainCardDao +import com.few.api.repo.dao.article.command.ArticleMainCardExcludeWorkbookCommand +import com.few.api.repo.dao.article.command.UpdateArticleMainCardWorkbookCommand +import com.few.api.repo.dao.article.command.WorkbookCommand +import com.few.api.repo.explain.ResultGenerator +import com.few.api.repo.jooq.JooqTestSpec +import com.few.data.common.code.CategoryType +import io.github.oshai.kotlinlogging.KotlinLogging +import jooq.jooq_dsl.tables.ArticleMainCard +import org.jooq.DSLContext +import org.jooq.JSON +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Tag +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import java.net.URL +import java.time.LocalDateTime + +@Tag("explain") +class ArticleMainCardDaoExplainGenerateTest : JooqTestSpec() { + private val log = KotlinLogging.logger {} + + @Autowired + private lateinit var dslContext: DSLContext + + @Autowired + private lateinit var articleMainCardDao: ArticleMainCardDao + + @BeforeEach + fun setUp() { + log.debug { "===== start setUp =====" } + dslContext.deleteFrom(ArticleMainCard.ARTICLE_MAIN_CARD).execute() + dslContext.insertInto(ArticleMainCard.ARTICLE_MAIN_CARD) + .set(ArticleMainCard.ARTICLE_MAIN_CARD.ID, 1L) + .set(ArticleMainCard.ARTICLE_MAIN_CARD.TITLE, "this is title1") + .set( + ArticleMainCard.ARTICLE_MAIN_CARD.MAIN_IMAGE_URL, + "http://localhost:8080/image1.jpg" + ) + .set(ArticleMainCard.ARTICLE_MAIN_CARD.CATEGORY_CD, CategoryType.fromCode(0)!!.code) + .set(ArticleMainCard.ARTICLE_MAIN_CARD.CREATED_AT, LocalDateTime.now()) + .set(ArticleMainCard.ARTICLE_MAIN_CARD.WRITER_ID, 1L) + .set(ArticleMainCard.ARTICLE_MAIN_CARD.WRITER_EMAIL, "writer@gmail.com") + .set( + ArticleMainCard.ARTICLE_MAIN_CARD.WRITER_DESCRIPTION, + JSON.valueOf("{ \"name\": \"writer\", \"url\": \"http://localhost:8080/writer\", \"imgUrl\": \"http://localhost:8080/writer.jpg\" }") + ) + .set( + ArticleMainCard.ARTICLE_MAIN_CARD.WORKBOOKS, + JSON.valueOf("[{\"id\": 1, \"title\": \"title\"}]") + ) + .execute() + log.debug { "===== finish setUp =====" } + } + + @Test + fun selectArticleMainCardsRecordQueryExplain() { + val query = articleMainCardDao.selectArticleMainCardsRecordQuery(setOf(1L)) + + val explain = dslContext.explain(query).toString() + ResultGenerator.execute(query, explain, "selectArticleMainCardsRecordQueryExplain") + } + + @Test + fun insertArticleMainCardCommandExplain() { + val command = ArticleMainCardExcludeWorkbookCommand( + articleId = 2L, + articleTitle = "this is title2", + mainImageUrl = URL("http://localhost:8080/image2.jpg"), + categoryCd = CategoryType.fromCode(0)!!.code, + createdAt = LocalDateTime.now(), + writerId = 1L, + writerEmail = "writer@gmail.com", + writerName = "writer", + writerUrl = URL("http://localhost:8080/writer"), + writerImgUrl = URL("http://localhost:8080/writer.jpg") + ).let { + articleMainCardDao.insertArticleMainCardCommand(it) + } + + val explain = command.toString() + + ResultGenerator.execute(command, explain, "insertArticleMainCardCommandExplain") + } + + @Test + fun updateArticleMainCardSetWorkbookCommandExplain() { + val command = UpdateArticleMainCardWorkbookCommand( + articleId = 1L, + workbooks = listOf( + WorkbookCommand( + id = 1L, + title = "workbook1" + ), + WorkbookCommand( + id = 2L, + title = "workbook2" + ) + ) + ).let { + articleMainCardDao.updateArticleMainCardSetWorkbookCommand(it) + } + + val explain = command.toString() + + ResultGenerator.execute(command, explain, "updateArticleMainCardSetWorkbookCommandExplain") + } +} \ No newline at end of file diff --git a/api-repo/src/test/kotlin/com/few/api/repo/explain/article/ArticleViewCountDaoExplainGenerateTest.kt b/api-repo/src/test/kotlin/com/few/api/repo/explain/article/ArticleViewCountDaoExplainGenerateTest.kt index 81ee73653..2902167e1 100644 --- a/api-repo/src/test/kotlin/com/few/api/repo/explain/article/ArticleViewCountDaoExplainGenerateTest.kt +++ b/api-repo/src/test/kotlin/com/few/api/repo/explain/article/ArticleViewCountDaoExplainGenerateTest.kt @@ -3,6 +3,8 @@ package com.few.api.repo.explain.article import com.few.api.repo.dao.article.ArticleViewCountDao import com.few.api.repo.dao.article.command.ArticleViewCountCommand import com.few.api.repo.dao.article.query.ArticleViewCountQuery +import com.few.api.repo.dao.article.query.SelectArticlesOrderByViewsQuery +import com.few.api.repo.dao.article.query.SelectRankByViewsQuery import com.few.api.repo.explain.InsertUpdateExplainGenerator import com.few.api.repo.explain.ResultGenerator import com.few.api.repo.jooq.JooqTestSpec @@ -61,4 +63,41 @@ class ArticleViewCountDaoExplainGenerateTest : JooqTestSpec() { ResultGenerator.execute(command, explain, "upsertArticleViewCountQueryExplain") } + + @Test + fun insertArticleViewCountToZeroQueryExplain() { + val command = ArticleViewCountQuery( + articleId = 1L, + categoryType = CategoryType.fromCode(0)!! + ).let { + articleViewCountDao.insertArticleViewCountToZeroQuery(it) + } + + val explain = InsertUpdateExplainGenerator.execute(dslContext, command.sql, command.bindValues) + + ResultGenerator.execute(command, explain, "insertArticleViewCountToZeroQueryExplain") + } + + @Test + fun selectRankByViewsQueryExplain() { + val query = SelectRankByViewsQuery(1L).let { + articleViewCountDao.selectRankByViewsQuery(it) + } + + val explain = dslContext.explain(query).toString() + ResultGenerator.execute(query, explain, "selectRankByViewsQueryExplain") + } + + @Test + fun selectArticlesOrderByViewsQueryExplain() { + val query = SelectArticlesOrderByViewsQuery( + offset = 0, + category = CategoryType.fromCode(0)!! + ).let { + articleViewCountDao.selectArticlesOrderByViewsQuery(it) + } + + val explain = dslContext.explain(query).toString() + ResultGenerator.execute(query, explain, "selectArticlesOrderByViewsQueryExplain") + } } \ No newline at end of file diff --git a/api-repo/src/test/kotlin/com/few/api/repo/explain/member/MemberDaoExplainGenerateTest.kt b/api-repo/src/test/kotlin/com/few/api/repo/explain/member/MemberDaoExplainGenerateTest.kt index 1bed29ec0..3f85b76b5 100644 --- a/api-repo/src/test/kotlin/com/few/api/repo/explain/member/MemberDaoExplainGenerateTest.kt +++ b/api-repo/src/test/kotlin/com/few/api/repo/explain/member/MemberDaoExplainGenerateTest.kt @@ -1,10 +1,12 @@ package com.few.api.repo.explain.member import com.few.api.repo.dao.member.MemberDao +import com.few.api.repo.dao.member.command.DeleteMemberCommand import com.few.api.repo.dao.member.command.InsertMemberCommand import com.few.api.repo.dao.member.command.UpdateDeletedMemberTypeCommand import com.few.api.repo.dao.member.command.UpdateMemberTypeCommand import com.few.api.repo.dao.member.query.BrowseWorkbookWritersQuery +import com.few.api.repo.dao.member.query.SelectMemberByEmailNotConsiderDeletedAtQuery import com.few.api.repo.dao.member.query.SelectMemberByEmailQuery import com.few.api.repo.dao.member.query.SelectWriterQuery import com.few.api.repo.dao.member.support.WriterDescription @@ -117,7 +119,7 @@ class MemberDaoExplainGenerateTest : JooqTestSpec() { @Test fun selectMemberByEmailNotConsiderDeletedAtQueryExplain() { - val query = SelectMemberByEmailQuery("test@gmail.com").let { + val query = SelectMemberByEmailNotConsiderDeletedAtQuery("test@gmail.com").let { memberDao.selectMemberByEmailQuery(it) } @@ -181,11 +183,10 @@ class MemberDaoExplainGenerateTest : JooqTestSpec() { @Test fun deleteMemberCommandExplain() { - val command = UpdateDeletedMemberTypeCommand( - id = 1, - memberType = MemberType.WRITER + val command = DeleteMemberCommand( + memberId = 1 ).let { - memberDao.updateMemberTypeCommand(it) + memberDao.deleteMemberCommand(it) } val explain = InsertUpdateExplainGenerator.execute(dslContext, command.sql, command.bindValues) diff --git a/api-repo/src/test/kotlin/com/few/api/repo/explain/subscription/SubscriptionDaoExplainGenerateTest.kt b/api-repo/src/test/kotlin/com/few/api/repo/explain/subscription/SubscriptionDaoExplainGenerateTest.kt index 08166d265..6bfb9a271 100644 --- a/api-repo/src/test/kotlin/com/few/api/repo/explain/subscription/SubscriptionDaoExplainGenerateTest.kt +++ b/api-repo/src/test/kotlin/com/few/api/repo/explain/subscription/SubscriptionDaoExplainGenerateTest.kt @@ -3,6 +3,7 @@ package com.few.api.repo.explain.subscription import com.few.api.repo.dao.subscription.SubscriptionDao import com.few.api.repo.dao.subscription.command.InsertWorkbookSubscriptionCommand import com.few.api.repo.dao.subscription.command.UpdateDeletedAtInAllSubscriptionCommand +import com.few.api.repo.dao.subscription.command.UpdateDeletedAtInWorkbookSubscriptionCommand import com.few.api.repo.dao.subscription.query.CountWorkbookMappedArticlesQuery import com.few.api.repo.dao.subscription.query.SelectAllMemberWorkbookActiveSubscription import com.few.api.repo.dao.subscription.query.SelectAllWorkbookSubscriptionStatusNotConsiderDeletedAtQuery @@ -137,4 +138,33 @@ class SubscriptionDaoExplainGenerateTest : JooqTestSpec() { ResultGenerator.execute(query, explain, "countAllWorkbookSubscriptionQueryExplain") } + + @Test + fun reSubscribeWorkBookSubscriptionCommandExplain() { + val command = InsertWorkbookSubscriptionCommand( + memberId = 1L, + workbookId = 1L + ).let { + subscriptionDao.reSubscribeWorkBookSubscriptionCommand(it) + } + + val explain = InsertUpdateExplainGenerator.execute(dslContext, command.sql, command.bindValues) + + ResultGenerator.execute(command, explain, "reSubscribeWorkBookSubscriptionCommandExplain") + } + + @Test + fun updateDeletedAtInWorkbookSubscriptionCommandExplain() { + val command = UpdateDeletedAtInWorkbookSubscriptionCommand( + memberId = 1L, + workbookId = 1L, + opinion = "test" + ).let { + subscriptionDao.updateDeletedAtInWorkbookSubscriptionCommand(it) + } + + val explain = InsertUpdateExplainGenerator.execute(dslContext, command.sql, command.bindValues) + + ResultGenerator.execute(command, explain, "updateDeletedAtInWorkbookSubscriptionCommandExplain") + } } \ No newline at end of file diff --git a/api-repo/src/test/kotlin/com/few/api/repo/explain/workbook/WorkbookDaoExplainGenerateTest.kt b/api-repo/src/test/kotlin/com/few/api/repo/explain/workbook/WorkbookDaoExplainGenerateTest.kt index 536839f45..f736d1c69 100644 --- a/api-repo/src/test/kotlin/com/few/api/repo/explain/workbook/WorkbookDaoExplainGenerateTest.kt +++ b/api-repo/src/test/kotlin/com/few/api/repo/explain/workbook/WorkbookDaoExplainGenerateTest.kt @@ -2,6 +2,7 @@ package com.few.api.repo.explain.workbook import com.few.api.repo.dao.workbook.WorkbookDao import com.few.api.repo.dao.workbook.command.InsertWorkBookCommand +import com.few.api.repo.dao.workbook.command.MapWorkBookToArticleCommand import com.few.api.repo.dao.workbook.query.BrowseWorkBookQueryWithSubscriptionCount import com.few.api.repo.dao.workbook.query.SelectWorkBookRecordQuery import com.few.api.repo.explain.InsertUpdateExplainGenerator @@ -89,4 +90,19 @@ class WorkbookDaoExplainGenerateTest : JooqTestSpec() { ResultGenerator.execute(query, explain, "browseWorkBookQueryCategoryCondition") } + + @Test + fun mapWorkBookToArticleCommandExplain() { + val command = MapWorkBookToArticleCommand( + workbookId = 1L, + articleId = 1L, + dayCol = 1 + ).let { + workbookDao.mapWorkBookToArticleCommand(it) + } + + val explain = InsertUpdateExplainGenerator.execute(dslContext, command.sql, command.bindValues) + + ResultGenerator.execute(command, explain, "mapWorkBookToArticleCommandExplain") + } } \ No newline at end of file From ae265fb1efac3af3cb1d6634ba7f0ee11c5ca43a Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Sun, 11 Aug 2024 23:40:13 +0900 Subject: [PATCH 8/8] =?UTF-8?q?chore:=20=EC=9D=91=EB=8B=B5=20=EC=84=A4?= =?UTF-8?q?=EB=AA=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../few/api/web/controller/article/ArticleControllerTest.kt | 4 ++-- .../com/few/api/web/controller/member/MemberControllerTest.kt | 2 +- .../web/controller/subscription/SubscriptionControllerTest.kt | 2 +- .../few/api/web/controller/workbook/WorkBookControllerTest.kt | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt index 19ef02f59..aa7772367 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt @@ -264,9 +264,9 @@ class ArticleControllerTest : ControllerTestSpec() { PayloadDocumentation.fieldWithPath("data.categories") .fieldWithArray("카테고리 목록"), PayloadDocumentation.fieldWithPath("data.categories[].code") - .fieldWithNumber("카테고리 code"), + .fieldWithNumber("카테고리 코드"), PayloadDocumentation.fieldWithPath("data.categories[].name") - .fieldWithString("카테고리 name") + .fieldWithString("카테고리 이름") ) ) ).build() diff --git a/api/src/test/kotlin/com/few/api/web/controller/member/MemberControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/member/MemberControllerTest.kt index 4652ebf0f..1eeaff20d 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/member/MemberControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/member/MemberControllerTest.kt @@ -149,7 +149,7 @@ class MemberControllerTest : ControllerTestSpec() { PayloadDocumentation.fieldWithPath("data.refreshToken") .fieldWithString("refreshToken"), PayloadDocumentation.fieldWithPath("data.isLogin") - .fieldWithBoolean("로그인 여부") + .fieldWithBoolean("로그인/회원가입 여부") ) ) ) diff --git a/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt index 2f9a76928..bffabac20 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt @@ -121,7 +121,7 @@ class SubscriptionControllerTest : ControllerTestSpec() { PayloadDocumentation.fieldWithPath("data.workbooks[].rank") .fieldWithNumber("순위"), PayloadDocumentation.fieldWithPath("data.workbooks[].totalSubscriber") - .fieldWithNumber("전체 구독자 수"), + .fieldWithNumber("누적 구독자 수"), PayloadDocumentation.fieldWithPath("data.workbooks[].articleInfo") .fieldWithString("학습지 정보(Json 타입)") ) diff --git a/api/src/test/kotlin/com/few/api/web/controller/workbook/WorkBookControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/workbook/WorkBookControllerTest.kt index d00278892..1126263cf 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/workbook/WorkBookControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/workbook/WorkBookControllerTest.kt @@ -169,9 +169,9 @@ class WorkBookControllerTest : ControllerTestSpec() { PayloadDocumentation.fieldWithPath("data.workbooks[].writers[].name") .fieldWithString("워크북 작가 이름"), PayloadDocumentation.fieldWithPath("data.workbooks[].writers[].url") - .fieldWithString("워크북 작가 링크"), + .fieldWithString("워크북 작가 외부 링크"), PayloadDocumentation.fieldWithPath("data.workbooks[].subscriberCount") - .fieldWithNumber("워크북 구독자 수") + .fieldWithNumber("워크북 현재 구독자 수") ) ) ).build()