diff --git a/perfume-api/src/docs/asciidoc/index.adoc b/perfume-api/src/docs/asciidoc/index.adoc index 944a9f6b..beb73f54 100644 --- a/perfume-api/src/docs/asciidoc/index.adoc +++ b/perfume-api/src/docs/asciidoc/index.adoc @@ -111,6 +111,17 @@ include::{snippets}/get-brand/http-request.adoc[] include::{snippets}/get-brand/http-response.adoc[] include::{snippets}/get-brand/response-fields.adoc[] +=== 실패 응답 +include::{snippets}/get-brand-failed/http-response.adoc[] + +== 브랜드 목록 조회 +=== 요청 +include::{snippets}/get-all-brands/http-request.adoc[] + +=== 응답 +include::{snippets}/get-all-brands/http-response.adoc[] +include::{snippets}/get-all-brands/response-fields.adoc[] + == 브랜드 매거진 생성 === 요청 include::{snippets}/create-magazine/http-request.adoc[] diff --git a/perfume-api/src/main/java/io/perfume/api/brand/application/exception/BrandNotFoundException.java b/perfume-api/src/main/java/io/perfume/api/brand/application/exception/BrandNotFoundException.java index d65b5e9c..5b538081 100644 --- a/perfume-api/src/main/java/io/perfume/api/brand/application/exception/BrandNotFoundException.java +++ b/perfume-api/src/main/java/io/perfume/api/brand/application/exception/BrandNotFoundException.java @@ -7,6 +7,6 @@ public class BrandNotFoundException extends CustomHttpException { public BrandNotFoundException(Long id) { super( - HttpStatus.NOT_FOUND, "cannot find brand.", "cannot find brand. id: " + id, LogLevel.WARN); + HttpStatus.NOT_FOUND, "cannot find brand.", "cannot find brand. id: " + id, LogLevel.INFO); } } diff --git a/perfume-api/src/test/java/io/perfume/api/brand/adapter/in/http/FindBrandControllerTest.java b/perfume-api/src/test/java/io/perfume/api/brand/adapter/in/http/FindBrandControllerTest.java index 8d1792da..7afa309c 100644 --- a/perfume-api/src/test/java/io/perfume/api/brand/adapter/in/http/FindBrandControllerTest.java +++ b/perfume-api/src/test/java/io/perfume/api/brand/adapter/in/http/FindBrandControllerTest.java @@ -10,9 +10,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import io.perfume.api.brand.adapter.in.http.dto.BrandResponse; +import io.perfume.api.brand.application.exception.BrandNotFoundException; import io.perfume.api.brand.application.port.in.FindBrandUseCase; import io.perfume.api.brand.application.port.in.dto.BrandResult; +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -22,6 +24,7 @@ import org.springframework.http.MediaType; import org.springframework.restdocs.RestDocumentationContextProvider; import org.springframework.restdocs.RestDocumentationExtension; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; @@ -52,15 +55,13 @@ void setUp( @DisplayName("브랜드 정보를 조회할 수 있다.") void get() throws Exception { // given + Long id = 1L; String name = "CHANEL"; String story = "샤넬 향수는 특별한 향과 함께 있는 그대로의 모습을 드러내는 작품입니다. 특별한 시리즈를 통해 샤넬 향수에 관련된 노하우와 전문 기술, 창의성을 끊임없이 추구하는 샤넬의 여정을 확인해 보세요."; String thumbnail = "testUrl.com"; - BrandResponse brandResponse = - BrandResponse.builder().name(name).story(story).thumbnail(thumbnail).build(); - - BrandResult brandResult = new BrandResult(name, story, thumbnail); + BrandResult brandResult = new BrandResult(id, name, story, thumbnail); given(findBrandUseCase.findBrandById(anyLong())).willReturn(brandResult); @@ -73,17 +74,73 @@ void get() throws Exception { .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.name").value(brandResponse.name())) - .andExpect(jsonPath("$.story").value(brandResponse.story())) - .andExpect(jsonPath("$.thumbnail").value(brandResponse.thumbnail())) + .andExpect(jsonPath("$.name").value(brandResult.name())) + .andExpect(jsonPath("$.story").value(brandResult.story())) + .andExpect(jsonPath("$.thumbnail").value(brandResult.thumbnail())) .andDo( document( "get-brand", responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("브랜드 ID"), fieldWithPath("name").type(JsonFieldType.STRING).description("브랜드 이름"), fieldWithPath("story").type(JsonFieldType.STRING).description("브랜드 이야기"), fieldWithPath("thumbnail") .type(JsonFieldType.STRING) .description("브랜드 썸네일 이미지")))); } + + @Test + @DisplayName("id에 해당하는 브랜드가 없으면 404 에러를 반환한다.") + void getFailed() throws Exception { + // given + Long id = 100L; + String name = "CHANEL"; + String story = + "샤넬 향수는 특별한 향과 함께 있는 그대로의 모습을 드러내는 작품입니다. 특별한 시리즈를 통해 샤넬 향수에 관련된 노하우와 전문 기술, 창의성을 끊임없이 추구하는 샤넬의 여정을 확인해 보세요."; + String thumbnail = "testUrl.com"; + + BrandResult brandResult = new BrandResult(id, name, story, thumbnail); + + given(findBrandUseCase.findBrandById(anyLong())).willThrow(new BrandNotFoundException(id)); + + // when + // then + mockMvc + .perform( + MockMvcRequestBuilders.get("/v1/brands/{brandId}", 1L) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andDo(document("get-brand-failed")); + } + + @Test + @DisplayName("브랜드 정보를 모두 조회할 수 있다.") + void getAll() throws Exception { + // given + List list = new ArrayList<>(); + list.add(new BrandResult(1L, "샤넬", "브랜드 스토리", "testUrl.com")); + list.add(new BrandResult(2L, "조말론", "브랜드 스토리", "testUrl.com")); + given(findBrandUseCase.findAll()).willReturn(list); + + // when & then + mockMvc + .perform(RestDocumentationRequestBuilders.get("/v1/brands")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$[0].id").value(list.get(0).id())) + .andExpect(jsonPath("$[0].name").value(list.get(0).name())) + .andExpect(jsonPath("$[0].story").value(list.get(0).story())) + .andExpect(jsonPath("$[0].thumbnail").value(list.get(0).thumbnail())) + .andDo( + document( + "get-all-brands", + responseFields( + fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("브랜드 ID"), + fieldWithPath("[].name").type(JsonFieldType.STRING).description("브랜드 이름"), + fieldWithPath("[].story").type(JsonFieldType.STRING).description("브랜드 이야기"), + fieldWithPath("[].thumbnail") + .type(JsonFieldType.STRING) + .description("브랜드 썸네일 이미지")))); + } } diff --git a/perfume-api/src/test/java/io/perfume/api/brand/adapter/out/persistence/BrandQueryPersistenceAdapterTest.java b/perfume-api/src/test/java/io/perfume/api/brand/adapter/out/persistence/BrandQueryPersistenceAdapterTest.java index fb176a52..8b13dfcf 100644 --- a/perfume-api/src/test/java/io/perfume/api/brand/adapter/out/persistence/BrandQueryPersistenceAdapterTest.java +++ b/perfume-api/src/test/java/io/perfume/api/brand/adapter/out/persistence/BrandQueryPersistenceAdapterTest.java @@ -1,8 +1,10 @@ package io.perfume.api.brand.adapter.out.persistence; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import io.perfume.api.brand.adapter.out.persistence.brand.BrandEntity; import io.perfume.api.brand.adapter.out.persistence.brand.BrandMapper; @@ -12,6 +14,7 @@ import io.perfume.api.configuration.TestQueryDSLConfiguration; import jakarta.persistence.EntityManager; import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -24,7 +27,7 @@ @ActiveProfiles("test") @Import({BrandQueryPersistenceAdapter.class, BrandMapper.class, TestQueryDSLConfiguration.class}) @DataJpaTest -public class BrandQueryPersistenceAdapterTest { +class BrandQueryPersistenceAdapterTest { @Autowired private EntityManager em; @Autowired private BrandQueryRepository brandQueryRepository; @@ -32,7 +35,7 @@ public class BrandQueryPersistenceAdapterTest { @Test @DisplayName("Brand 1건을 조회한다.") @Transactional - public void findByBrandId() { + void findByBrandId() { // given String brandName = "brand_name1"; String brandStory = "story1"; @@ -58,7 +61,7 @@ public void findByBrandId() { @Test @DisplayName("제거된 Brand는 조회할 수 없다.") @Transactional - public void findByBrandId_NotFound() { + void findByBrandId_NotFound() { // given String brandName = "brand_name1"; String brandStory = "story1"; @@ -81,4 +84,23 @@ public void findByBrandId_NotFound() { // then assertFalse(optionalBrand.isPresent()); } + + @Test + @DisplayName("Brand를 모두 조회한다.") + void findAll() { + for (int i = 0; i < 10; i++) { + BrandEntity brandEntity = + BrandEntity.builder().name("브랜드" + i).story("브랜드 스토리" + i).thumbnailId((long) i).build(); + em.persist(brandEntity); + } + em.flush(); + em.clear(); + + // when + List brands = brandQueryRepository.findAll(); + + // then + assertEquals(10, brands.size()); + assertTrue(brands.stream().allMatch(brand -> brand.getDeletedAt() == null)); + } } diff --git a/perfume-api/src/test/java/io/perfume/api/brand/application/service/FindBrandServiceTest.java b/perfume-api/src/test/java/io/perfume/api/brand/application/service/FindBrandServiceTest.java index 93de511b..98d022c3 100644 --- a/perfume-api/src/test/java/io/perfume/api/brand/application/service/FindBrandServiceTest.java +++ b/perfume-api/src/test/java/io/perfume/api/brand/application/service/FindBrandServiceTest.java @@ -1,6 +1,7 @@ package io.perfume.api.brand.application.service; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; import io.perfume.api.brand.application.port.in.dto.BrandResult; @@ -8,8 +9,7 @@ import io.perfume.api.brand.domain.Brand; import io.perfume.api.file.application.port.in.FindFileUseCase; import io.perfume.api.file.domain.File; -import java.io.FileNotFoundException; -import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -24,11 +24,10 @@ class FindBrandServiceTest { @Mock private FindFileUseCase findFileUseCase; @Test - void findBrandById() throws FileNotFoundException { + void findBrandById() { // given Long id = 1L; Long thumbnailId = 1L; - LocalDateTime now = LocalDateTime.now(); Brand brand = Brand.builder().id(id).name("CHANEL").story("스토리").thumbnailId(thumbnailId).build(); File file = File.builder().id(id).url("fileurl").build(); @@ -43,4 +42,29 @@ void findBrandById() throws FileNotFoundException { assertEquals(brand.getStory(), brandResult.story()); assertEquals(file.getUrl(), brandResult.thumbnail()); } + + @Test + void getAll() { + // given + Brand brand1 = Brand.builder().id(1L).name("샤넬").story("브랜드 스토리").thumbnailId(1L).build(); + Brand brand2 = Brand.builder().id(2L).name("조말론").story("브랜드 스토리").thumbnailId(2L).build(); + + File file = File.builder().id(1L).url("testurl1.com").build(); + + given(brandQueryRepository.findAll()).willReturn(List.of(brand1, brand2)); + given(findFileUseCase.findFileById(anyLong())).willReturn(Optional.ofNullable(file)); + + // when + List list = findBrandService.findAll(); + + // then + assertEquals(2, list.size()); + assertEquals(brand1.getName(), list.get(0).name()); + assertEquals(brand1.getStory(), list.get(0).story()); + assertEquals(file.getUrl(), list.get(0).thumbnail()); + + assertEquals(brand2.getName(), list.get(1).name()); + assertEquals(brand2.getStory(), list.get(1).story()); + assertEquals(file.getUrl(), list.get(1).thumbnail()); + } }