diff --git a/src/main/java/in/koreatech/koin/admin/shop/service/AdminShopService.java b/src/main/java/in/koreatech/koin/admin/shop/service/AdminShopService.java index 777b6b6dc..2df999e4d 100644 --- a/src/main/java/in/koreatech/koin/admin/shop/service/AdminShopService.java +++ b/src/main/java/in/koreatech/koin/admin/shop/service/AdminShopService.java @@ -111,14 +111,14 @@ public void createShop(AdminCreateShopRequest adminCreateShopRequest) { .shop(savedShop) .name(categoryName) .build(); - adminMenuCategoryRepository.save(menuCategory); + savedShop.getMenuCategories().add(menuCategory); } for (String imageUrl : adminCreateShopRequest.imageUrls()) { ShopImage shopImage = ShopImage.builder() .shop(savedShop) .imageUrl(imageUrl) .build(); - adminShopImageRepository.save(shopImage); + savedShop.getShopImages().add(shopImage); } for (InnerShopOpen open : adminCreateShopRequest.open()) { ShopOpen shopOpen = ShopOpen.builder() @@ -128,7 +128,7 @@ public void createShop(AdminCreateShopRequest adminCreateShopRequest) { .dayOfWeek(open.dayOfWeek()) .closed(open.closed()) .build(); - adminShopOpenRepository.save(shopOpen); + savedShop.getShopOpens().add(shopOpen); } List categories = adminShopCategoryRepository.findAllByIdIn(adminCreateShopRequest.categoryIds()); for (ShopCategory shopCategory : categories) { @@ -136,7 +136,7 @@ public void createShop(AdminCreateShopRequest adminCreateShopRequest) { .shopCategory(shopCategory) .shop(savedShop) .build(); - adminShopCategoryMapRepository.save(shopCategoryMap); + savedShop.getShopCategories().add(shopCategoryMap); } } @@ -160,14 +160,14 @@ public void createMenu(Integer shopId, AdminCreateMenuRequest adminCreateMenuReq .menuCategory(menuCategory) .menu(savedMenu) .build(); - adminMenuCategoryMapRepository.save(menuCategoryMap); + savedMenu.getMenuCategoryMaps().add(menuCategoryMap); } for (String imageUrl : adminCreateMenuRequest.imageUrls()) { MenuImage menuImage = MenuImage.builder() .imageUrl(imageUrl) .menu(savedMenu) .build(); - adminMenuImageRepository.save(menuImage); + savedMenu.getMenuImages().add(menuImage); } if (adminCreateMenuRequest.optionPrices() == null) { MenuOption menuOption = MenuOption.builder() @@ -175,7 +175,7 @@ public void createMenu(Integer shopId, AdminCreateMenuRequest adminCreateMenuReq .price(adminCreateMenuRequest.singlePrice()) .menu(menu) .build(); - adminMenuDetailRepository.save(menuOption); + savedMenu.getMenuOptions().add(menuOption); } else { for (var option : adminCreateMenuRequest.optionPrices()) { MenuOption menuOption = MenuOption.builder() @@ -183,7 +183,7 @@ public void createMenu(Integer shopId, AdminCreateMenuRequest adminCreateMenuReq .price(option.price()) .menu(menu) .build(); - adminMenuDetailRepository.save(menuOption); + savedMenu.getMenuOptions().add(menuOption); } } } diff --git a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java index 205456286..353998a97 100644 --- a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java +++ b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java @@ -71,7 +71,6 @@ public void saveDiningImage(DiningImageRequest imageRequest) { if (isOpened && !isImageExist) { eventPublisher.publishEvent(new DiningImageUploadEvent(dining.getId(), dining.getImageUrl())); } - dining.setImageUrl(imageRequest.imageUrl()); } diff --git a/src/main/java/in/koreatech/koin/domain/ownershop/service/OwnerShopService.java b/src/main/java/in/koreatech/koin/domain/ownershop/service/OwnerShopService.java index a96423a1a..5b9565ec0 100644 --- a/src/main/java/in/koreatech/koin/domain/ownershop/service/OwnerShopService.java +++ b/src/main/java/in/koreatech/koin/domain/ownershop/service/OwnerShopService.java @@ -97,14 +97,14 @@ public void createOwnerShops(Integer ownerId, OwnerShopsRequest ownerShopsReques .shop(savedShop) .name(categoryName) .build(); - menuCategoryRepository.save(menuCategory); + savedShop.getMenuCategories().add(menuCategory); } for (String imageUrl : ownerShopsRequest.imageUrls()) { ShopImage shopImage = ShopImage.builder() .shop(savedShop) .imageUrl(imageUrl) .build(); - shopImageRepository.save(shopImage); + savedShop.getShopImages().add(shopImage); } for (OwnerShopsRequest.InnerOpenRequest open : ownerShopsRequest.open()) { ShopOpen shopOpen = ShopOpen.builder() @@ -114,7 +114,7 @@ public void createOwnerShops(Integer ownerId, OwnerShopsRequest ownerShopsReques .dayOfWeek(open.dayOfWeek()) .closed(open.closed()) .build(); - shopOpenRepository.save(shopOpen); + savedShop.getShopOpens().add(shopOpen); } List shopCategories = shopCategoryRepository.findAllByIdIn(ownerShopsRequest.categoryIds()); for (ShopCategory shopCategory : shopCategories) { @@ -122,7 +122,7 @@ public void createOwnerShops(Integer ownerId, OwnerShopsRequest ownerShopsReques .shopCategory(shopCategory) .shop(savedShop) .build(); - shopCategoryMapRepository.save(shopCategoryMap); + savedShop.getShopCategories().add(shopCategoryMap); } } @@ -188,14 +188,14 @@ public void createMenu(Integer shopId, Integer ownerId, CreateMenuRequest create .menuCategory(menuCategory) .menu(savedMenu) .build(); - menuCategoryMapRepository.save(menuCategoryMap); + savedMenu.getMenuCategoryMaps().add(menuCategoryMap); } for (String imageUrl : createMenuRequest.imageUrls()) { MenuImage menuImage = MenuImage.builder() .imageUrl(imageUrl) .menu(savedMenu) .build(); - menuImageRepository.save(menuImage); + savedMenu.getMenuImages().add(menuImage); } if (createMenuRequest.optionPrices() == null) { MenuOption menuOption = MenuOption.builder() @@ -203,7 +203,7 @@ public void createMenu(Integer shopId, Integer ownerId, CreateMenuRequest create .price(createMenuRequest.singlePrice()) .menu(menu) .build(); - menuDetailRepository.save(menuOption); + savedMenu.getMenuOptions().add(menuOption); } else { for (var option : createMenuRequest.optionPrices()) { MenuOption menuOption = MenuOption.builder() @@ -211,7 +211,7 @@ public void createMenu(Integer shopId, Integer ownerId, CreateMenuRequest create .price(option.price()) .menu(menu) .build(); - menuDetailRepository.save(menuOption); + savedMenu.getMenuOptions().add(menuOption); } } } diff --git a/src/main/java/in/koreatech/koin/domain/shop/repository/MenuRepository.java b/src/main/java/in/koreatech/koin/domain/shop/repository/MenuRepository.java index ad68626fe..221f1365f 100644 --- a/src/main/java/in/koreatech/koin/domain/shop/repository/MenuRepository.java +++ b/src/main/java/in/koreatech/koin/domain/shop/repository/MenuRepository.java @@ -21,4 +21,6 @@ default Menu getById(Integer menuId) { } List findAllByShopId(Integer shopId); + + List findAll(); } diff --git a/src/main/java/in/koreatech/koin/domain/user/service/StudentService.java b/src/main/java/in/koreatech/koin/domain/user/service/StudentService.java index 4ca069153..cd8ad0655 100644 --- a/src/main/java/in/koreatech/koin/domain/user/service/StudentService.java +++ b/src/main/java/in/koreatech/koin/domain/user/service/StudentService.java @@ -120,7 +120,6 @@ public ModelAndView authenticate(AuthTokenRequest request) { } Student student = studentTemporaryStatus.get().toStudent(passwordEncoder); - studentRepository.save(student); userRepository.save(student.getUser()); diff --git a/src/test/java/in/koreatech/koin/AcceptanceTest.java b/src/test/java/in/koreatech/koin/AcceptanceTest.java index 0be2b62d4..e9c5011ec 100644 --- a/src/test/java/in/koreatech/koin/AcceptanceTest.java +++ b/src/test/java/in/koreatech/koin/AcceptanceTest.java @@ -1,19 +1,19 @@ package in.koreatech.koin; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; - import java.time.Clock; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.context.transaction.TestTransaction; +import org.springframework.test.web.servlet.MockMvc; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; @@ -28,12 +28,12 @@ import in.koreatech.koin.domain.shop.model.ReviewEventListener; import in.koreatech.koin.domain.shop.model.ShopEventListener; import in.koreatech.koin.domain.user.model.StudentEventListener; -import in.koreatech.koin.util.TestCircuitBreakerClient; import in.koreatech.koin.support.DBInitializer; -import io.restassured.RestAssured; +import in.koreatech.koin.util.TestCircuitBreakerClient; import jakarta.persistence.EntityManager; -@SpringBootTest(webEnvironment = RANDOM_PORT) +@SpringBootTest +@AutoConfigureMockMvc @Import({DBInitializer.class, TestJpaConfiguration.class, TestTimeConfig.class, TestRedisConfiguration.class}) @ActiveProfiles("test") public abstract class AcceptanceTest { @@ -41,8 +41,8 @@ public abstract class AcceptanceTest { private static final String ROOT = "test"; private static final String ROOT_PASSWORD = "1234"; - @LocalServerPort - protected int port; + @Autowired + public MockMvc mockMvc; @MockBean protected OwnerEventListener ownerEventListener; @@ -97,18 +97,18 @@ private static void configureProperties(final DynamicPropertyRegistry registry) static { mySqlContainer = (MySQLContainer)new MySQLContainer("mysql:8.0.29") - .withDatabaseName("test") - .withUsername(ROOT) - .withPassword(ROOT_PASSWORD) - .withCommand("--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"); + .withDatabaseName("test") + .withUsername(ROOT) + .withPassword(ROOT_PASSWORD) + .withCommand("--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"); redisContainer = new GenericContainer<>( - DockerImageName.parse("redis:7.0.9")) - .withExposedPorts(6379); + DockerImageName.parse("redis:7.0.9")) + .withExposedPorts(6379); mongoContainer = new GenericContainer<>( - DockerImageName.parse("mongo:6.0.14")) - .withExposedPorts(27017); + DockerImageName.parse("mongo:6.0.14")) + .withExposedPorts(27017); mySqlContainer.start(); redisContainer.start(); @@ -116,10 +116,18 @@ private static void configureProperties(final DynamicPropertyRegistry registry) } @BeforeEach - void delete() { - if (RestAssured.port == RestAssured.UNDEFINED_PORT) { - RestAssured.port = port; - } + void initIncrement() { + dataInitializer.initIncrement(); + dataInitializer.clearRedis(); + } + + protected void clear() { dataInitializer.clear(); } + + protected void forceVerify(Runnable runnable) { + TestTransaction.flagForCommit(); + TestTransaction.end(); + runnable.run(); + } } diff --git a/src/test/java/in/koreatech/koin/acceptance/ActivityApiTest.java b/src/test/java/in/koreatech/koin/acceptance/ActivityApiTest.java index 1d372c434..c09f806bf 100644 --- a/src/test/java/in/koreatech/koin/acceptance/ActivityApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/ActivityApiTest.java @@ -1,26 +1,30 @@ package in.koreatech.koin.acceptance; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + import java.time.LocalDate; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.fixture.ActivityFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class ActivityApiTest extends AcceptanceTest { @Autowired protected ActivityFixture activityFixture; @Test - @DisplayName("BCSD Lab 활동 내역을 조회한다.") - void getActivities() { + void BCSDLab_활동_내역을_조회한다() throws Exception { activityFixture.builder() .title("BCSD/KAP 통합") .description("BCSD와 KAP가 통합되었습니다.") @@ -48,17 +52,13 @@ void getActivities() { .isDeleted(false) .build(); - var response = RestAssured - .given() - .when() - .param("year", 2019) - .get("/activities") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/activities") + .param("year", "2019") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "Activities": [ { @@ -88,12 +88,11 @@ void getActivities() { } ] } - """); + """)); } @Test - @DisplayName("BCSD Lab 활동 내역을 조회한다. - 파라미터가 없는 경우 전체조회") - void getActivitiesWithoutYear() { + void BCSDLab_활동_내역을_조회한다_파라미터가_없는_경우_전체조회() throws Exception { activityFixture.builder() .title("BCSD/KAP 통합") .description("BCSD와 KAP가 통합되었습니다.") @@ -121,16 +120,12 @@ void getActivitiesWithoutYear() { .isDeleted(false) .build(); - var response = RestAssured - .given() - .when() - .get("/activities") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/activities") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "Activities": [ { @@ -172,6 +167,6 @@ void getActivitiesWithoutYear() { } ] } - """); + """)); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/ArticleApiTest.java b/src/test/java/in/koreatech/koin/acceptance/ArticleApiTest.java index 4a326ee06..653e3db92 100644 --- a/src/test/java/in/koreatech/koin/acceptance/ArticleApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/ArticleApiTest.java @@ -1,18 +1,22 @@ package in.koreatech.koin.acceptance; -import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.time.LocalDate; import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; -import in.koreatech.koin.config.FixedDate; import in.koreatech.koin.domain.community.article.model.Article; import in.koreatech.koin.domain.community.article.model.Board; import in.koreatech.koin.domain.community.article.model.Comment; @@ -22,10 +26,10 @@ import in.koreatech.koin.fixture.ArticleFixture; import in.koreatech.koin.fixture.BoardFixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class ArticleApiTest extends AcceptanceTest { @Autowired @@ -47,8 +51,9 @@ class ArticleApiTest extends AcceptanceTest { Board board; Article article1, article2; - @BeforeEach + @BeforeAll void givenBeforeEach() { + clear(); student = userFixture.준호_학생(); board = boardFixture.자유게시판(); article1 = articleFixture.자유글_1(board); @@ -56,8 +61,7 @@ void givenBeforeEach() { } @Test - @DisplayName("특정 게시글을 단일 조회한다.") - void getArticle() { + void 특정_게시글을_단일_조회한다() throws Exception { // given Comment request = Comment.builder() .article(article1) @@ -68,502 +72,33 @@ void getArticle() { .build(); commentRepository.save(request); - // when then - var response = RestAssured - .given() - .when() - .get("/articles/{articleId}", article1.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - { - "id": 1, - "board_id": 1, - "title": "자유 글의 제목입니다", - "content": "

내용

", - "author": "작성자1", - "hit": 3, - "attachments": [ - { - "id": 1, - "name": "첨부파일1.png", - "url": "https://example.com", - "created_at": "2024-01-15 12:00:00", - "updated_at": "2024-01-15 12:00:00" - } - ], - "registered_at": "2024-01-15", - "prev_id": null, - "next_id": 2, - "updated_at": "2024-01-15 12:00:00" - } - """); - } - - @Test - @DisplayName("게시글들을 페이지네이션하여 조회한다.") - void getArticlesByPagination() { - // when then - var response = RestAssured - .given() - .when() - .param("boardId", board.getId()) - .param("page", 1) - .param("limit", 10) - .get("/articles") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - { - "articles": [ - { - "id": 2, - "board_id": 1, - "title": "자유 글2의 제목입니다", - "author": "작성자2", - "hit": 2, - "registered_at": "2024-01-15", - "updated_at": "2024-01-15 12:00:00" - }, - { - "id": 1, - "board_id": 1, - "title": "자유 글의 제목입니다", - "author": "작성자1", - "hit": 2, - "registered_at": "2024-01-15", - "updated_at": "2024-01-15 12:00:00" - } - ], - "total_count": 2, - "current_count": 2, - "total_page": 1, - "current_page": 1 - } - """); - } - - @Test - @DisplayName("게시글들을 페이지네이션하여 조회한다. - 페이지가 0이면 1 페이지 조회") - void getArticlesByPagination_0Page() { - // when then - var response = RestAssured - .given() - .when() - .param("boardId", board.getId()) - .param("page", 0L) - .param("limit", 1) - .get("/articles") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertThat(response.jsonPath().getInt("articles[0].id")).isEqualTo(article2.getId()); - } - - @Test - @DisplayName("게시글들을 페이지네이션하여 조회한다. - 페이지가 음수이면 1 페이지 조회") - void getArticlesByPagination_lessThan0Pages() { - // when then - var response = RestAssured - .given() - .when() - .param("boardId", board.getId()) - .param("page", -10L) - .param("limit", 1) - .get("/articles") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertThat(response.jsonPath().getInt("articles[0].id")).isEqualTo(article2.getId()); - } - - @Test - @DisplayName("게시글들을 페이지네이션하여 조회한다. - limit가 0 이면 한 번에 1 게시글 조회") - void getArticlesByPagination_1imit() { - // when then - var response = RestAssured - .given() - .when() - .param("boardId", board.getId()) - .param("page", 1) - .param("limit", 0L) - .get("/articles") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertThat(response.jsonPath().getList("articles")).hasSize(1); - } - - @Test - @DisplayName("게시글들을 페이지네이션하여 조회한다. - limit가 음수이면 한 번에 1 게시글 조회") - void getArticlesByPagination_lessThan0Limit() { - // when then - var response = RestAssured - .given() - .when() - .param("boardId", board.getId()) - .param("page", 1) - .param("limit", -10L) - .get("/articles") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertThat(response.jsonPath().getList("articles")).hasSize(1); - } - - @Test - @DisplayName("게시글들을 페이지네이션하여 조회한다. - limit가 50 이상이면 한 번에 50 게시글 조회") - void getArticlesByPagination_over50Limit() { - // given - for (int i = 3; i < 63; i++) { // unique 중복 처리 - Article article = Article.builder() - .board(board) - .title("제목") - .content("

내용

") - .author("BCSD") - .hit(14) - .koinHit(0) - .isDeleted(false) - .articleNum(i) - .url("https://example.com") - .registeredAt(LocalDate.of(2024, 1, 15)) - .build(); - articleRepository.save(article); - } - - // when then - var response = RestAssured - .given() - .when() - .param("boardId", board.getId()) - .param("page", 1) - .param("limit", 100L) - .get("/articles") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertThat(response.jsonPath().getList("articles")).hasSize(50); - } - - @Test - @DisplayName("게시글들을 페이지네이션하여 조회한다. - 페이지, limit가 주어지지 않으면 1 페이지 10 게시글 조회") - void getArticlesByPagination_default() { - // given - for (int i = 3; i < 13; i++) { // unique 중복 처리 - Article article = Article.builder() - .board(board) - .title("제목") - .content("

내용

") - .author("BCSD") - .hit(14) - .koinHit(0) - .isDeleted(false) - .articleNum(i) - .url("https://example.com") - .registeredAt(LocalDate.of(2024, 1, 15)) - .build(); - articleRepository.save(article); - } - - // when then - var response = RestAssured - .given() - .when() - .param("boardId", board.getId()) - .get("/articles") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertThat(response.jsonPath().getList("articles")).hasSize(10); - - } - - @Test - @DisplayName("게시글들을 페이지네이션하여 조회한다. - 특정 페이지 조회") - void getArticlesByPagination_pageTest() { - var response = RestAssured - .given() - .when() - .param("boardId", board.getId()) - .param("page", 2) - .param("limit", 1) - .get("/articles") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/articles/{articleId}", article1.getId()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { - "articles": [ - { - "id": 1, - "board_id": 1, - "title": "자유 글의 제목입니다", - "author": "작성자1", - "hit": 2, - "registered_at": "2024-01-15", - "updated_at": "2024-01-15 12:00:00" - } - ], - "total_count": 2, - "current_count": 1, - "total_page": 2, - "current_page": 2 - } - """); - } - - @Test - @DisplayName("게시글들을 페이지네이션하여 조회한다. - 최대 페이지를 초과한 요청이 들어오면 마지막 페이지를 반환한다.") - void getArticlesByPagination_overMaxPageNotFound() { - // when then - var response = RestAssured - .given() - .when() - .param("boardId", board.getId()) - .param("page", 10000L) - .param("limit", 1) - .get("/articles") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - { - "articles": [ - { - "id": 1, - "board_id": 1, - "title": "자유 글의 제목입니다", - "author": "작성자1", - "hit": 2, - "registered_at": "2024-01-15", - "updated_at": "2024-01-15 12:00:00" - } - ], - "total_count": 2, - "current_count": 1, - "total_page": 2, - "current_page": 2 - } - """); - } - - @Test - @FixedDate(year = 2024, month = 1, day = 20) - @DisplayName("인기많은 게시글 목록을 조회한다.") - void getHotArticles() { - // given - for (int i = 5; i <= 7; i++) { - articleRepository.save(Article.builder() - .board(board) - .title(String.format("Article %d", i)) - .content("

내용

") - .author("BCSD") - .hit(i) - .koinHit(0) - .isDeleted(false) - .articleNum(i) - .url("https://example.com") - .registeredAt(LocalDate.of(2024, 1, 15)) - .build() - ); - } - - // when then - var response = RestAssured - .given() - .when() - .get("/articles/hot") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - [ - { - "id": 5, - "board_id": 1, - "title": "Article 7", - "author": "BCSD", - "hit": 7, - "registered_at": "2024-01-15", - "updated_at": "2024-01-15 12:00:00" - }, - { - "id": 4, - "board_id": 1, - "title": "Article 6", - "author": "BCSD", - "hit": 6, - "registered_at": "2024-01-15", - "updated_at": "2024-01-15 12:00:00" - }, - { - "id": 3, - "board_id": 1, - "title": "Article 5", - "author": "BCSD", - "hit": 5, - "registered_at": "2024-01-15", - "updated_at": "2024-01-15 12:00:00" - }, - { - "id": 2, - "board_id": 1, - "title": "자유 글2의 제목입니다", - "author": "작성자2", - "hit": 2, - "registered_at": "2024-01-15", - "updated_at": "2024-01-15 12:00:00" - }, - { - "id": 1, - "board_id": 1, - "title": "자유 글의 제목입니다", - "author": "작성자1", - "hit": 2, - "registered_at": "2024-01-15", - "updated_at": "2024-01-15 12:00:00" - } - ] - """); - } - - @Test - @DisplayName("게시글을 검색한다.") - void searchNoticeArticles() { - var response = RestAssured - .given() - .when() - .queryParam("query", "자유") - .queryParam("board", 1) - .get("/articles/search") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - { - "articles": [ - { - "id": 2, - "board_id": 1, - "title": "자유 글2의 제목입니다", - "author": "작성자2", - "hit": 2, - "registered_at": "2024-01-15", - "updated_at": "2024-01-15 12:00:00" - }, - { - "id": 1, - "board_id": 1, - "title": "자유 글의 제목입니다", - "author": "작성자1", - "hit": 2, - "registered_at": "2024-01-15", - "updated_at": "2024-01-15 12:00:00" - } - ], - "total_count": 2, - "current_count": 2, - "total_page": 1, - "current_page": 1 - } - """); - } - - @Test - void 사용자들이_많이_검색_한_키워드_추천() { - for (int i = 4; i <= 14; i++) { - Article article = Article.builder() - .board(board) - .title("제목%s".formatted(i)) - .content("

내용333

") - .author("작성자3") - .hit(1) - .koinHit(1) - .isDeleted(false) - .articleNum(i) - .url("https://example3.com") - .attachments(List.of()) - .registeredAt(LocalDate.of(2024, 1, 15)) - .isNotice(false) - .build(); - - articleRepository.save(article); - } - - String ipAddress1 = "192.168.1.1"; - String ipAddress2 = "192.168.1.2"; - String ipAddress3 = "192.168.1.3"; - - for (int i = 4; i < 9; i++) { - RestAssured - .given() - .queryParam("query", "검색어" + i) - .queryParam("board", 1) - .queryParam("page", 1) - .queryParam("limit", 10) - .queryParam("ipAddress", ipAddress1) - .when() - .get("/articles/search") - .then() - .statusCode(HttpStatus.OK.value()); - - RestAssured - .given() - .queryParam("query", "검색어" + i) - .queryParam("board", 1) - .queryParam("page", 1) - .queryParam("limit", 10) - .queryParam("ipAddress", ipAddress2) - .when() - .get("/articles/search") - .then() - .statusCode(HttpStatus.OK.value()); - } - - for (int i = 9; i < 14; i++) { - RestAssured - .given() - .queryParam("query", "검색어" + i) - .queryParam("board", 1) - .queryParam("page", 1) - .queryParam("limit", 10) - .queryParam("ipAddress", ipAddress3) - .when() - .get("/articles/search") - .then() - .statusCode(HttpStatus.OK.value()); - } - - var response = RestAssured - .given() - .queryParam("count", 5) - .when() - .get("/articles/hot/keyword") - .then() - .statusCode(HttpStatus.OK.value()) - .extract() - .asString(); - - assertThat(response).contains("검색어4", "검색어5", "검색어6", "검색어7", "검색어8"); + "id": 1, + "board_id": 1, + "title": "자유 글의 제목입니다", + "content": "

내용

", + "author": "작성자1", + "hit": 3, + "attachments": [ + { + "id": 1, + "name": "첨부파일1.png", + "url": "https://example.com", + "created_at": "2024-01-15 12:00:00", + "updated_at": "2024-01-15 12:00:00" + } + ], + "registered_at": "2024-01-15", + "prev_id": null, + "next_id": 2, + "updated_at": "2024-01-15 12:00:00" + } + """)); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/AuthApiTest.java b/src/test/java/in/koreatech/koin/acceptance/AuthApiTest.java index 1446494e9..7ff962522 100644 --- a/src/test/java/in/koreatech/koin/acceptance/AuthApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/AuthApiTest.java @@ -1,12 +1,19 @@ package in.koreatech.koin.acceptance; import static in.koreatech.koin.domain.user.model.UserType.STUDENT; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.transaction.annotation.Transactional; + +import com.fasterxml.jackson.databind.JsonNode; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.user.model.User; @@ -15,10 +22,10 @@ import in.koreatech.koin.domain.user.repository.UserTokenRepository; import in.koreatech.koin.fixture.UserFixture; import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class AuthApiTest extends AcceptanceTest { @Autowired @@ -31,8 +38,7 @@ class AuthApiTest extends AcceptanceTest { private UserTokenRepository tokenRepository; @Test - @DisplayName("사용자가 로그인을 수행한다") - void userLoginSuccess() { + void 사용자가_로그인을_수행한다() throws Exception { User user = userFixture.builder() .password("1234") .nickname("주노") @@ -44,41 +50,27 @@ void userLoginSuccess() { .isDeleted(false) .build(); - var response = RestAssured - .given() - .body(""" - { - "email": "test@koreatech.ac.kr", - "password": "1234" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/user/login") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - User userResult = userRepository.findById(user.getId()).get(); - UserToken token = tokenRepository.findById(userResult.getId()).get(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" - { - "token": "%s", - "refresh_token": "%s", - "user_type": "%s" - } - """, - response.jsonPath().getString("token"), - token.getRefreshToken(), - user.getUserType().name() - )); + MvcResult result = mockMvc.perform( + post("/user/login") + .content(""" + { + "email": "test@koreatech.ac.kr", + "password": "1234" + } + """ + ) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.token").isNotEmpty()) + .andExpect( + jsonPath("$.refresh_token").value(tokenRepository.findById(user.getId()).get().getRefreshToken())) + .andExpect(jsonPath("$.user_type").value(user.getUserType().name())) + .andReturn(); } @Test - @DisplayName("사용자가 로그인 이후 로그아웃을 수행한다") - void userLogoutSuccessg() { + void 사용자가_로그인_이후_로그아웃을_수행한다() throws Exception { User user = userFixture.builder() .password("1234") .nickname("주노") @@ -90,36 +82,33 @@ void userLogoutSuccessg() { .isDeleted(false) .build(); - var response = RestAssured - .given() - .body(""" - { - "email": "test@koreatech.ac.kr", - "password": "1234" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/user/login") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - RestAssured - .given() - .header("Authorization", "Bearer " + response.jsonPath().getString("token")) - .when() - .post("/user/logout") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + MvcResult result = mockMvc.perform( + post("/user/login") + .content(""" + { + "email": "test@koreatech.ac.kr", + "password": "1234" + } + """ + ) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()) + .andReturn(); + + JsonNode jsonNode = JsonAssertions.convertJsonNode(result); + mockMvc.perform( + post("/user/logout") + .header("Authorization", "Bearer " + jsonNode.get("token").asText()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); Assertions.assertThat(tokenRepository.findById(user.getId())).isEmpty(); } @Test - @DisplayName("사용자가 로그인 이후 refreshToken을 재발급한다") - void userRefreshToken() { + void 사용자가_로그인_이후_refreshToken을_재발급한다() throws Exception { User user = userFixture.builder() .password("1234") .nickname("주노") @@ -131,47 +120,47 @@ void userRefreshToken() { .isDeleted(false) .build(); - var loginResponse = RestAssured - .given() - .body(""" - { - "email": "test@koreatech.ac.kr", - "password": "1234" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/user/login") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - var response = RestAssured - .given() - .body(String.format(""" - { - "refresh_token": "%s" - } - """, - loginResponse.jsonPath().getString("refresh_token")) + MvcResult loginResult = mockMvc.perform( + post("/user/login") + .content(""" + { + "email": "test@koreatech.ac.kr", + "password": "1234" + } + """ + ) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()) + .andReturn(); + + JsonNode loginJsonNode = JsonAssertions.convertJsonNode(loginResult); + + MvcResult refreshResult = mockMvc.perform( + post("/user/refresh") + .content(String.format(""" + { + "refresh_token": "%s" + } + """, loginJsonNode.get("refresh_token").asText()) + ) + .contentType(MediaType.APPLICATION_JSON) ) - .contentType(ContentType.JSON) - .when() - .post("/user/refresh") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + .andExpect(status().isCreated()) + .andReturn(); + + JsonNode refreshJsonNode = JsonAssertions.convertJsonNode(refreshResult); UserToken token = tokenRepository.findById(user.getId()).get(); - JsonAssertions.assertThat(response.asPrettyString()) + JsonAssertions.assertThat(refreshResult.getResponse().getContentAsString()) .isEqualTo(String.format(""" { "token": "%s", "refresh_token": "%s" } """, - response.jsonPath().getString("token"), + refreshJsonNode.get("token").asText(), token.getRefreshToken() )); } diff --git a/src/test/java/in/koreatech/koin/acceptance/BusApiTest.java b/src/test/java/in/koreatech/koin/acceptance/BusApiTest.java index 5f7a42642..09d93fc8b 100644 --- a/src/test/java/in/koreatech/koin/acceptance/BusApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/BusApiTest.java @@ -1,7 +1,8 @@ package in.koreatech.koin.acceptance; import static java.time.format.DateTimeFormatter.ofPattern; -import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.time.LocalTime; import java.time.ZonedDateTime; @@ -9,12 +10,15 @@ import java.util.List; import org.assertj.core.api.SoftAssertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.transaction.annotation.Transactional; + +import com.fasterxml.jackson.databind.JsonNode; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.bus.dto.SingleBusTimeResponse; @@ -34,10 +38,10 @@ import in.koreatech.koin.domain.version.repository.VersionRepository; import in.koreatech.koin.fixture.BusFixture; import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; @SuppressWarnings("NonAsciiCharacters") -@TestConfiguration +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class BusApiTest extends AcceptanceTest { @Autowired @@ -52,28 +56,25 @@ class BusApiTest extends AcceptanceTest { @Autowired private ExpressBusCacheRepository expressBusCacheRepository; - @BeforeEach + @BeforeAll void setup() { + clear(); busFixture.버스_시간표_등록(); busFixture.시내버스_시간표_등록(); } @Test - @DisplayName("다음 셔틀버스까지 남은 시간을 조회한다.") - void getNextShuttleBusRemainTime() { - var response = RestAssured - .given() - .when() - .param("bus_type", "shuttle") - .param("depart", "koreatech") - .param("arrival", "terminal") - .get("/bus") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 다음_셔틀버스까지_남은_시간을_조회한다() throws Exception { + + mockMvc.perform( + get("/bus") + .param("bus_type", "shuttle") + .param("depart", "koreatech") + .param("arrival", "terminal") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "bus_type": "shuttle", "now_bus": { @@ -82,87 +83,11 @@ void getNextShuttleBusRemainTime() { }, "next_bus": null } - """); - } - - @Test - @DisplayName("다음 시내버스까지 남은 시간을 조회한다. - Redis 캐시 히트") - void getNextCityBusRemainTimeRedis() { - final long remainTime = 600L; - final long busNumber = 400; - BusType busType = BusType.CITY; - BusStation depart = BusStation.TERMINAL; - BusStation arrival = BusStation.KOREATECH; - - BusDirection direction = BusStation.getDirection(depart, arrival); - Version version = versionRepository.save( - Version.builder() - .version("test_version") - .type(VersionType.CITY.getValue()) - .build() - ); - - cityBusCacheRepository.save( - CityBusCache.of( - depart.getNodeId(direction).get(0), - List.of(CityBusCacheInfo.of( - CityBusArrival.builder() - .routeno(busNumber) - .arrtime(remainTime) - .build(), - version.getUpdatedAt()) - ) - ) - ); - - var response = RestAssured - .given() - .when() - .param("bus_type", busType.getName()) - .param("depart", depart.name()) - .param("arrival", arrival.name()) - .get("/bus") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - { - "bus_type": "city", - "now_bus": { - "bus_number": 400, - "remain_time": 600 - }, - "next_bus": null - } - """); - } - - @Test - @DisplayName("셔틀버스의 코스 정보들을 조회한다.") - void getBusCourses() { - var response = RestAssured - .given() - .when() - .get("/bus/courses") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertSoftly( - softly -> { - softly.assertThat(response.body().jsonPath().getList("").size()).isEqualTo(1); - softly.assertThat(response.body().jsonPath().getString("[0].bus_type")).isEqualTo("shuttle"); - softly.assertThat(response.body().jsonPath().getString("[0].direction")).isEqualTo("from"); - softly.assertThat(response.body().jsonPath().getString("[0].region")).isEqualTo("천안"); - } - ); + """)); } @Test - @DisplayName("다음 셔틀버스까지 남은 시간을 조회한다.") - void getSearchTimetable() { + void 도착_시간이_18시_10분인_버스를_정확하게_조회한다() throws Exception { versionRepository.save( Version.builder() .version("test_version") @@ -200,21 +125,22 @@ void getSearchTimetable() { ); expressBusCacheRepository.save(expressBusCache); - var response = RestAssured - .given() - .when() - .param("date", requestedAt.toLocalDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))) - .param("time", requestedAt.toLocalTime().format(DateTimeFormatter.ofPattern("HH:mm"))) - .param("depart", depart.name()) - .param("arrival", arrival.name()) - .get("/bus/search") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + MvcResult result = mockMvc.perform( + get("/bus/search") + .param("date", requestedAt.toLocalDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))) + .param("time", requestedAt.toLocalTime().format(DateTimeFormatter.ofPattern("HH:mm"))) + .param("depart", depart.name()) + .param("arrival", arrival.name()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andReturn(); SoftAssertions.assertSoftly( softly -> { - softly.assertThat(response.body().jsonPath().getList("", SingleBusTimeResponse.class)) + JsonNode jsonNode = JsonAssertions.convertJsonNode(result); + List actualResponseList = JsonAssertions.convertToList(jsonNode, SingleBusTimeResponse.class); + softly.assertThat(actualResponseList) .containsExactly( new SingleBusTimeResponse("express", LocalTime.parse(arrivalTime)), new SingleBusTimeResponse("shuttle", LocalTime.parse(arrivalTime)), @@ -225,67 +151,79 @@ void getSearchTimetable() { } @Test - @DisplayName("시내버스 시간표를 조회한다 - 지원하지 않음") - void getCityBusTimetable() { - Version version = Version.builder() - .version("test_version") - .type(VersionType.CITY.getValue()) - .build(); - versionRepository.save(version); + void 다음_시내버스까지_남은_시간을_조회한다_Redis_캐시_히트() throws Exception { + final long remainTime = 600L; + final long busNumber = 400; + BusType busType = BusType.CITY; + BusStation depart = BusStation.TERMINAL; + BusStation arrival = BusStation.KOREATECH; - Long busNumber = 400L; - String direction = "종합터미널"; + BusDirection direction = BusStation.getDirection(depart, arrival); + Version version = versionRepository.save( + Version.builder() + .version("test_version") + .type(VersionType.CITY.getValue()) + .build() + ); - var response = RestAssured - .given() - .when() - .param("bus_number", busNumber) - .param("direction", direction) - .get("/bus/timetable/city") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + cityBusCacheRepository.save( + CityBusCache.of( + depart.getNodeId(direction).get(0), + List.of(CityBusCacheInfo.of( + CityBusArrival.builder() + .routeno(busNumber) + .arrtime(remainTime) + .build(), + version.getUpdatedAt()) + ) + ) + ); - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/bus") + .param("bus_type", busType.getName()) + .param("depart", depart.name()) + .param("arrival", arrival.name()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { - "bus_info": { - "arrival_node": "종합터미널", - "depart_node": "병천3리", - "number": 400 + "bus_type": "city", + "now_bus": { + "bus_number": 400, + "remain_time": 600 }, - "bus_timetables": [ - { - "day_of_week": "평일", - "depart_info": ["06:00", "07:00"] - }, - { - "day_of_week": "주말", - "depart_info": ["08:00", "09:00"] - } - ], - "updated_at": "2024-07-19 19:00:00" + "next_bus": null } - """); + """)); } @Test - @DisplayName("셔틀버스 시간표를 조회한다.") - void getShuttleBusTimetable() { - var response = RestAssured - .given() - .when() - .param("bus_type", "shuttle") - .param("direction", "from") - .param("region", "천안") - .get("/bus/timetable") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + void 셔틀버스의_코스_정보들을_조회한다() throws Exception { + mockMvc.perform( + get("/bus/courses") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(1)) + .andExpect(jsonPath("$[0].bus_type").value("shuttle")) + .andExpect(jsonPath("$[0].direction").value("from")) + .andExpect(jsonPath("$[0].region").value("천안")); + } - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - [ + @Test + void 셔틀버스_시간표를_조회한다() throws Exception { + mockMvc.perform( + get("/bus/timetable") + .param("bus_type", "shuttle") + .param("direction", "from") + .param("region", "천안") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + [ { "route_name": "주중", "arrival_info": [ @@ -308,12 +246,11 @@ void getShuttleBusTimetable() { ] } ] - """); + """)); } @Test - @DisplayName("셔틀버스 시간표를 조회한다(업데이트 시각 포함).") - void getShuttleBusTimetableWithUpdatedAt() { + void 셔틀버스_시간표를_조회한다_업데이트_시각_포함() throws Exception { Version version = Version.builder() .version("test_version") .type(VersionType.SHUTTLE.getValue()) @@ -324,20 +261,16 @@ void getShuttleBusTimetableWithUpdatedAt() { String direction = "from"; String region = "천안"; - var response = RestAssured - .given() - .when() - .param("bus_type", busType.getName()) - .param("direction", direction) - .param("region", region) - .get("/bus/timetable/v2") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - { + mockMvc.perform( + get("/bus/timetable/v2") + .param("bus_type", busType.getName()) + .param("direction", direction) + .param("region", region) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { "bus_timetables": [ { "route_name": "주중", @@ -363,6 +296,6 @@ void getShuttleBusTimetableWithUpdatedAt() { ], "updated_at": "2024-01-15 12:00:00" } - """); + """)); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/CircuitBreakerTest.java b/src/test/java/in/koreatech/koin/acceptance/CircuitBreakerTest.java index ce4b8ce6a..58ece6777 100644 --- a/src/test/java/in/koreatech/koin/acceptance/CircuitBreakerTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/CircuitBreakerTest.java @@ -4,11 +4,13 @@ import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doThrow; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.config.TestResilience4jConfig; @@ -17,6 +19,8 @@ import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; @Import(TestResilience4jConfig.class) +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class CircuitBreakerTest extends AcceptanceTest { @Autowired @@ -24,7 +28,7 @@ class CircuitBreakerTest extends AcceptanceTest { private CircuitBreaker circuitBreaker; - @BeforeEach + @BeforeAll void setUp() { circuitBreaker = circuitBreakerRegistry.circuitBreaker("test"); circuitBreaker.reset(); diff --git a/src/test/java/in/koreatech/koin/acceptance/CoopShopTest.java b/src/test/java/in/koreatech/koin/acceptance/CoopShopTest.java index 89b58734d..07eea0c71 100644 --- a/src/test/java/in/koreatech/koin/acceptance/CoopShopTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/CoopShopTest.java @@ -1,19 +1,24 @@ package in.koreatech.koin.acceptance; -import static io.restassured.RestAssured.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.coopshop.model.CoopShop; import in.koreatech.koin.domain.coopshop.repository.CoopShopRepository; import in.koreatech.koin.fixture.CoopShopFixture; -import in.koreatech.koin.support.JsonAssertions; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class CoopShopTest extends AcceptanceTest { @Autowired @@ -25,26 +30,22 @@ class CoopShopTest extends AcceptanceTest { private CoopShop 학생식당; private CoopShop 세탁소; - @BeforeEach + @BeforeAll void setUp() { + clear(); 학생식당 = coopShopFixture.학생식당(); 세탁소 = coopShopFixture.세탁소(); } @Test - public void getCoopShops() { - var response = given() - .when() - .get("/coopshop") - .then() - .log().all() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo( - """ - [ + void 생협의_모든_상점을_조회한다() throws Exception { + mockMvc.perform( + get("/coopshop") + .contentType(MediaType.ALL.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + [ { "id": 1, "name": "학생식당", @@ -104,24 +105,18 @@ public void getCoopShops() { "updated_at" : "2024-01-15" } ] - """ - ); + """)); } @Test - public void getCoopShop() { - var response = given() - .when() - .get("/coopshop/1") - .then() - .log().all() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo( - """ - { + void 생협의_상점을_조회한다() throws Exception { + mockMvc.perform( + get("/coopshop/1") + .contentType(MediaType.ALL.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { "id": 1, "name": "학생식당", "semester" : "하계방학", @@ -155,9 +150,7 @@ public void getCoopShop() { "location": "학생회관 1층", "remarks": "공휴일 휴무", "updated_at" : "2024-01-15" - } - """ - ); - + } + """)); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/DeptApiTest.java b/src/test/java/in/koreatech/koin/acceptance/DeptApiTest.java index 55ff3c7e1..57df819f9 100644 --- a/src/test/java/in/koreatech/koin/acceptance/DeptApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/DeptApiTest.java @@ -1,60 +1,48 @@ package in.koreatech.koin.acceptance; -import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; +import org.junit.jupiter.api.TestInstance; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.dept.model.Dept; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class DeptApiTest extends AcceptanceTest { @Test - @DisplayName("학과 번호를 통해 학과 이름을 조회한다.") - void findDeptNameByDeptNumber() { - // given + void 학과_번호를_통해_학과_이름을_조회한다() throws Exception { Dept dept = Dept.COMPUTER_SCIENCE; - - // when then - var response = RestAssured - .given() - .when() - .param("dept_num", dept.getNumbers().get(0)) - .get("/dept") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/dept") + .param("dept_num", dept.getNumbers().get(0)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "dept_num": "35", "name": "컴퓨터공학부" } - """ - ); + """)); } @Test - @DisplayName("모든 학과 정보를 조회한다.") - void findAllDepts() { + void 모든_학과_정보를_조회한다() throws Exception { //given final int DEPT_SIZE = Dept.values().length - 1; - //when then - var response = RestAssured - .given() - .when() - .get("/depts") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertThat(response.body().jsonPath().getList(".")).hasSize(DEPT_SIZE); + mockMvc.perform( + get("/depts") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(DEPT_SIZE)); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java b/src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java index 862c04101..fe14df1ab 100644 --- a/src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java @@ -1,18 +1,22 @@ package in.koreatech.koin.acceptance; -import static io.restassured.RestAssured.given; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.time.LocalDate; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.coop.model.DiningSoldOutCache; @@ -24,11 +28,10 @@ import in.koreatech.koin.fixture.CoopShopFixture; import in.koreatech.koin.fixture.DiningFixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class DiningApiTest extends AcceptanceTest { @Autowired @@ -53,8 +56,9 @@ class DiningApiTest extends AcceptanceTest { private String token_현수; private CoopShop 학생식당; - @BeforeEach + @BeforeAll void setUp() { + clear(); coop_준기 = userFixture.준기_영양사().getUser(); token_준기 = userFixture.getToken(coop_준기); owner_현수 = userFixture.현수_사장님().getUser(); @@ -64,17 +68,13 @@ void setUp() { } @Test - @DisplayName("특정 날짜의 모든 식단들을 조회한다.") - void findDinings() { - var response = given() - .when() - .get("/dinings?date=240115") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 특정_날짜의_모든_식단들을_조회한다() throws Exception { + mockMvc.perform( + get("/dinings?date=240115") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" [ { "id": 1, @@ -99,32 +99,27 @@ void findDinings() { "is_liked" : false } ] - """); + """)); } @Test - @DisplayName("잘못된 형식의 날짜로 조회한다. - 날짜의 형식이 잘못되었다면 400") - void invalidFormatDate() { - given() - .when() - .get("/dinings?date=20240115") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); + void 잘못된_형식의_날짜로_조회한다_날짜의_형식이_잘못되었다면_400() throws Exception { + + mockMvc.perform( + get("/dinings?date=20240115") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("날짜가 비어있다. - 오늘 날짜를 받아 조회한다.") - void nullDate() { - var response = given() - .when() - .get("/dinings") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 날짜_비어있다_오늘_날짜를_받아_조회한다() throws Exception { + mockMvc.perform( + get("/dinings") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" [ { "id": 1, @@ -149,221 +144,194 @@ void nullDate() { "is_liked" : false } ] - """); + """)); } @Test - @DisplayName("영양사 권한으로 품절 요청을 보낸다.") - void requestSoldOut() { - RestAssured.given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준기) - .body(String.format(""" + void 영양사_권한으로_품절_요청을_보낸다() throws Exception { + mockMvc.perform( + patch("/coop/dining/soldout") + .header("Authorization", "Bearer " + token_준기) + .content(String.format(""" { "menu_id": "%s", "sold_out": %s } - """, A코너_점심.getId(), true) + """, A코너_점심.getId(), true)) + .contentType(MediaType.APPLICATION_JSON) ) - .when() - .patch("/coop/dining/soldout") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + .andExpect(status().isOk()); } @Test - @DisplayName("권한이 없는 사용자가 품절 요청을 보낸다") - void requestSoldOutNoAuth() { - given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_현수) - .body(String.format(""" - { - "menu_id": "%s", - "sold_out": %s - } - """, A코너_점심.getId(), true)) - .when() - .patch("/coop/dining/soldout") - .then() - .statusCode(HttpStatus.FORBIDDEN.value()) - .extract(); + void 권한이_없는_사용자가_품절_요청을_보낸다() throws Exception { + mockMvc.perform( + patch("/coop/dining/soldout") + .header("Authorization", "Bearer " + token_현수) + .content(String.format(""" + { + "menu_id": "%s", + "sold_out": %s + } + """, A코너_점심.getId(), true)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isForbidden()); } @Test - @DisplayName("영양사님 권한으로 식단 이미지를 업로드한다. - 이미지 URL이 DB에 저장된다.") - void ImageUpload() { + void 영양사님_권한으로_식단_이미지를_업로드한다_이미지_URL이_DB에_저장된다() throws Exception { String imageUrl = "https://stage.koreatech.in/image.jpg"; - - given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준기) - .body(String.format(""" - { - "menu_id": "%s", - "image_url": "%s" - } - """, A코너_점심.getId(), imageUrl) + mockMvc.perform( + patch("/coop/dining/image") + .header("Authorization", "Bearer " + token_준기) + .content(String.format(""" + { + "menu_id": "%s", + "image_url": "%s" + } + """, A코너_점심.getId(), imageUrl)) + .contentType(MediaType.APPLICATION_JSON) ) - .when() - .patch("/coop/dining/image") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - Dining result = diningRepository.getById(A코너_점심.getId()); - assertThat(result.getImageUrl()).isEqualTo(imageUrl); + .andExpect(status().isOk()) + .andReturn(); + Dining dining = diningRepository.getById(A코너_점심.getId()); + assertThat(dining.getImageUrl()).isEqualTo(imageUrl); } @Test - @DisplayName("허용되지 않은 권한으로 식단 이미지를 업로드한다. - 권한 오류.") - void ImageUploadWithNoAuth() { + void 허용되지_않은_권한으로_식단_이미지를_업로드한다_권한_오류() throws Exception { String imageUrl = "https://stage.koreatech.in/image.jpg"; - - given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_현수) - .body(String.format(""" - { - "menu_id": "%s", - "image_url": "%s" - } - """, A코너_점심.getId(), imageUrl) + mockMvc.perform( + patch("/coop/dining/image") + .header("Authorization", "Bearer " + token_현수) + .content(String.format(""" + { + "menu_id": "%s", + "image_url": "%s" + } + """, A코너_점심.getId(), imageUrl)) + .contentType(MediaType.APPLICATION_JSON) ) - .when() - .patch("/coop/dining/image") - .then() - .statusCode(HttpStatus.FORBIDDEN.value()) - .extract(); + .andExpect(status().isForbidden()) + .andReturn(); } @Test - @DisplayName("해당 식사시간에 품절 요청을 한다. - 품절 알림이 발송된다.") - void checkSoldOutNotification() { - given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준기) - .body(String.format(""" - { - "menu_id": "%s", - "sold_out": %s - } - """, A코너_점심.getId(), true)) - .when() - .patch("/coop/dining/soldout") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + void 해당_식사시간에_품절_요청을_한다_품절_알림이_발송된다() throws Exception { + mockMvc.perform( + patch("/coop/dining/soldout") + .header("Authorization", "Bearer " + token_준기) + .content(String.format(""" + { + "menu_id": "%s", + "sold_out": "%s" + } + """, A코너_점심.getId(), true)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andReturn(); - verify(coopEventListener).onDiningSoldOutRequest(any()); + forceVerify(() -> verify(coopEventListener).onDiningSoldOutRequest(any())); + clear(); + setUp(); } @Test - @DisplayName("해당 식사시간 외에 품절 요청을 한다. - 품절 알림이 발송되지 않는다.") - void checkSoldOutNotificationAfterHours() { + void 해당_식사시간_외에_품절_요청을_한다_품절_알림이_발송되지_않는다() throws Exception { Dining A코너_저녁 = diningFixture.A코너_저녁(LocalDate.parse("2024-01-15")); - given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준기) - .body(String.format(""" - { - "menu_id": "%s", - "sold_out": %s - } - """, A코너_저녁.getId(), true)) - .when() - .patch("/coop/dining/soldout") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - verify(coopEventListener, never()).onDiningSoldOutRequest(any()); + mockMvc.perform( + patch("/coop/dining/soldout") + .header("Authorization", "Bearer " + token_준기) + .content(String.format(""" + { + "menu_id": "%s", + "sold_out": "%s" + } + """, A코너_저녁.getId(), true)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andReturn(); + forceVerify(() -> verify(coopEventListener, never()).onDiningSoldOutRequest(any())); + clear(); + setUp(); } @Test - @DisplayName("동일한 식단 코너의 두 번째 품절 요청은 알림이 가지 않는다.") - void checkSoldOutNotificationResend() { + void 동일한_식단_코너의_두_번째_품절_요청은_알림이_가지_않는다() throws Exception { diningSoldOutCacheRepository.save(DiningSoldOutCache.from(A코너_점심.getPlace())); - - given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준기) - .body(String.format(""" - { - "menu_id": "%s", - "sold_out": %s - } - """, A코너_점심.getId(), true)) - .when() - .patch("/coop/dining/soldout") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - verify(coopEventListener, never()).onDiningSoldOutRequest(any()); + mockMvc.perform( + patch("/coop/dining/soldout") + .header("Authorization", "Bearer " + token_준기) + .content(String.format(""" + { + "menu_id": "%s", + "sold_out": "%s" + } + """, A코너_점심.getId(), true)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andReturn(); + forceVerify(() -> verify(coopEventListener, never()).onDiningSoldOutRequest(any())); + clear(); + setUp(); } @Test - @DisplayName("특정 식단의 좋아요를 누른다") - void likeDining() { - RestAssured.given() - .header("Authorization", "Bearer " + token_준기) - .param("diningId", 1) - .when() - .patch("/dining/like") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + void 특정_식단의_좋아요를_누른다() throws Exception { + mockMvc.perform( + patch("/dining/like") + .header("Authorization", "Bearer " + token_준기) + .param("diningId", String.valueOf(1)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andReturn(); } @Test - @DisplayName("특정 식단의 좋아요 중복해서 누르면 에러") - void likeDiningDuplicate() { - RestAssured.given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준기) - .param("diningId", 1) - .when() - .patch("/dining/like") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - RestAssured.given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준기) - .param("diningId", 1) - .when() - .patch("/dining/like") - .then() - .statusCode(HttpStatus.CONFLICT.value()) - .extract(); + void 특정_식단의_좋아요_중복해서_누르면_에러() throws Exception { + mockMvc.perform( + patch("/dining/like") + .header("Authorization", "Bearer " + token_준기) + .param("diningId", String.valueOf(1)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andReturn(); + + mockMvc.perform( + patch("/dining/like") + .header("Authorization", "Bearer " + token_준기) + .param("diningId", String.valueOf(1)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isConflict()) + .andReturn(); } @Test - @DisplayName("좋아요 누른 식단은 isLiked가 true로 반환") - void checkIsLikedTrue() { - RestAssured.given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준기) - .param("diningId", 1) - .when() - .patch("/dining/like") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - var response = given() - .header("Authorization", "Bearer " + token_준기) - .when() - .get("/dinings?date=240115") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 좋아요_누른_식단은_isLiked가_true로_반환() throws Exception { + mockMvc.perform( + patch("/dining/like") + .header("Authorization", "Bearer " + token_준기) + .param("diningId", String.valueOf(1)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andReturn(); + + mockMvc.perform( + get("/dinings?date=240115") + .header("Authorization", "Bearer " + token_준기) + .param("diningId", String.valueOf(1)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" [ { "id": 1, @@ -388,22 +356,20 @@ void checkIsLikedTrue() { "is_liked" : true } ] - """); + """)) + .andReturn(); } @Test - @DisplayName("좋아요 안누른 식단은 isLiked가 false로 반환") - void checkIsLikedFalse() { - var response = given() - .when() - .header("Authorization", "Bearer " + token_준기) - .get("/dinings?date=240115") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 좋아요_안누른_식단은_isLiked가_false로_반환() throws Exception { + mockMvc.perform( + get("/dinings?date=240115") + .header("Authorization", "Bearer " + token_준기) + .param("diningId", String.valueOf(1)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" [ { "id": 1, @@ -428,68 +394,62 @@ void checkIsLikedFalse() { "is_liked" : false } ] - """); + """)) + .andReturn(); } @Test - @DisplayName("이미지 업로드를 한다. - 품절 알림이 발송된다.") - void checkImageUploadNotification() { + void 이미지_업로드를_한다_품절_알림이_발송된다() throws Exception { String imageUrl = "https://stage.koreatech.in/image.jpg"; - given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준기) - .body(String.format(""" - { - "menu_id": "%s", - "image_url": "%s" - } - """, A코너_점심.getId(), imageUrl)) - .when() - .patch("/coop/dining/image") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - verify(coopEventListener).onDiningImageUploadRequest(any()); + mockMvc.perform( + patch("/coop/dining/image") + .header("Authorization", "Bearer " + token_준기) + .content(String.format(""" + { + "menu_id": "%s", + "image_url": "%s" + } + """, A코너_점심.getId(), imageUrl)) + .param("diningId", String.valueOf(1)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); + forceVerify(() -> verify(coopEventListener).onDiningImageUploadRequest(any())); + clear(); + setUp(); } @Test - @DisplayName("해당 식사시간 외에 이미지 업로드를 한다. - 품절 알림이 발송되지 않는다.") - void checkImageUploadNotificationAfterHours() { + void 해당_식사시간_외에_이미지_업로드를_한다_품절_알림이_발송되지_않는다() throws Exception { Dining A코너_저녁 = diningFixture.A코너_저녁(LocalDate.parse("2024-01-15")); String imageUrl = "https://stage.koreatech.in/image.jpg"; - - given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준기) - .body(String.format(""" - { - "menu_id": "%s", - "image_url": "%s" - } - """, A코너_저녁.getId(), imageUrl)) - .when() - .patch("/coop/dining/image") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - verify(coopEventListener, never()).onDiningImageUploadRequest(any()); + mockMvc.perform( + patch("/coop/dining/image") + .header("Authorization", "Bearer " + token_준기) + .content(String.format(""" + { + "menu_id": "%s", + "image_url": "%s" + } + """, A코너_저녁.getId(), imageUrl)) + .param("diningId", String.valueOf(1)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); + forceVerify(() -> verify(coopEventListener, never()).onDiningImageUploadRequest(any())); + clear(); + setUp(); } @Test - @DisplayName("특정 메뉴, 특정 코너의 식단을 검색한다") - void searchDinings() { - var response = given() - .header("Authorization", "Bearer " + token_준기) - .when() - .get("/dinings/search?keyword=육개장&page=1&limit=10&filter=A코너") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 특정_메뉴_특정_코너의_식단을_검색한다() throws Exception { + mockMvc.perform( + get("/dinings/search?keyword=육개장&page=1&limit=10&filter=A코너") + .header("Authorization", "Bearer " + token_준기) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "total_count": 1, "current_count": 1, @@ -515,46 +475,44 @@ void searchDinings() { "likes": 0 } ] - } - """); + } + """)) + .andReturn(); } @Test - @DisplayName("특정 메뉴, 특정 코너의 식단을 검색한다 - 해당사항 없을 경우") - void searchDiningsNothing() { - var response = given() - .header("Authorization", "Bearer " + token_준기) - .when() - .get("/dinings/search?keyword=육개장&page=1&limit=10&filter=B코너") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 특정_메뉴_특정_코너의_식단을_검색한다_해당사항_없을_경우() throws Exception { + mockMvc.perform( + get("/dinings/search?keyword=육개장&page=1&limit=10&filter=B코너") + .queryParam("keyword", "육개장") + .queryParam("page", "1") + .queryParam("limit", "10") + .queryParam("filter", "B코너") + .header("Authorization", "Bearer " + token_준기) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "total_count": 0, "current_count": 0, "total_page": 0, "current_page": 1, "dinings": [] - } - """); + } + """)) + .andReturn(); } @Test - @DisplayName("특정 메뉴의 식단을 검색한다 - 필터 없을 경우") - void searchDiningsNoFilter() { - var response = given() - .header("Authorization", "Bearer " + token_준기) - .when() - .get("/dinings/search?keyword=육개장&page=1&limit=10&filter=") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 특정_메뉴의_식단을_검색한다_필터_없을_경우() throws Exception { + mockMvc.perform( + get("/dinings/search?keyword=육개장&page=1&limit=10&filter=") + .header("Authorization", "Bearer " + token_준기) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "total_count": 1, "current_count": 1, @@ -581,6 +539,7 @@ void searchDiningsNoFilter() { } ] } - """); + """)) + .andReturn(); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/KeywordApiTest.java b/src/test/java/in/koreatech/koin/acceptance/KeywordApiTest.java index 4560dd104..fee434949 100644 --- a/src/test/java/in/koreatech/koin/acceptance/KeywordApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/KeywordApiTest.java @@ -4,13 +4,19 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.ArrayList; import java.util.List; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.community.article.model.Article; @@ -25,11 +31,10 @@ import in.koreatech.koin.fixture.BoardFixture; import in.koreatech.koin.fixture.KeywordFixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class KeywordApiTest extends AcceptanceTest { @Autowired @@ -53,115 +58,94 @@ public class KeywordApiTest extends AcceptanceTest { @Autowired private ArticleFixture articleFixture; + private Student 준호_학생; + private String token; + + @BeforeAll + void setup() { + clear(); + 준호_학생 = userFixture.준호_학생(); + token = userFixture.getToken(준호_학생.getUser()); + } + @Test - void 알림_키워드_추가() { - Student student = userFixture.준호_학생(); - String token = userFixture.getToken(student.getUser()); - - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" - { - "keyword": "장학금" - } - """) - .when() - .post("/articles/keyword") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 알림_키워드_추가() throws Exception { + mockMvc.perform( + post("/articles/keyword") + .header("Authorization", "Bearer " + token) + .content(""" + { + "keyword": "장학금" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "id": 1, "keyword": "장학금" } - """); + """)); } @Test - void 알림_키워드_10개_넘게_추가시_400에러() { - Student student = userFixture.준호_학생(); - String token = userFixture.getToken(student.getUser()); - + void 알림_키워드_10개_넘게_추가시_400에러() throws Exception { for (int i = 0; i < 10; i++) { - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(String.format(""" + mockMvc.perform( + get("/articles/keyword") + .header("Authorization", "Bearer " + token) + .content(String.format(""" { - "keyword": "keyword%d" - } - """, i)) - - .when() - .post("/articles/keyword") - .then() - .statusCode(HttpStatus.OK.value()); + "keyword": "keyword%d" + } + """, i)) + .contentType(MediaType.APPLICATION_JSON) + ); } - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" - { - "keyword": "장학금" - } - """) - .when() - .post("/articles/keyword") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()); + mockMvc.perform( + get("/articles/keyword") + .header("Authorization", "Bearer " + token) + .content(""" + { + "keyword": "장학금" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - void 알림_키워드_삭제() { - Student student = userFixture.준호_학생(); - String token = userFixture.getToken(student.getUser()); - ArticleKeywordUserMap articleKeywordUserMap = keywordFixture.키워드1("수강 신청", student.getUser()); - - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .pathParam("id", articleKeywordUserMap.getId()) - .contentType(ContentType.JSON) - .when() - .delete("/articles/keyword/{id}") - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract() - .asString(); + void 알림_키워드_삭제() throws Exception { + ArticleKeywordUserMap articleKeywordUserMap = keywordFixture.키워드1("수강 신청", 준호_학생.getUser()); + + mockMvc.perform( + delete("/articles/keyword/{id}", String.valueOf(articleKeywordUserMap.getId())) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); assertThat(articleKeywordUserMapRepository.findById(articleKeywordUserMap.getId()).isEmpty()); assertThat(articleKeywordRepository.findById(articleKeywordUserMap.getArticleKeyword().getId()).isEmpty()); } @Test - void 자신의_알림_키워드_조회() { - Student student = userFixture.준호_학생(); - String token = userFixture.getToken(student.getUser()); - ArticleKeywordUserMap articleKeywordUserMap1 = keywordFixture.키워드1("수강신청", student.getUser()); - ArticleKeywordUserMap articleKeywordUserMap2 = keywordFixture.키워드1("장학금", student.getUser()); - ArticleKeywordUserMap articleKeywordUserMap3 = keywordFixture.키워드1("생활관", student.getUser()); - - var response = RestAssured - .given() - .when() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .get("/articles/keyword/me") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - { + void 자신의_알림_키워드_조회() throws Exception { + ArticleKeywordUserMap articleKeywordUserMap1 = keywordFixture.키워드1("수강신청", 준호_학생.getUser()); + ArticleKeywordUserMap articleKeywordUserMap2 = keywordFixture.키워드1("장학금", 준호_학생.getUser()); + ArticleKeywordUserMap articleKeywordUserMap3 = keywordFixture.키워드1("생활관", 준호_학생.getUser()); + + mockMvc.perform( + get("/articles/keyword/me") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { "count": 3, "keywords": [ { @@ -177,37 +161,28 @@ public class KeywordApiTest extends AcceptanceTest { "keyword": "생활관" } ] - }"""); + } + """)); } @Test - void 사용자_아무_것도_추가_안_했을_때_자신의_알림_키워드_조회_빈_리스트_반환() { - Student student = userFixture.준호_학생(); - String token = userFixture.getToken(student.getUser()); - - var response = RestAssured - .given() - .when() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .get("/articles/keyword/me") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - { + void 사용자_아무_것도_추가_안_했을_때_자신의_알림_키워드_조회_빈_리스트_반환() throws Exception { + mockMvc.perform( + get("/articles/keyword/me") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { "count": 0, "keywords": [] - }"""); + } + """)); } @Test - void 가장_인기_있는_키워드_추천() { - Student student = userFixture.준호_학생(); - String token1 = userFixture.getToken(student.getUser()); - + void 가장_인기_있는_키워드_추천() throws Exception { // Redis에 인기 키워드 15개 저장 List hotKeywords = new ArrayList<>(); for (int i = 1; i <= 15; i++) { @@ -219,33 +194,25 @@ public class KeywordApiTest extends AcceptanceTest { hotKeywords.forEach(keyword -> articleKeywordSuggestRepository.save(keyword)); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token1) - .contentType(ContentType.JSON) - .when() - .get("/articles/keyword/suggestions") - .then() - .statusCode(HttpStatus.OK.value()) - .extract() - .asPrettyString(); - - JsonAssertions.assertThat(response).isEqualTo(""" - { - "keywords": [ - "수강신청1", "수강신청2", "수강신청3", "수강신청4", "수강신청5", - "수강신청6", "수강신청7", "수강신청8", "수강신청9", "수강신청10", - "수강신청11", "수강신청12", "수강신청13", "수강신청14", "수강신청15" - ] - } - """); + mockMvc.perform( + get("/articles/keyword/suggestions") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "keywords": [ + "수강신청1", "수강신청2", "수강신청3", "수강신청4", "수강신청5", + "수강신청6", "수강신청7", "수강신청8", "수강신청9", "수강신청10", + "수강신청11", "수강신청12", "수강신청13", "수강신청14", "수강신청15" + ] + } + """)); } @Test - void 새로운_공지사항이_올라오고_해당_키워드를_갖고_있는_사용자가_있을_경우_알림이_발송된다() { - Student student1 = userFixture.준호_학생(); - Student student2 = userFixture.성빈_학생(); - + void 새로운_공지사항이_올라오고_해당_키워드를_갖고_있는_사용자가_있을_경우_알림이_발송된다() throws Exception { Board board = boardFixture.자유게시판(); List articleIds = new ArrayList<>(); @@ -255,28 +222,27 @@ public class KeywordApiTest extends AcceptanceTest { articleIds.add(article.getId()); } - keywordFixture.키워드1("수강신청1", student1.getUser()); - - RestAssured - .given() - .contentType(ContentType.JSON) - .body(""" - { - "update_notification": %s - } - """.formatted(articleIds.toString())) - .when() - .post("/articles/keyword/notification") - .then() - .statusCode(HttpStatus.OK.value()); - - verify(articleKeywordEventListener).onKeywordRequest(any()); + keywordFixture.키워드1("수강신청1", 준호_학생.getUser()); + + mockMvc.perform( + post("/articles/keyword/notification") + .header("Authorization", "Bearer " + token) + .content(""" + { + "update_notification": %s + } + """.formatted(articleIds.toString())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); + forceVerify(() -> verify(articleKeywordEventListener).onKeywordRequest(any())); + clear(); + setup(); } @Test - void 새로운_공지사항이_올라오고_해당_키워드를_갖고_있는_사용자가_없으면_알림이_발송되지_않는다() { - Student student1 = userFixture.준호_학생(); - Student student2 = userFixture.성빈_학생(); + @Transactional + void 새로운_공지사항이_올라오고_해당_키워드를_갖고_있는_사용자가_없으면_알림이_발송되지_않는다() throws Exception { Board board = boardFixture.자유게시판(); @@ -287,21 +253,21 @@ public class KeywordApiTest extends AcceptanceTest { articleIds.add(article.getId()); } - keywordFixture.키워드1("수강신청6", student1.getUser()); - - RestAssured - .given() - .contentType(ContentType.JSON) - .body(""" - { - "update_notification": %s - } - """.formatted(articleIds.toString())) - .when() - .post("/articles/keyword/notification") - .then() - .statusCode(HttpStatus.OK.value()); - - verify(articleKeywordEventListener, never()).onKeywordRequest(any()); + keywordFixture.키워드1("수강신청6", 준호_학생.getUser()); + + mockMvc.perform( + post("/articles/keyword/notification") + .content(""" + { + "update_notification": %s + } + """.formatted(articleIds.toString())) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); + forceVerify(() -> verify(articleKeywordEventListener, never()).onKeywordRequest(any())); + clear(); + setup(); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/LandApiTest.java b/src/test/java/in/koreatech/koin/acceptance/LandApiTest.java index 218a64aa3..a3af67b11 100644 --- a/src/test/java/in/koreatech/koin/acceptance/LandApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/LandApiTest.java @@ -1,38 +1,38 @@ package in.koreatech.koin.acceptance; -import org.junit.jupiter.api.DisplayName; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.land.model.Land; import in.koreatech.koin.fixture.LandFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class LandApiTest extends AcceptanceTest { @Autowired private LandFixture landFixture; @Test - @DisplayName("복덕방 리스트를 조회한다.") - void getLands() { + void 복덕방_리스트를_조회한다() throws Exception { landFixture.신안빌(); landFixture.에듀윌(); - var response = RestAssured - .given() - .when() - .get("/lands") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/lands") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "lands": [ { @@ -57,24 +57,19 @@ void getLands() { } ] } - """); + """)); } @Test - @DisplayName("복덕방을 단일 조회한다.") - void getLand() { + void 복덕방을_단일_조회한다() throws Exception { Land land = landFixture.에듀윌(); - var response = RestAssured - .given() - .when() - .get("/lands/{id}", land.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/lands/{id}", land.getId()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "opt_electronic_door_locks": false, "opt_tv": false, @@ -116,6 +111,6 @@ void getLand() { "permalink": "%EC%97%90", "room_type": "원룸" } - """); + """)); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/MemberApiTest.java b/src/test/java/in/koreatech/koin/acceptance/MemberApiTest.java index 9e621a021..0917824ec 100644 --- a/src/test/java/in/koreatech/koin/acceptance/MemberApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/MemberApiTest.java @@ -1,10 +1,16 @@ package in.koreatech.koin.acceptance; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.member.model.Member; @@ -12,9 +18,10 @@ import in.koreatech.koin.domain.member.repository.TrackRepository; import in.koreatech.koin.fixture.MemberFixture; import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class MemberApiTest extends AcceptanceTest { @Autowired @@ -26,8 +33,9 @@ class MemberApiTest extends AcceptanceTest { Track backend; Track frontend; - @BeforeEach + @BeforeAll void setUp() { + clear(); backend = trackRepository.save( Track.builder() .name("BackEnd") @@ -41,22 +49,20 @@ void setUp() { } @Test - @DisplayName("BCSDLab 회원의 정보를 조회한다") - void getMember() { + void BCSDLab_회원의_정보를_조회한다() throws Exception { Member member = memberFixture.최준호(backend); - var response = RestAssured - .given() - .when() - .get("/members/{id}", member.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + MvcResult result = mockMvc.perform( + get("/members/{id}", member.getId()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andReturn(); - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + JsonAssertions.assertThat(result.getResponse().getContentAsString()) + .isEqualTo(""" { - "id": %d, + "id": 1, "name": "최준호", "student_number": "2019136135", "track": "BackEnd", @@ -64,31 +70,23 @@ void getMember() { "email": "testjuno@gmail.com", "image_url": "https://imagetest.com/juno.jpg", "is_deleted": false, - "created_at": "%s", - "updated_at": "%s" - }""", - member.getId(), - response.jsonPath().getString("created_at"), - response.jsonPath().getString("updated_at") - )); + "created_at": "2024-01-15 12:00:00", + "updated_at": "2024-01-15 12:00:00" + }""" + ); } @Test - @DisplayName("BCSDLab 회원들의 정보를 조회한다") - void getMembers() { + void BCSDLab_회원들의_정보를_조회한다() throws Exception { memberFixture.최준호(backend); memberFixture.박한수(frontend); - var response = RestAssured - .given() - .when() - .get("/members") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/members") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" [ { "id": 1, @@ -115,7 +113,6 @@ void getMembers() { "updated_at": "2024-01-15 12:00:00" } ] - """ - ); + """)); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/NotificationApiTest.java b/src/test/java/in/koreatech/koin/acceptance/NotificationApiTest.java index efcede631..1dbff099b 100644 --- a/src/test/java/in/koreatech/koin/acceptance/NotificationApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/NotificationApiTest.java @@ -4,12 +4,16 @@ import static in.koreatech.koin.global.domain.notification.model.NotificationSubscribeType.DINING_SOLD_OUT; import static in.koreatech.koin.global.domain.notification.model.NotificationSubscribeType.SHOP_EVENT; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.user.model.User; @@ -19,11 +23,10 @@ import in.koreatech.koin.global.domain.notification.model.NotificationSubscribe; import in.koreatech.koin.global.domain.notification.model.NotificationSubscribeType; import in.koreatech.koin.global.domain.notification.repository.NotificationSubscribeRepository; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class NotificationApiTest extends AcceptanceTest { @Autowired @@ -39,16 +42,16 @@ class NotificationApiTest extends AcceptanceTest { String userToken; String deviceToken; - @BeforeEach + @BeforeAll void setUp() { + clear(); user = userFixture.준호_학생().getUser(); userToken = userFixture.getToken(user); deviceToken = "testToken"; } @Test - @DisplayName("알림 구독 내역을 조회한다.") - void getNotificationSubscribe() { + void 알림_구독_내역을_조회한다() throws Exception { //given NotificationSubscribe notificationSubscribe = NotificationSubscribe.builder() .subscribeType(SHOP_EVENT) @@ -57,18 +60,13 @@ void getNotificationSubscribe() { notificationSubscribeRepository.save(notificationSubscribe); - //when then - var response = RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .when() - .get("/notification") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + MvcResult result = mockMvc.perform( + get("/notification") + .header("Authorization", "Bearer " + userToken) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "is_permit": false, "subscribes": [ @@ -113,73 +111,66 @@ void getNotificationSubscribe() { } ] } - """); + """)) + .andReturn(); } @Test - @DisplayName("전체 알림을 구독한다. - 디바이스 토큰을 추가한다.") - void createDivceToken() { + void 전체_알림을_구독한다_디바이스_토큰을_추가한다() throws Exception { //when then - RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .body(String.format(""" - { - "device_token": "%s" - } - """, deviceToken)) - .contentType(ContentType.JSON) - .when() - .post("/notification") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - User result = userRepository.getById(user.getId()); - assertThat(result.getDeviceToken()).isEqualTo(deviceToken); + MvcResult result = mockMvc.perform( + post("/notification") + .content(String.format(""" + { + "device_token": "%s" + } + """, deviceToken)) + .header("Authorization", "Bearer " + userToken) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()) + .andReturn(); + + User resultUser = userRepository.getById(user.getId()); + assertThat(resultUser.getDeviceToken()).isEqualTo(deviceToken); } @Test - @DisplayName("특정 알림을 구독한다.") - void subscribeNotificationType() { + void 특정_알림을_구독한다() throws Exception { String notificationType = SHOP_EVENT.name(); - RestAssured.given() - .header("Authorization", "Bearer " + userToken) - .body(String.format(""" - { - "device_token": "%s" - } - """, deviceToken)) - .contentType(ContentType.JSON) - .when() - .post("/notification") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .contentType(ContentType.JSON) - .queryParam("type", notificationType) - .when() - .post("/notification/subscribe") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - var response = RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .when() - .get("/notification") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + post("/notification") + .content(String.format(""" + { + "device_token": "%s" + } + """, deviceToken)) + .header("Authorization", "Bearer " + userToken) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); + + mockMvc.perform( + post("/notification/subscribe") + .content(String.format(""" + { + "device_token": "%s" + } + """, deviceToken)) + .header("Authorization", "Bearer " + userToken) + .queryParam("type", notificationType) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); + + mockMvc.perform( + get("/notification") + .header("Authorization", "Bearer " + userToken) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "is_permit": true, "subscribes": [ @@ -224,62 +215,66 @@ void subscribeNotificationType() { } ] } - """); + """)); } @Test - @DisplayName("특정 세부알림을 구독한다.") - void subscribeNotificationDetailType() { + void 특정_세부알림을_구독한다() throws Exception { String notificationType = DINING_SOLD_OUT.name(); String notificationDetailType = LUNCH.name(); - RestAssured.given() - .header("Authorization", "Bearer " + userToken) - .body(String.format(""" - { - "device_token": "%s" - } - """, deviceToken)) - .contentType(ContentType.JSON) - .when() - .post("/notification") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .contentType(ContentType.JSON) - .queryParam("type", notificationType) - .when() - .post("/notification/subscribe") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .contentType(ContentType.JSON) - .queryParam("detail_type", notificationDetailType) - .when() - .post("/notification/subscribe/detail") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - var response = RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .when() - .get("/notification") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + post("/notification") + .content(String.format(""" + { + "device_token": "%s" + } + """, deviceToken)) + .header("Authorization", "Bearer " + userToken) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); + + mockMvc.perform( + post("/notification/subscribe") + .queryParam("type", notificationType) + .content(String.format(""" + { + "device_token": "%s" + } + """, deviceToken)) + .header("Authorization", "Bearer " + userToken) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); + + mockMvc.perform( + post("/notification/subscribe/detail") + .queryParam("detail_type", notificationDetailType) + .content(String.format(""" + { + "device_token": "%s" + } + """, deviceToken)) + .header("Authorization", "Bearer " + userToken) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); + + mockMvc.perform( + get("/notification") + .queryParam("detail_type", notificationDetailType) + .content(String.format(""" + { + "device_token": "%s" + } + """, deviceToken)) + .header("Authorization", "Bearer " + userToken) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { "is_permit": true, "subscribes": [ @@ -324,30 +319,26 @@ void subscribeNotificationDetailType() { } ] } - """); + """)); } @Test - @DisplayName("전체 알림 구독을 취소한다. - 디바이스 토큰을 삭제한다.") - void deleteDeviceToken() { + void 전체_알림_구독을_취소한다_디바이스_토큰을_삭제한다() throws Exception { user.permitNotification(deviceToken); - RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .when() - .delete("/notification") - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); + mockMvc.perform( + delete("/notification") + .header("Authorization", "Bearer " + userToken) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); User result = userRepository.getById(user.getId()); assertThat(result.getDeviceToken()).isNull(); } @Test - @DisplayName("특정 알림 구독을 취소한다.") - void unsubscribeNotificationType() { + void 특정_알림_구독을_취소한다() throws Exception { var SubscribeShopEvent = NotificationSubscribe.builder() .subscribeType(SHOP_EVENT) .user(user) @@ -363,41 +354,39 @@ void unsubscribeNotificationType() { String notificationType = SHOP_EVENT.name(); - RestAssured.given() - .header("Authorization", "Bearer " + userToken) - .body(String.format(""" - { - "device_token": "%s" - } - """, deviceToken)) - .contentType(ContentType.JSON) - .when() - .post("/notification") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .queryParam("type", notificationType) - .when() - .delete("/notification/subscribe") - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); - - var response = RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .when() - .get("/notification") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + post("/notification") + .header("Authorization", "Bearer " + userToken) + .content(String.format(""" + { + "device_token": "%s" + } + """, deviceToken)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); + + mockMvc.perform( + delete("/notification/subscribe") + .header("Authorization", "Bearer " + userToken) + .queryParam("type", notificationType) + .content(String.format(""" + { + "device_token": "%s" + } + """, deviceToken)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); + + mockMvc.perform( + get("/notification") + .header("Authorization", "Bearer " + userToken) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { "is_permit": true, "subscribes": [ @@ -442,12 +431,12 @@ void unsubscribeNotificationType() { } ] } - """); + """)); + ; } @Test - @DisplayName("특정 세부 알림 구독을 취소한다.") - void unsubscribeNotificationDetailType() { + void 특정_세부_알림_구독을_취소한다() throws Exception { var SubscribeDiningSoldOut = NotificationSubscribe.builder() .subscribeType(NotificationSubscribeType.DINING_SOLD_OUT) .user(user) @@ -472,52 +461,41 @@ void unsubscribeNotificationDetailType() { String notificationType = DINING_SOLD_OUT.name(); String notificationDetailType = LUNCH.name(); - RestAssured.given() - .header("Authorization", "Bearer " + userToken) - .body(String.format(""" - { - "device_token": "%s" - } - """, deviceToken)) - .contentType(ContentType.JSON) - .when() - .post("/notification") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .contentType(ContentType.JSON) - .queryParam("type", notificationType) - .when() - .post("/notification/subscribe") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .queryParam("detail_type", notificationDetailType) - .when() - .delete("/notification/subscribe/detail") - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); - - var response = RestAssured - .given() - .header("Authorization", "Bearer " + userToken) - .when() - .get("/notification") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + post("/notification") + .header("Authorization", "Bearer " + userToken) + .content(String.format(""" + { + "device_token": "%s" + } + """, deviceToken)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); + + mockMvc.perform( + post("/notification/subscribe") + .header("Authorization", "Bearer " + userToken) + .queryParam("type", notificationType) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); + + mockMvc.perform( + delete("/notification/subscribe/detail") + .header("Authorization", "Bearer " + userToken) + .queryParam("detail_type", notificationDetailType) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); + + mockMvc.perform( + get("/notification") + .header("Authorization", "Bearer " + userToken) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "is_permit": true, "subscribes": [ @@ -562,6 +540,8 @@ void unsubscribeNotificationDetailType() { } ] } - """); + """)); } + + } diff --git a/src/test/java/in/koreatech/koin/acceptance/OwnerApiTest.java b/src/test/java/in/koreatech/koin/acceptance/OwnerApiTest.java index 69cee47a0..785c9a5de 100644 --- a/src/test/java/in/koreatech/koin/acceptance/OwnerApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/OwnerApiTest.java @@ -4,15 +4,20 @@ import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.mockito.Mockito.any; import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import org.assertj.core.api.Assertions; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; import in.koreatech.koin.AcceptanceTest; @@ -26,11 +31,10 @@ import in.koreatech.koin.domain.user.repository.UserRepository; import in.koreatech.koin.fixture.ShopFixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class OwnerApiTest extends AcceptanceTest { @Autowired @@ -58,188 +62,162 @@ class OwnerApiTest extends AcceptanceTest { private PasswordEncoder passwordEncoder; @Test - @DisplayName("사장님이 로그인을 진행한다") - void ownerLogin() { + void 사장님이_로그인을_진행한다() throws Exception { Owner owner = userFixture.원경_사장님(); String phoneNumber = owner.getAccount(); String password = "1234"; - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .body(""" - { - "account" : "%s", - "password" : "%s" - } - """.formatted(phoneNumber, password)) - .when() - .post("/owner/login") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + mockMvc.perform( + post("/owner/login") + .content(""" + { + "account" : "%s", + "password" : "%s" + } + """.formatted(phoneNumber, password)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); } @Test - @DisplayName("관리자가 승인하지 않은 사장님이 로그인을 진행한다") - void unAuthOwnerLogin() { + void 관리자가_승인하지_않은_사장님이_로그인을_진행한다() throws Exception { Owner owner = userFixture.철수_사장님(); String phoneNumber = owner.getAccount(); String password = "1234"; - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .body(""" - { - "account" : "%s", - "password" : "%s" - } - """.formatted(phoneNumber, password)) - .when() - .post("/owner/login") - .then() - .statusCode(HttpStatus.FORBIDDEN.value()) - .extract(); + mockMvc.perform( + post("/owner/login") + .content(""" + { + "account" : "%s", + "password" : "%s" + } + """.formatted(phoneNumber, password)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isForbidden()); } @Test - @DisplayName("로그인된 사장님 정보를 조회한다.") - void getOwner() { + void 로그인된_사장님_정보를_조회한다() throws Exception { // given Owner owner = userFixture.현수_사장님(); Shop shop = shopFixture.마슬랜(owner); String token = userFixture.getToken(owner.getUser()); - // when then - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/owner") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" - { - "email": "hysoo@naver.com", - "name": "테스트용_현수", - "company_number": "123-45-67190", - "account" : "01098765432", - "attachments": [ - { - "id": 1, - "file_url": "https://test.com/현수_사장님_인증사진_1.jpg", - "file_name": "현수_사장님_인증사진_1.jpg" - }, - { - "id": 2, - "file_url": "https://test.com/현수_사장님_인증사진_2.jpg", - "file_name": "현수_사장님_인증사진_2.jpg" - } - ], - "shops": [ - { - "id": %d, - "name": "마슬랜 치킨" - } - ] - } - """, shop.getId() - )); + mockMvc.perform( + get("/owner") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "email": "hysoo@naver.com", + "name": "테스트용_현수", + "company_number": "123-45-67190", + "account" : "01098765432", + "attachments": [ + { + "id": 1, + "file_url": "https://test.com/현수_사장님_인증사진_1.jpg", + "file_name": "현수_사장님_인증사진_1.jpg" + }, + { + "id": 2, + "file_url": "https://test.com/현수_사장님_인증사진_2.jpg", + "file_name": "현수_사장님_인증사진_2.jpg" + } + ], + "shops": [ + { + "id": 1, + "name": "마슬랜 치킨" + } + ] + } + """)); } @Test - @DisplayName("사장님이 회원가입 인증번호 전송 요청을 한다 - 전송한 코드로 인증요청이 성공한다") - void requestAndVerifySign() { + void 사장님이_회원가입_인증번호_전송_요청을_한다_전송한_코드로_인증요청이_성공한다() throws Exception { String ownerEmail = "junho5336@gmail.com"; - RestAssured - .given() - .body(String.format(""" - { - "address": "%s" - } - """, ownerEmail) + + mockMvc.perform( + post("/owners/verification/email") + .content(String.format(""" + { + "address": "%s" + } + """, ownerEmail)) + .contentType(MediaType.APPLICATION_JSON) ) - .contentType(ContentType.JSON) - .when() - .post("/owners/verification/email") - .then() - .statusCode(HttpStatus.OK.value()); + .andExpect(status().isOk()); var verifyCode = ownerVerificationStatusRepository.getByVerify(ownerEmail); - RestAssured - .given() - .body(String.format(""" - { - "address": "%s", - "certification_code": "%s" - } - """, ownerEmail, verifyCode.getCertificationCode())) - .contentType(ContentType.JSON) - .when() - .post("/owners/verification/code") - .then() - .statusCode(HttpStatus.OK.value()); - + mockMvc.perform( + post("/owners/verification/code") + .content(String.format(""" + { + "address": "%s", + "certification_code": "%s" + } + """, ownerEmail, verifyCode.getCertificationCode())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); var result = ownerVerificationStatusRepository.findById(ownerEmail); Assertions.assertThat(result).isNotPresent(); } @Test - @DisplayName("사장님 회원가입 이메일 인증번호 전송 요청 이벤트 발생 시 슬랙 전송 이벤트가 발생한다.") - void checkOwnerEventListenerByEmail() { - RestAssured - .given() - .body(""" - { - "address": "test@gmail.com" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/owners/verification/email") - .then() - .statusCode(HttpStatus.OK.value()); - - verify(ownerEventListener).onOwnerEmailRequest(any()); + void 사장님_회원가입_이메일_인증번호_전송_요청_이벤트_발생_시_슬랙_전송_이벤트가_발생한다() throws Exception { + mockMvc.perform( + post("/owners/verification/email") + .content(""" + { + "address": "test@gmail.com" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); + forceVerify(() -> verify(ownerEventListener).onOwnerEmailRequest(any())); + clear(); } @Nested @DisplayName("사장님 회원가입") + @Transactional + @TestInstance(TestInstance.Lifecycle.PER_CLASS) class ownerRegister { @Test - @DisplayName("사장님이 이메일로 회원가입 요청을 한다.") - void register() { + void 사장님이_이메일로_회원가입_요청을_한다() throws Exception { // when & then - var response = RestAssured - .given() - .body(""" - { - "attachment_urls": [ - { - "file_url": "https://static.koreatech.in/testimage.png" - } - ], - "company_number": "012-34-56789", - "email": "helloworld@koreatech.ac.kr", - "name": "최준호", - "password": "a0240120305812krlakdsflsa;1235", - "phone_number": "010-0000-0000", - "shop_id": null, - "shop_name": "기분좋은 뷔짱" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/owners/register") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + post("/owners/register") + .content(""" + { + "attachment_urls": [ + { + "file_url": "https://static.koreatech.in/testimage.png" + } + ], + "company_number": "012-34-56789", + "email": "helloworld@koreatech.ac.kr", + "name": "최준호", + "password": "a0240120305812krlakdsflsa;1235", + "phone_number": "010-0000-0000", + "shop_id": null, + "shop_name": "기분좋은 뷔짱" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); // when transactionTemplate.executeWithoutResult(status -> { @@ -255,7 +233,8 @@ void register() { .isEqualTo("https://static.koreatech.in/testimage.png"); softly.assertThat(owner.getUser().isAuthed()).isFalse(); softly.assertThat(owner.getUser().isDeleted()).isFalse(); - verify(ownerEventListener).onOwnerRegister(any()); + forceVerify(() -> verify(ownerEventListener).onOwnerRegister(any())); + clear(); } ); } @@ -263,32 +242,28 @@ void register() { } @Test - @DisplayName("사장님이 전화번호를 아이디로 회원가입 요청을 한다.") - void registerByPhoneNumber() { + void 사장님이_전화번호를_아이디로_회원가입_요청을_한다() throws Exception { // when & then - var response = RestAssured - .given() - .body(""" - { - "attachment_urls": [ - { - "file_url": "https://static.koreatech.in/testimage.png" - } - ], - "company_number": "012-34-56789", - "name": "최준호", - "password": "a0240120305812krlakdsflsa;1235", - "phone_number": "01012341234", - "shop_id": null, - "shop_name": "기분좋은 뷔짱" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/owners/register/phone") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + post("/owners/register/phone") + .content(""" + { + "attachment_urls": [ + { + "file_url": "https://static.koreatech.in/testimage.png" + } + ], + "company_number": "012-34-56789", + "name": "최준호", + "password": "a0240120305812krlakdsflsa;1235", + "phone_number": "01012341234", + "shop_id": null, + "shop_name": "기분좋은 뷔짱" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); // when transactionTemplate.executeWithoutResult(status -> { @@ -306,7 +281,8 @@ void registerByPhoneNumber() { .isEqualTo("https://static.koreatech.in/testimage.png"); softly.assertThat(owner.getUser().isAuthed()).isFalse(); softly.assertThat(owner.getUser().isDeleted()).isFalse(); - verify(ownerEventListener).onOwnerRegisterBySms(any()); + forceVerify(() -> verify(ownerEventListener).onOwnerRegisterBySms(any())); + clear(); } ); } @@ -314,120 +290,108 @@ void registerByPhoneNumber() { } @Test - @DisplayName("사장님이 회원가입 요청을 한다 - 첨부파일 이미지 URL이 잘못된 경우 400") - void registerNotAllowedFileUrl() { + void 사장님이_회원가입_요청을_한다_첨부파일_이미지_URL이_잘못된_경우_400() throws Exception { // given - RestAssured - .given() - .body(""" - { - "attachment_urls": [ - { - "file_url": "https://hello.koreatech.in/testimage.png" - } - ], - "company_number": "012-34-56789", - "email": "helloworld@koreatech.ac.kr", - "name": "최준호", - "password": "a0240120305812krlakdsflsa;1235", - "phone_number": "010-0000-0000", - "shop_id": null, - "shop_name": "기분좋은 뷔짱" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/owners/register") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()); + mockMvc.perform( + post("/owners/register") + .content(""" + { + "attachment_urls": [ + { + "file_url": "https://hello.koreatech.in/testimage.png" + } + ], + "company_number": "012-34-56789", + "email": "helloworld@koreatech.ac.kr", + "name": "최준호", + "password": "a0240120305812krlakdsflsa;1235", + "phone_number": "010-0000-0000", + "shop_id": null, + "shop_name": "기분좋은 뷔짱" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("사장님이 회원가입 요청을 한다 - 잘못된 사업자 등록번호인 경우 400") - void registerNotAllowedCompanyNumber() { + void 사장님이_회원가입_요청을_한다_잘못된_사업자_등록번호인_경우_400() throws Exception { // given - RestAssured - .given() - .body(""" - { - "attachment_urls": [ - { - "file_url": "https://static.koreatech.in/testimage.png" - } - ], - "company_number": "8121-34-56789", - "email": "helloworld@koreatech.ac.kr", - "name": "최준호", - "password": "a0240120305812krlakdsflsa;1235", - "phone_number": "01000000000", - "shop_id": null, - "shop_name": "기분좋은 뷔짱" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/owners/register") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()); + mockMvc.perform( + post("/owners/register") + .content(""" + { + "attachment_urls": [ + { + "file_url": "https://static.koreatech.in/testimage.png" + } + ], + "company_number": "8121-34-56789", + "email": "helloworld@koreatech.ac.kr", + "name": "최준호", + "password": "a0240120305812krlakdsflsa;1235", + "phone_number": "01000000000", + "shop_id": null, + "shop_name": "기분좋은 뷔짱" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("사장님이 회원가입 요청을 한다 - 이름이 없는경우 400") - void registerWithoutName() { + void 사장님이_회원가입_요청을_한다_이름이_없는경우_400() throws Exception { // given - RestAssured - .given() - .body(""" - { - "attachment_urls": [ - { - "file_url": "https://static.koreatech.in/testimage.png" - } - ], - "company_number": "011-34-56789", - "email": "helloworld@koreatech.ac.kr", - "name": "", - "password": "a0240120305812krlakdsflsa;1235", - "phone_number": "01000000000", - "shop_id": null, - "shop_name": "기분좋은 뷔짱" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/owners/register") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()); + mockMvc.perform( + post("/owners/register") + .content(""" + { + "attachment_urls": [ + { + "file_url": "https://static.koreatech.in/testimage.png" + } + ], + "company_number": "011-34-56789", + "email": "helloworld@koreatech.ac.kr", + "name": "", + "password": "a0240120305812krlakdsflsa;1235", + "phone_number": "01000000000", + "shop_id": null, + "shop_name": "기분좋은 뷔짱" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("사장님이 회원가입 요청을 한다 - 기존에 존재하는 상점과 함께 회원가입") - void registerWithExistShop() { + void 사장님이_회원가입_요청을_한다_기존에_존재하는_상점과_함께_회원가입() throws Exception { // given Shop shop = shopFixture.마슬랜(null); - RestAssured - .given() - .body(String.format(""" - { - "attachment_urls": [ - { - "file_url": "https://static.koreatech.in/testimage.png" - } - ], - "company_number": "011-34-12312", - "email": "helloworld@koreatech.ac.kr", - "name": "주노", - "password": "a0240120305812krlakdsflsa;1235", - "phone_number": "010-0000-0000", - "shop_id": %d, - "shop_name": "기분좋은 뷔짱" - } - """, shop.getId())) - .contentType(ContentType.JSON) - .when() - .post("/owners/register") - .then() - .statusCode(HttpStatus.OK.value()); + mockMvc.perform( + post("/owners/register") + .content(String.format(""" + { + "attachment_urls": [ + { + "file_url": "https://static.koreatech.in/testimage.png" + } + ], + "company_number": "011-34-12312", + "email": "helloworld@koreatech.ac.kr", + "name": "주노", + "password": "a0240120305812krlakdsflsa;1235", + "phone_number": "010-0000-0000", + "shop_id": %d, + "shop_name": "기분좋은 뷔짱" + } + """, shop.getId())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); Owner owner = ownerRepository.findByCompanyRegistrationNumber("011-34-12312").get(); var ownerShop = ownerShopRedisRepository.findById(owner.getId()); @@ -440,32 +404,29 @@ void registerWithExistShop() { } @Test - @DisplayName("사장님이 회원가입 요청을 한다 - 존재하지 않는 상점과 함께 회원가입") - void registerWithNotExistShop() { + void 사장님이_회원가입_요청을_한다_존재하지_않는_상점과_함께_회원가입() throws Exception { // given - RestAssured - .given() - .body(""" - { - "attachment_urls": [ - { - "file_url": "https://static.koreatech.in/testimage.png" - } - ], - "company_number": "011-34-56789", - "email": "helloworld@koreatech.ac.kr", - "name": "주노", - "password": "a0240120305812krlakdsflsa;1235", - "phone_number": "010-0000-0000", - "shop_id": null, - "shop_name": "기분좋은 뷔짱" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/owners/register") - .then() - .statusCode(HttpStatus.OK.value()); + mockMvc.perform( + post("/owners/register") + .content(""" + { + "attachment_urls": [ + { + "file_url": "https://static.koreatech.in/testimage.png" + } + ], + "company_number": "011-34-56789", + "email": "helloworld@koreatech.ac.kr", + "name": "주노", + "password": "a0240120305812krlakdsflsa;1235", + "phone_number": "010-0000-0000", + "shop_id": null, + "shop_name": "기분좋은 뷔짱" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); Owner owner = ownerRepository.findByCompanyRegistrationNumber("011-34-56789").get(); var ownerShop = ownerShopRedisRepository.findById(owner.getId()); assertSoftly( @@ -478,133 +439,111 @@ void registerWithNotExistShop() { } @Test - @DisplayName("사장님이 회원가입 인증번호를 확인한다") - void ownerCodeVerification() { + void 사장님이_회원가입_인증번호를_확인한다() throws Exception { // given OwnerVerificationStatus verification = OwnerVerificationStatus.of("junho5336@gmail.com", "123456"); ownerVerificationStatusRepository.save(verification); - RestAssured - .given() - .body(""" - { - "address": "junho5336@gmail.com", - "certification_code": "123456" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/owners/verification/code") - .then() - .statusCode(HttpStatus.OK.value()); + mockMvc.perform( + post("/owners/verification/code") + .content(""" + { + "address": "junho5336@gmail.com", + "certification_code": "123456" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); var result = ownerVerificationStatusRepository.findById(verification.getKey()); assertThat(result).isNotPresent(); } @Test - @DisplayName("사장님이 회원가입 인증번호를 확인한다 - 존재하지 않는 이메일로 요청을 보낸다") - void ownerCodeVerificationNotExistEmail() { + void 사장님이_회원가입_확인한다_존재하지_않는_이메일로_요청을_보낸다() throws Exception { // given OwnerVerificationStatus verification = OwnerVerificationStatus.of("junho5336@gmail.com", "123456"); ownerVerificationStatusRepository.save(verification); - RestAssured - .given() - .body(""" - { - "address": "someone@gmail.com", - "certification_code": "123456" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/owners/verification/code") - .then() - .statusCode(HttpStatus.NOT_FOUND.value()); + mockMvc.perform( + post("/owners/verification/code") + .content(""" + { + "address": "someone@gmail.com", + "certification_code": "123456" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNotFound()); } @Test - @DisplayName("사장님이 비밀번호 변경을 위한 인증번호 이메일을 전송을 요청한다") - void sendResetPasswordEmail() { + void 사장님이_비밀번호_변경을_위한_인증번호_이메일을_전송을_요청한다() throws Exception { // given Owner owner = userFixture.현수_사장님(); ownerRepository.save(owner); - RestAssured - .given() - .body(String.format(""" - { - "address": "%s" - } - """, owner.getUser().getEmail()) + mockMvc.perform( + post("/owners/password/reset/verification") + .content(String.format(""" + { + "address": "%s" + } + """, owner.getUser().getEmail())) + .contentType(MediaType.APPLICATION_JSON) ) - .contentType(ContentType.JSON) - .when() - .post("/owners/password/reset/verification") - .then() - .statusCode(HttpStatus.OK.value()); - + .andExpect(status().isOk()); assertThat(ownerVerificationStatusRepository.findById(owner.getUser().getEmail())).isPresent(); } @Test - @DisplayName("사장님이 비밀번호 변경을 위한 인증번호 이메일을 전송을 하루 요청 횟수(5번)를 초과하여 요청한다 - 400에러를 반환한다.") - void sendResetPasswordEmailWithDailyLimit() { + void 사장님이_비밀번호_변경을_위한_인증번호_이메일을_전송을_하루_요청_횟수_5번_를_초과하여_요청한다_400에러를_반환한다() throws Exception { // given int DAILY_LIMIT = 5; Owner owner = userFixture.현수_사장님(); ownerRepository.save(owner); for (int i = 0; i < DAILY_LIMIT; ++i) { - RestAssured - .given() - .body(String.format(""" - { - "address": "%s" - } - """, owner.getUser().getEmail()) + mockMvc.perform( + post("/owners/password/reset/verification") + .content(String.format(""" + { + "address": "%s" + } + """, owner.getUser().getEmail())) + .contentType(MediaType.APPLICATION_JSON) ) - .contentType(ContentType.JSON) - .when() - .post("/owners/password/reset/verification") - .then() - .statusCode(HttpStatus.OK.value()); + .andExpect(status().isOk()); } - - RestAssured - .given() - .body(String.format(""" - { - "address": "%s" - } - """, owner.getUser().getEmail()) + mockMvc.perform( + post("/owners/password/reset/verification") + .content(String.format(""" + { + "address": "%s" + } + """, owner.getUser().getEmail())) + .contentType(MediaType.APPLICATION_JSON) ) - .contentType(ContentType.JSON) - .when() - .post("/owners/password/reset/verification") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()); + .andExpect(status().isBadRequest()); } @Test - @DisplayName("사장님이 인증번호를 확인한다.") - void ownerVerify() { + void 사장님이_인증번호를_확인한다() throws Exception { // given String email = "test@test.com"; String code = "123123"; OwnerVerificationStatus verification = OwnerVerificationStatus.of(email, code); ownerVerificationStatusRepository.save(verification); - RestAssured - .given() - .body(String.format(""" - { - "address": "%s", - "certification_code": "%s" - } - """, email, code) + mockMvc.perform( + post("/owners/password/reset/send") + .content(String.format(""" + { + "address": "%s", + "certification_code": "%s" + } + """, email, code)) + .contentType(MediaType.APPLICATION_JSON) ) - .contentType(ContentType.JSON) - .when() - .post("/owners/password/reset/send") - .then() - .statusCode(HttpStatus.OK.value()); + .andExpect(status().isOk()); + var result = ownerVerificationStatusRepository.findById(email); assertSoftly( softly -> { @@ -615,49 +554,40 @@ void ownerVerify() { } @Test - @DisplayName("사장님이 인증번호를 확인한다. - 중복 시 404를 반환한다.") - void ownerVerifyDuplicated() { + void 사장님이_인증번호를_확인한다_중복_시_404를_반환한다() throws Exception { // given String email = "test@test.com"; String code = "123123"; OwnerVerificationStatus verification = OwnerVerificationStatus.of(email, code); ownerVerificationStatusRepository.save(verification); // when - RestAssured - .given() - .body(String.format(""" - { - "address": "%s", - "certification_code": "%s" - } - """, email, code) + mockMvc.perform( + post("/owners/password/reset/send") + .content(String.format(""" + { + "address": "%s", + "certification_code": "%s" + } + """, email, code)) + .contentType(MediaType.APPLICATION_JSON) ) - .contentType(ContentType.JSON) - .when() - .post("/owners/password/reset/send") - .then() - .statusCode(HttpStatus.OK.value()); + .andExpect(status().isOk()); - // then - RestAssured - .given() - .body(String.format(""" - { - "address": "%s", - "certification_code": "%s" - } - """, email, code) + mockMvc.perform( + post("/owners/password/reset/send") + .content(String.format(""" + { + "address": "%s", + "certification_code": "%s" + } + """, email, code)) + .contentType(MediaType.APPLICATION_JSON) ) - .contentType(ContentType.JSON) - .when() - .post("/owners/password/reset/send") - .then() - .statusCode(HttpStatus.NOT_FOUND.value()); + .andExpect(status().isNotFound()); } @Test - @DisplayName("사장님이 비밀번호를 변경한다.") - void ownerChangePassword() { + void 사장님이_비밀번호를_변경한다() throws Exception { // given User user = userFixture.현수_사장님().getUser(); String code = "123123"; @@ -665,37 +595,29 @@ void ownerChangePassword() { ownerVerificationStatusRepository.save(verification); String password = "asdf1234!"; - RestAssured - .given() - .body(String.format(""" - { - "address": "%s", - "certification_code": "%s" - } - """, user.getEmail(), code) + mockMvc.perform( + post("/owners/password/reset/send") + .content(String.format(""" + { + "address": "%s", + "certification_code": "%s" + } + """, user.getEmail(), code)) + .contentType(MediaType.APPLICATION_JSON) ) - .contentType(ContentType.JSON) - .when() - .post("/owners/password/reset/send") - .then() - .statusCode(HttpStatus.OK.value()); - - // when - RestAssured - .given() - .body(String.format(""" - { - "address": "%s", - "password": "%s" - } - """, user.getEmail(), password) + .andExpect(status().isOk()); + + mockMvc.perform( + put("/owners/password/reset") + .content(String.format(""" + { + "address": "%s", + "password": "%s" + } + """, user.getEmail(), password)) + .contentType(MediaType.APPLICATION_JSON) ) - .contentType(ContentType.JSON) - .when() - .put("/owners/password/reset") - .then() - .statusCode(HttpStatus.OK.value()); - + .andExpect(status().isOk()); // then var result = ownerVerificationStatusRepository.findById(user.getEmail()); User userResult = userRepository.getByEmail(user.getEmail()); @@ -708,135 +630,104 @@ void ownerChangePassword() { } @Test - @DisplayName("사장님이 회원탈퇴를 한다.") - void ownerDelete() { + void 사장님이_회원탈퇴를_한다() throws Exception { // given Owner owner = userFixture.현수_사장님(); String token = userFixture.getToken(owner.getUser()); - // when - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .delete("/user") - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); - + mockMvc.perform( + MockMvcRequestBuilders.delete("/user") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); // then assertThat(userRepository.findById(owner.getId())).isNotPresent(); } @Test - @DisplayName("사업자 등록번호 중복 검증 - 존재하지 않으면 200") - void checkDuplicateCompanyNumber() { + void 사업자_등록번호_중복_검증_존재하지_않으면_200() throws Exception { // when & then - RestAssured - .given() - .queryParam("company_number", "123-45-67190") - .when() - .get("/owners/exists/company-number") - .then() - .statusCode(HttpStatus.OK.value()); + mockMvc.perform( + get("/owners/exists/company-number") + .queryParam("company_number", "123-45-67190") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); } @Test - @DisplayName("사업자 등록번호 중복 검증 - 이미 존재하면 409") - void checkDuplicateCompanyNumberExists() { + void 사업자_등록번호_중복_검증_이미_존재하면_409() throws Exception { // given Owner owner = userFixture.현수_사장님(); // when & then - var response = RestAssured - .given() - .queryParam("company_number", owner.getCompanyRegistrationNumber()) - .when() - .get("/owners/exists/company-number") - .then() - .statusCode(HttpStatus.CONFLICT.value()) - .extract(); - - assertThat(response.body().jsonPath().getString("message")) - .isEqualTo("이미 존재하는 사업자 등록번호입니다."); + mockMvc.perform( + get("/owners/exists/company-number") + .queryParam("company_number", owner.getCompanyRegistrationNumber()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isConflict()); } @Test - @DisplayName("사업자 등록번호 중복 검증 - 값이 존재하지 않으면 400") - void checkDuplicateCompanyNumberNotAccept() { + void 사업자_등록번호_중복_검증_값이_존재하지_않으면_400() throws Exception { // when & then - RestAssured - .given() - .when() - .get("/owners/exists/company-number") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()); + mockMvc.perform( + get("/owners/exists/company-number") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("사업자 등록번호 중복 검증 - 값이 올바르지 않으면 400") - void checkDuplicateCompanyNumberNotMatchedPattern() { + void 사업자_등록번호_중복_검증_값이_올바르지_않으면_400() throws Exception { // when & then - RestAssured - .given() - .queryParam("company_number", "1234567890") - .when() - .get("/owners/exists/company-number") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()); + mockMvc.perform( + get("/owners/exists/company-number") + .queryParam("company_number", "1234567890") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("사장님 아이디(전화번호) 중복 검증 - 존재하지 않으면 200") - void checkExistsPhoneNumber() { - RestAssured - .given() - .param("account", "01012345678") - .when() - .get("/owners/exists/account") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + void 사장님_아이디_전화번호_중복_검증_존재하지_않으면_200() throws Exception { + mockMvc.perform( + get("/owners/exists/account") + .param("account", "01012345678") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); } @Test - @DisplayName("사장님 아이디(전화번호) 중복 검증 - 이미 존재하면 409") - void checkExistsPhoneNumberConflict() { + void 사장님_아이디_전화번호_중복_검증_이미_존재하면_409() throws Exception { Owner owner = userFixture.현수_사장님(); - var response = RestAssured - .given() - .param("account", owner.getAccount()) - .when() - .get("/owners/exists/account") - .then() - .statusCode(HttpStatus.CONFLICT.value()) - .extract(); - - assertThat(response.body().jsonPath().getString("message")) - .contains("이미 존재하는 휴대폰번호입니다."); + mockMvc.perform( + get("/owners/exists/account") + .param("account", owner.getAccount()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isConflict()); } @Test - @DisplayName("사장님 아이디(전화번호) 중복 검증 - 파라미터에 전화번호를 포함하지 않으면 400") - void checkExistsPhoneNumberNull() { - RestAssured - .when() - .get("/owners/exists/account") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); + void 사장님_아이디_전화번호_중복_검증_파라미터에_전화번호를_포함하지_않으면_400() throws Exception { + mockMvc.perform( + get("/owners/exists/account") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("사장님 아이디(전화번호) 중복 검증 - 잘못된 전화번호 형식이면 400") - void checkExistsPhoneNumberWrongFormat() { + void 사장님_아이디_전화번호_중복_검증_잘못된_전화번호_형식이면_400() throws Exception { String phoneNumber = "123123123123"; - RestAssured - .given() - .param("phone_number", phoneNumber) - .when() - .get("/owners/exists/account") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); + mockMvc.perform( + get("/owners/exists/account") + .param("phone_number", phoneNumber) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/OwnerShopApiTest.java b/src/test/java/in/koreatech/koin/acceptance/OwnerShopApiTest.java index d35978a6f..35019bf6a 100644 --- a/src/test/java/in/koreatech/koin/acceptance/OwnerShopApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/OwnerShopApiTest.java @@ -3,20 +3,23 @@ import static java.time.format.DateTimeFormatter.ofPattern; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.hamcrest.Matchers.hasSize; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.time.LocalDate; import java.util.List; import java.util.Optional; import java.util.Set; -import org.assertj.core.api.SoftAssertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.transaction.support.TransactionTemplate; import in.koreatech.koin.AcceptanceTest; @@ -42,11 +45,11 @@ import in.koreatech.koin.fixture.ShopCategoryFixture; import in.koreatech.koin.fixture.ShopFixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; +import jakarta.transaction.Transactional; +@Transactional @SuppressWarnings("NonAsciiCharacters") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class OwnerShopApiTest extends AcceptanceTest { @Autowired @@ -92,8 +95,9 @@ class OwnerShopApiTest extends AcceptanceTest { private MenuCategory menuCategory_메인; private MenuCategory menuCategory_사이드; - @BeforeEach + @BeforeAll void setUp() { + clear(); owner_현수 = userFixture.현수_사장님(); token_현수 = userFixture.getToken(owner_현수.getUser()); owner_준영 = userFixture.준영_사장님(); @@ -106,23 +110,15 @@ void setUp() { } @Test - @DisplayName("사장님의 가게 목록을 조회한다.") - void getOwnerShops() { + void 사장님의_가게_목록을_조회한다() throws Exception { // given - shopFixture.영업중이_아닌_신전_떡볶이(owner_현수); - - // when then - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .when() - .get("/owner/shops") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + shopFixture.배달_안되는_신전_떡볶이(owner_현수); + mockMvc.perform( + get("/owner/shops") + .header("Authorization", "Bearer " + token_현수) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "count": 2, "shops": [ @@ -138,122 +134,107 @@ void getOwnerShops() { } ] } - """); + """) + ); } @Test - @DisplayName("상점을 생성한다.") - void createOwnerShop() { + void 상점을_생성한다() throws Exception { // given - RestAssured - .given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_현수) - .body(String.format(""" - { - "address": "대전광역시 유성구 대학로 291", - "category_ids": [ - %d - ], - "delivery": true, - "delivery_price": 4000, - "description": "테스트 상점2입니다.", - "image_urls": [ - "https://test.com/test1.jpg", - "https://test.com/test2.jpg", - "https://test.com/test3.jpg" - ], - - "name": "테스트 상점2", - "open": [ - { - "close_time": "21:00", - "closed": false, - "day_of_week": "MONDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "TUESDAY", - "open_time": "09:00" - }, + mockMvc.perform( + post("/owner/shops") + .header("Authorization", "Bearer " + token_현수) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" { - "close_time": "21:00", - "closed": false, - "day_of_week": "WEDNESDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "THURSDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "FRIDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "SATURDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "SUNDAY", - "open_time": "09:00" + "address": "대전광역시 유성구 대학로 291", + "category_ids": [ + %d + ], + "delivery": true, + "delivery_price": 4000, + "description": "테스트 상점2입니다.", + "image_urls": [ + "https://test.com/test1.jpg", + "https://test.com/test2.jpg", + "https://test.com/test3.jpg" + ], + "name": "테스트 상점2", + "open": [ + { + "close_time": "21:00", + "closed": false, + "day_of_week": "MONDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "TUESDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "WEDNESDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "THURSDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "FRIDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "SATURDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "SUNDAY", + "open_time": "09:00" + } + ], + "pay_bank": true, + "pay_card": true, + "phone": "010-1234-5678" } - ], - "pay_bank": true, - "pay_card": true, - "phone": "010-1234-5678" - } - """, shopCategory_치킨.getId()) + """, shopCategory_치킨.getId()) + ) ) - .when() - .post("/owner/shops") - .then() - .log().all() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - transactionTemplate.executeWithoutResult(status -> { - List shops = shopRepository.findAllByOwnerId(owner_현수.getId()); - Shop result = shops.get(1); - assertSoftly( - softly -> { - softly.assertThat(result.getAddress()).isEqualTo("대전광역시 유성구 대학로 291"); - softly.assertThat(result.getDeliveryPrice()).isEqualTo(4000); - softly.assertThat(result.getDescription()).isEqualTo("테스트 상점2입니다."); - softly.assertThat(result.getName()).isEqualTo("테스트 상점2"); - softly.assertThat(result.getShopImages()).hasSize(3); - softly.assertThat(result.getShopOpens()).hasSize(7); - softly.assertThat(result.getShopCategories()).hasSize(1); - } - ); - }); + .andExpect(status().isCreated()); + List shops = shopRepository.findAllByOwnerId(owner_현수.getId()); + Shop result = shops.get(1); + assertSoftly( + softly -> { + softly.assertThat(result.getAddress()).isEqualTo("대전광역시 유성구 대학로 291"); + softly.assertThat(result.getDeliveryPrice()).isEqualTo(4000); + softly.assertThat(result.getDescription()).isEqualTo("테스트 상점2입니다."); + softly.assertThat(result.getName()).isEqualTo("테스트 상점2"); + softly.assertThat(result.getShopImages()).hasSize(3); + softly.assertThat(result.getShopOpens()).hasSize(7); + softly.assertThat(result.getShopCategories()).hasSize(1); + System.out.println("dsa"); + } + ); } @Test - @DisplayName("상점 사장님이 특정 상점 조회") - void getShop() { - // given - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .when() - .get("/owner/shops/{shopId}", shop_마슬랜.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 상점_사장님이_특정_상점_조회() throws Exception { + mockMvc.perform( + get("/owner/shops/{shopId}", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_현수) + ).andExpect(status().isOk()) + .andExpect(content().json(""" { "address": "천안시 동남구 병천면 1600", "delivery": true, @@ -293,33 +274,25 @@ void getShop() { "pay_card": true, "phone": "010-7574-1212", "shop_categories": [ - ], "updated_at": "2024-01-15", "is_event": false, "bank": "국민", "account_number": "01022595923" } - """); + """)); } @Test - @DisplayName("특정 상점의 모든 메뉴를 조회한다.") - void findOwnerShopMenu() { - // given + void 특정_상점의_모든_메뉴를_조회한다() throws Exception { menuFixture.짜장면_옵션메뉴(shop_마슬랜, menuCategory_메인); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .param("shopId", shop_마슬랜.getId()) - .when() - .get("/owner/shops/menus") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/owner/shops/menus") + .header("Authorization", "Bearer " + token_현수) + .param("shopId", shop_마슬랜.getId().toString()) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "count": 1, "menu_categories": [ @@ -354,26 +327,20 @@ void findOwnerShopMenu() { ], "updated_at": "2024-01-15" } - """); + """)); } @Test - @DisplayName("사장님이 자신의 상점 메뉴 카테고리들을 조회한다.") - void findOwnerMenuCategories() { + void 사장님이_자신의_상점_메뉴_카테고리들을_조회한다() throws Exception { // given menuFixture.짜장면_단일메뉴(shop_마슬랜, menuCategory_메인); - - var response = RestAssured - .given() - .param("shopId", shop_마슬랜.getId()) - .header("Authorization", "Bearer " + token_현수) - .when() - .get("/owner/shops/menus/categories") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/owner/shops/menus/categories") + .header("Authorization", "Bearer " + token_현수) + .param("shopId", shop_마슬랜.getId().toString()) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "count": 2, "menu_categories": [ @@ -387,178 +354,137 @@ void findOwnerMenuCategories() { } ] } - """); + """)); } @Test - @DisplayName("사장님이 자신의 상점의 특정 메뉴를 조회한다.") - void findMenuShopOwner() { + void 사장님이_자신의_상점의_특정_메뉴를_조회한다() throws Exception { // given Menu menu = menuFixture.짜장면_옵션메뉴(shop_마슬랜, menuCategory_메인); - - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .when() - .get("/owner/shops/menus/{menuId}", menu.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - SoftAssertions.assertSoftly( - softly -> { - softly.assertThat(response.body().jsonPath().getInt("id")).isEqualTo(menu.getId()); - - softly.assertThat(response.body().jsonPath().getInt("shop_id")).isEqualTo(menu.getShopId()); - softly.assertThat(response.body().jsonPath().getString("name")).isEqualTo(menu.getName()); - softly.assertThat(response.body().jsonPath().getBoolean("is_hidden")).isEqualTo(menu.isHidden()); - - softly.assertThat(response.body().jsonPath().getBoolean("is_single")).isFalse(); - softly.assertThat((Integer)response.body().jsonPath().get("single_price")).isNull(); - softly.assertThat(response.body().jsonPath().getList("option_prices")).hasSize(2); - softly.assertThat(response.body().jsonPath().getString("description")).isEqualTo(menu.getDescription()); - softly.assertThat(response.body().jsonPath().getList("category_ids")) - .hasSize(menu.getMenuCategoryMaps().size()); - softly.assertThat(response.body().jsonPath().getList("image_urls")) - .hasSize(menu.getMenuImages().size()); - } - ); + mockMvc.perform( + get("/owner/shops/menus/{menuId}", menu.getId()) + .header("Authorization", "Bearer " + token_현수) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(menu.getId())) + .andExpect(jsonPath("$.shop_id").value(menu.getShopId())) + .andExpect(jsonPath("$.name").value(menu.getName())) + .andExpect(jsonPath("$.is_hidden").value(menu.isHidden())) + .andExpect(jsonPath("$.is_single").value(false)) + .andExpect(jsonPath("$.single_price").doesNotExist()) + .andExpect(jsonPath("$.option_prices", hasSize(2))) + .andExpect(jsonPath("$.description").value(menu.getDescription())) + .andExpect(jsonPath("$.category_ids", hasSize(menu.getMenuCategoryMaps().size()))) + .andExpect(jsonPath("$.image_urls", hasSize(menu.getMenuImages().size()))); } @Test - @DisplayName("권한이 없는 상점 사장님이 특정 상점 조회") - void ownerCannotQueryOtherStoresWithoutPermission() { + void 권한이_없는_상점_사장님이_특정_상점_조회() throws Exception { // given - RestAssured - .given() - .header("Authorization", "Bearer " + token_준영) - .when() - .get("/owner/shops/{shopId}", shop_마슬랜.getId()) - .then() - .statusCode(HttpStatus.FORBIDDEN.value()) - .extract(); + mockMvc.perform( + get("/owner/shops/{shopId}", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_준영) + ) + .andExpect(status().isForbidden()); } @Test - @DisplayName("사장님이 메뉴 카테고리를 삭제한다.") - void deleteMenuCategory() { + void 사장님이_메뉴_카테고리를_삭제한다() throws Exception { // when & then - RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .when() - .delete("/owner/shops/menus/categories/{categoryId}", menuCategory_메인.getId()) - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); - + mockMvc.perform( + delete("/owner/shops/menus/categories/{categoryId}", menuCategory_메인.getId()) + .header("Authorization", "Bearer " + token_현수) + ) + .andExpect(status().isNoContent()); assertThat(menuCategoryRepository.findById(menuCategory_메인.getId())).isNotPresent(); } @Test - @DisplayName("사장님이 메뉴를 삭제한다.") - void deleteMenu() { + void 사장님이_메뉴를_삭제한다() throws Exception { // given Menu menu = menuFixture.짜장면_단일메뉴(shop_마슬랜, menuCategory_메인); - - RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .when() - .delete("/owner/shops/menus/{menuId}", menu.getId()) - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); - + mockMvc.perform( + delete("/owner/shops/menus/{menuId}", menu.getId()) + .header("Authorization", "Bearer " + token_현수) + ) + .andExpect(status().isNoContent()); assertThat(menuRepository.findById(menu.getId())).isNotPresent(); } @Test - @DisplayName("사장님이 옵션이 여러개인 메뉴를 추가한다.") - void createManyOptionMenu() { + void 사장님이_옵션이_여러개인_메뉴를_추가한다() throws Exception { // given MenuCategory menuCategory = menuCategory_메인; - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "category_ids": [ - %s - ], - "description": "테스트메뉴입니다.", - "image_urls": [ - "https://test-image.com/짜장면.jpg" - ], - "is_single": false, - "name": "짜장면", - "option_prices": [ - { - "option": "중", - "price": 10000 - }, - { - "option": "소", - "price": 5000 - } - ] - } - """, menuCategory.getId())) - .when() - .post("/owner/shops/{id}/menus", shop_마슬랜.getId()) - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - transactionTemplate.executeWithoutResult(status -> { - Menu menu = menuRepository.getById(1); - assertSoftly( - softly -> { - List menuCategoryMaps = menu.getMenuCategoryMaps(); - List menuOptions = menu.getMenuOptions(); - List menuImages = menu.getMenuImages(); - softly.assertThat(menu.getDescription()).isEqualTo("테스트메뉴입니다."); - softly.assertThat(menu.getName()).isEqualTo("짜장면"); - softly.assertThat(menuImages.get(0).getImageUrl()).isEqualTo("https://test-image.com/짜장면.jpg"); - softly.assertThat(menuCategoryMaps.get(0).getMenuCategory().getId()).isEqualTo(1); - softly.assertThat(menuOptions).hasSize(2); - } - ); - }); + mockMvc.perform( + post("/owner/shops/{id}/menus", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_현수) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "category_ids": [ + %s + ], + "description": "테스트메뉴입니다.", + "image_urls": [ + "https://test-image.com/짜장면.jpg" + ], + "is_single": false, + "name": "짜장면", + "option_prices": [ + { + "option": "중", + "price": 10000 + }, + { + "option": "소", + "price": 5000 + } + ] + } + """, menuCategory.getId())) + ) + .andExpect(status().isCreated()); + Menu menu = menuRepository.getById(1); + assertSoftly( + softly -> { + List menuCategoryMaps = menu.getMenuCategoryMaps(); + List menuOptions = menu.getMenuOptions(); + List menuImages = menu.getMenuImages(); + softly.assertThat(menu.getDescription()).isEqualTo("테스트메뉴입니다."); + softly.assertThat(menu.getName()).isEqualTo("짜장면"); + softly.assertThat(menuImages.get(0).getImageUrl()).isEqualTo("https://test-image.com/짜장면.jpg"); + softly.assertThat(menuCategoryMaps.get(0).getMenuCategory().getId()).isEqualTo(1); + softly.assertThat(menuOptions).hasSize(2); + } + ); } @Test - @DisplayName("사장님이 옵션이 한개인 메뉴를 추가한다.") - void createOneOptionMenu() { + void 사장님이_옵션이_한개인_메뉴를_추가한다() throws Exception { // given MenuCategory menuCategory = menuCategory_메인; - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "category_ids": [ - %s - ], - "description": "테스트메뉴입니다.", - "image_urls": [ - "https://test-image.com/짜장면.jpg" - ], - "is_single": true, - "name": "짜장면", - "option_prices": null, - "single_price": 10000 - } - """, menuCategory.getId())) - .when() - .post("/owner/shops/{id}/menus", shop_마슬랜.getId()) - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - transactionTemplate.executeWithoutResult(status -> { + mockMvc.perform( + post("/owner/shops/{id}/menus", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_현수) + .content(String.format(""" + { + "category_ids": [ + %s + ], + "description": "테스트메뉴입니다.", + "image_urls": [ + "https://test-image.com/짜장면.jpg" + ], + "is_single": true, + "name": "짜장면", + "option_prices": null, + "single_price": 10000 + } + """, menuCategory.getId())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); + transactionTemplate.executeWithoutResult(execute -> { Menu menu = menuRepository.getById(1); assertSoftly( softly -> { @@ -567,10 +493,8 @@ void createOneOptionMenu() { List menuImages = menu.getMenuImages(); softly.assertThat(menu.getDescription()).isEqualTo("테스트메뉴입니다."); softly.assertThat(menu.getName()).isEqualTo("짜장면"); - softly.assertThat(menuImages.get(0).getImageUrl()).isEqualTo("https://test-image.com/짜장면.jpg"); softly.assertThat(menuCategoryMaps.get(0).getMenuCategory().getId()).isEqualTo(1); - softly.assertThat(menuOptions.get(0).getPrice()).isEqualTo(10000); } ); @@ -578,58 +502,48 @@ void createOneOptionMenu() { } @Test - @DisplayName("사장님이 메뉴 카테고리를 추가한다.") - void createMenuCategory() { - // given - RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "name": "대박메뉴" - } - """)) - .when() - .post("/owner/shops/{id}/menus/categories", shop_마슬랜.getId()) - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - + void 사장님이_메뉴_카테고리를_추가한다() throws Exception { + menuFixture.짜장면_단일메뉴(shop_마슬랜, menuCategory_메인); + mockMvc.perform( + post("/owner/shops/{id}/menus/categories", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_현수) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "name": "대박메뉴" + } + """) + ) + .andExpect(status().isCreated()); var menuCategories = menuCategoryRepository.findAllByShopId(shop_마슬랜.getId()); - assertThat(menuCategories).anyMatch(menuCategory -> "대박메뉴".equals(menuCategory.getName())); } @Test - @DisplayName("사장님이 단일 메뉴로 수정한다.") - void modifyOneMenu() { + void 사장님이_단일_메뉴로_수정한다() throws Exception { // given Menu menu = menuFixture.짜장면_단일메뉴(shop_마슬랜, menuCategory_메인); - RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "category_ids": [ - %d - ], - "description": "테스트메뉴수정", - "image_urls": [ - "https://test-image.net/테스트메뉴.jpeg" - ], - "is_single": true, - "name": "짜장면2", - "single_price": 10000 - } - """, shopCategory_일반.getId())) - .when() - .put("/owner/shops/menus/{menuId}", menu.getId()) - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + mockMvc.perform( + put("/owner/shops/menus/{menuId}", menu.getId()) + .header("Authorization", "Bearer " + token_현수) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "category_ids": [ + %d + ], + "description": "테스트메뉴수정", + "image_urls": [ + "https://test-image.net/테스트메뉴.jpeg" + ], + "is_single": true, + "name": "짜장면2", + "single_price": 10000 + } + """, shopCategory_일반.getId())) + ) + .andExpect(status().isCreated()); transactionTemplate.executeWithoutResult(status -> { Menu result = menuRepository.getById(1); @@ -640,55 +554,49 @@ void modifyOneMenu() { List menuImages = result.getMenuImages(); softly.assertThat(result.getDescription()).isEqualTo("테스트메뉴수정"); softly.assertThat(result.getName()).isEqualTo("짜장면2"); - softly.assertThat(menuImages.get(0).getImageUrl()).isEqualTo("https://test-image.net/테스트메뉴.jpeg"); softly.assertThat(menuCategoryMaps.get(0).getMenuCategory().getId()).isEqualTo(2); - softly.assertThat(menuOptions.get(0).getPrice()).isEqualTo(10000); - } ); }); } @Test - @DisplayName("사장님이 여러옵션을 가진 메뉴로 수정한다.") - void modifyManyOptionMenu() { + void 사장님이_여러옵션을_가진_메뉴로_수정한다() throws Exception { // given Menu menu = menuFixture.짜장면_옵션메뉴(shop_마슬랜, menuCategory_메인); - RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "category_ids": [ - %d, %d - ], - "description": "테스트메뉴입니다.", - "image_urls": [ - "https://fixed-testimage.com/수정된짜장면.png" - ], - "is_single": false, - "name": "짜장면", - "option_prices": [ - { - "option": "중", - "price": 10000 - }, - { - "option": "소", - "price": 5000 - } - ] - } - """, menuCategory_메인.getId(), menuCategory_사이드.getId()) + + mockMvc.perform( + put("/owner/shops/menus/{menuId}", menu.getId()) + .header("Authorization", "Bearer " + token_현수) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "category_ids": [ + %d, %d + ], + "description": "테스트메뉴입니다.", + "image_urls": [ + "https://fixed-testimage.com/수정된짜장면.png" + ], + "is_single": false, + "name": "짜장면", + "option_prices": [ + { + "option": "중", + "price": 10000 + }, + { + "option": "소", + "price": 5000 + } + ] + } + """, menuCategory_메인.getId(), menuCategory_사이드.getId()) + ) ) - .when() - .put("/owner/shops/menus/{menuId}", menu.getId()) - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + .andExpect(status().isCreated()); transactionTemplate.executeWithoutResult(status -> { Menu result = menuRepository.getById(1); @@ -709,51 +617,48 @@ void modifyManyOptionMenu() { } @Test - @DisplayName("사장님이 상점을 수정한다.") - void modifyShop() { + void 사장님이_상점을_수정한다() throws Exception { // given - RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "address": "충청남도 천안시 동남구 병천면 충절로 1600", - "category_ids": [ - %d, %d - ], - "delivery": false, - "delivery_price": 1000, - "description": "이번주 전 메뉴 10%% 할인 이벤트합니다.", - "image_urls": [ - "https://fixed-shopimage.com/수정된_상점_이미지.png" - ], - "name": "써니 숯불 도시락", - "open": [ - { - "close_time": "22:30", - "closed": false, - "day_of_week": "MONDAY", - "open_time": "10:00" - }, - { - "close_time": "23:30", - "closed": true, - "day_of_week": "SUNDAY", - "open_time": "11:00" - } - ], - "pay_bank": true, - "pay_card": true, - "phone": "041-123-4567" - } - """, shopCategory_일반.getId(), shopCategory_치킨.getId() - )) - .when() - .put("/owner/shops/{id}", shop_마슬랜.getId()) - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + mockMvc.perform( + put("/owner/shops/{id}", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_현수) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "address": "충청남도 천안시 동남구 병천면 충절로 1600", + "category_ids": [ + %d, %d + ], + "delivery": false, + "delivery_price": 1000, + "description": "이번주 전 메뉴 10%% 할인 이벤트합니다.", + "image_urls": [ + "https://fixed-shopimage.com/수정된_상점_이미지.png" + ], + "name": "써니 숯불 도시락", + "open": [ + { + "close_time": "22:30", + "closed": false, + "day_of_week": "MONDAY", + "open_time": "10:00" + }, + { + "close_time": "23:30", + "closed": true, + "day_of_week": "SUNDAY", + "open_time": "11:00" + } + ], + "pay_bank": true, + "pay_card": true, + "phone": "041-123-4567" + } + """, shopCategory_일반.getId(), shopCategory_치킨.getId() + ) + ) + ) + .andExpect(status().isCreated()); transactionTemplate.executeWithoutResult(status -> { Shop result = shopRepository.getById(1); @@ -793,337 +698,294 @@ void modifyShop() { } @Test - @DisplayName("사장님이 단일 메뉴로 수정한다. - 가격 옵션이 null이 아니면 400 에러") - void modifySingleMenuWithEmptyOptionPrices() { + void 사장님이_단일_메뉴로_수정한다_가격_옵션이_null이_아니면_400_에러() throws Exception { // given Menu menu = menuFixture.짜장면_단일메뉴(shop_마슬랜, menuCategory_메인); - RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "category_ids": [ - %d - ], - "description": "테스트메뉴수정", - "image_urls": [ - "https://test-image.net/테스트메뉴.jpeg" - ], - "is_single": true, - "name": "짜장면2", - "single_price": 10000, - "option_prices": [] - } - """, shopCategory_일반.getId())) - .when() - .put("/owner/shops/menus/{menuId}", menu.getId()) - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); + mockMvc.perform( + put("/owner/shops/menus/{menuId}", menu.getId()) + .header("Authorization", "Bearer " + token_현수) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "category_ids": [ + %d + ], + "description": "테스트메뉴수정", + "image_urls": [ + "https://test-image.net/테스트메뉴.jpeg" + ], + "is_single": true, + "name": "짜장면2", + "single_price": 10000, + "option_prices": [] + } + """, shopCategory_일반.getId()) + ) + ) + .andExpect(status().isBadRequest()); } - @Test - @DisplayName("권한이 없는 상점 사장님이 특정 카테고리 조회한다.") - void ownerCannotQueryOtherCategoriesWithoutPermission() { + void 권한이_없는_상점_사장님이_특정_카테고리_조회한다() throws Exception { // given - RestAssured - .given() - .param("shopId", 1) - .header("Authorization", "Bearer " + token_준영) - .when() - .get("/owner/shops/menus/categories") - .then() - .statusCode(HttpStatus.FORBIDDEN.value()) - .extract(); + mockMvc.perform( + get("/owner/shops/menus/categories") + .header("Authorization", "Bearer " + token_준영) + .param("shopId", shop_마슬랜.getId().toString()) + ) + .andExpect(status().isForbidden()); } @Test - @DisplayName("권한이 없는 상점 사장님이 특정 메뉴 조회한다.") - void ownerCannotQueryOtherMenusWithoutPermission() { + void 권한이_없는_상점_사장님이_특정_메뉴_조회한다() throws Exception { + menuFixture.짜장면_단일메뉴(shop_마슬랜, menuCategory_메인); // given - RestAssured - .given() - .header("Authorization", "Bearer " + token_준영) - .param("shopId", 1) - .when() - .get("/owner/shops/menus") - .then() - .statusCode(HttpStatus.FORBIDDEN.value()) - .extract(); + mockMvc.perform( + get("/owner/shops/menus/{menuId}", 1) + .header("Authorization", "Bearer " + token_준영) + ) + .andExpect(status().isForbidden()); } @Test - @DisplayName("권한이 없는 사장님이 메뉴 카테고리를 삭제한다.") - void ownerCannotDeleteOtherCategoriesWithoutPermission() { + void 권한이_없는_사장님이_메뉴_카테고리를_삭제한다() throws Exception { // given MenuCategory menuCategory = menuCategoryFixture.세트메뉴(shop_마슬랜); - RestAssured - .given() - .header("Authorization", "Bearer " + token_준영) - .when() - .delete("/owner/shops/menus/categories/{categoryId}", menuCategory.getId()) - .then() - .statusCode(HttpStatus.FORBIDDEN.value()) - .extract(); + mockMvc.perform( + delete("/owner/shops/menus/categories/{categoryId}", menuCategory.getId()) + .header("Authorization", "Bearer " + token_준영) + ) + .andExpect(status().isForbidden()); } @Test - @DisplayName("권한이 없는 사장님이 메뉴를 삭제한다.") - void ownerCannotDeleteOtherMenusWithoutPermission() { + void 권한이_없는_사장님이_메뉴를_삭제한다() throws Exception { // given Menu menu = menuFixture.짜장면_단일메뉴(shop_마슬랜, menuCategory_메인); - - RestAssured - .given() - .header("Authorization", "Bearer " + token_준영) - .when() - .delete("/owner/shops/menus/{menuId}", menu.getId()) - .then() - .statusCode(HttpStatus.FORBIDDEN.value()) - .extract(); + mockMvc.perform( + delete("/owner/shops/menus/{menuId}", menu.getId()) + .header("Authorization", "Bearer " + token_준영) + ) + .andExpect(status().isForbidden()); } @Test - @DisplayName("사장님이 이벤트를 추가한다.") - void ownerShopCreateEvent() { + void 사장님이_이벤트를_추가한다() throws Exception { LocalDate startDate = LocalDate.now(); LocalDate endDate = startDate.plusDays(10); - RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "title": "감성떡볶이 이벤트합니다!", - "content": "테스트 이벤트입니다.", - "thumbnail_images": [ - "https://test.com/test1.jpg" - ], - "start_date": "%s", - "end_date": "%s" - } - """, - startDate.format(ofPattern("yyyy-MM-dd")), - endDate.format(ofPattern("yyyy-MM-dd")) - )) - .when() - .post("/owner/shops/{shopId}/event", shop_마슬랜.getId()) - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - transactionTemplate.executeWithoutResult(status -> { - EventArticle eventArticle = eventArticleRepository.getById(1); - assertSoftly( - softly -> { - softly.assertThat(eventArticle.getShop().getId()).isEqualTo(1); - softly.assertThat(eventArticle.getTitle()).isEqualTo("감성떡볶이 이벤트합니다!"); - softly.assertThat(eventArticle.getContent()).isEqualTo("테스트 이벤트입니다."); - softly.assertThat(eventArticle.getThumbnailImages().get(0).getThumbnailImage()) - .isEqualTo("https://test.com/test1.jpg"); - softly.assertThat(eventArticle.getStartDate()).isEqualTo(startDate); - softly.assertThat(eventArticle.getEndDate()).isEqualTo(endDate); - } - ); + try { + mockMvc.perform( + post("/owner/shops/{shopId}/event", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_현수) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "title": "감성떡볶이 이벤트합니다!", + "content": "테스트 이벤트입니다.", + "thumbnail_images": [ + "https://test.com/test1.jpg" + ], + "start_date": "%s", + "end_date": "%s" + } + """, + startDate.format(ofPattern("yyyy-MM-dd")), + endDate.format(ofPattern("yyyy-MM-dd")) + ) + ) + ) + .andExpect(status().isCreated()); + } catch (Exception e) { + throw new RuntimeException(e); + } }); - verify(shopEventListener).onShopEventCreate(any()); + EventArticle eventArticle = eventArticleRepository.getById(1); + assertSoftly( + softly -> { + softly.assertThat(eventArticle.getShop().getId()).isEqualTo(1); + softly.assertThat(eventArticle.getTitle()).isEqualTo("감성떡볶이 이벤트합니다!"); + softly.assertThat(eventArticle.getContent()).isEqualTo("테스트 이벤트입니다."); + softly.assertThat(eventArticle.getThumbnailImages().get(0).getThumbnailImage()) + .isEqualTo("https://test.com/test1.jpg"); + softly.assertThat(eventArticle.getStartDate()).isEqualTo(startDate); + softly.assertThat(eventArticle.getEndDate()).isEqualTo(endDate); + } + ); + forceVerify(() -> verify(shopEventListener, times(1)).onShopEventCreate(any())); + clear(); + setUp(); } @Test - @DisplayName("사장님이 이벤트를 수정한다.") - void ownerShopModifyEvent() { + void 사장님이_이벤트를_수정한다() throws Exception { LocalDate startDate = LocalDate.now(); LocalDate endDate = startDate.plusDays(10); EventArticle eventArticle = eventArticleFixture.할인_이벤트(shop_마슬랜, startDate, endDate); - RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "title": "감성떡볶이 이벤트합니다!", - "content": "테스트 이벤트입니다.", - "thumbnail_images": [ - "https://test.com/test1.jpg" - ], - "start_date": "%s", - "end_date": "%s" - } - """, - startDate, - endDate)) - .when() - .put("/owner/shops/{shopId}/events/{eventId}", shop_마슬랜.getId(), eventArticle.getId()) - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + mockMvc.perform( + put("/owner/shops/{shopId}/events/{eventId}", shop_마슬랜.getId(), eventArticle.getId()) + .header("Authorization", "Bearer " + token_현수) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "title": "감성떡볶이 이벤트합니다!", + "content": "테스트 이벤트입니다.", + "thumbnail_images": [ + "https://test.com/test1.jpg" + ], + "start_date": "%s", + "end_date": "%s" + } + """, + startDate, + endDate) + ) + ) + .andExpect(status().isCreated()); - transactionTemplate.executeWithoutResult(status -> { - EventArticle result = eventArticleRepository.getById(eventArticle.getId()); - assertSoftly( - softly -> { - softly.assertThat(result.getShop().getId()).isEqualTo(1); - softly.assertThat(result.getTitle()).isEqualTo("감성떡볶이 이벤트합니다!"); - softly.assertThat(result.getContent()).isEqualTo("테스트 이벤트입니다."); - softly.assertThat(result.getThumbnailImages().get(0).getThumbnailImage()) - .isEqualTo("https://test.com/test1.jpg"); - softly.assertThat(result.getStartDate()).isEqualTo(startDate); - softly.assertThat(result.getEndDate()).isEqualTo(endDate); - } - ); - }); + EventArticle result = eventArticleRepository.getById(eventArticle.getId()); + assertSoftly( + softly -> { + softly.assertThat(result.getShop().getId()).isEqualTo(1); + softly.assertThat(result.getTitle()).isEqualTo("감성떡볶이 이벤트합니다!"); + softly.assertThat(result.getContent()).isEqualTo("테스트 이벤트입니다."); + softly.assertThat(result.getThumbnailImages().get(0).getThumbnailImage()) + .isEqualTo("https://test.com/test1.jpg"); + softly.assertThat(result.getStartDate()).isEqualTo(startDate); + softly.assertThat(result.getEndDate()).isEqualTo(endDate); + } + ); } @Test - @DisplayName("사장님이 이벤트를 삭제한다.") - void ownerShopDeleteEvent() { + void 사장님이_이벤트를_삭제한다() throws Exception { EventArticle eventArticle = eventArticleFixture.할인_이벤트( shop_마슬랜, LocalDate.of(2024, 10, 24), LocalDate.of(2024, 10, 26) ); - - RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .contentType(ContentType.JSON) - .when() - .delete("/owner/shops/{shopId}/events/{eventId}", shop_마슬랜.getId(), eventArticle.getId()) - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); - + mockMvc.perform( + delete("/owner/shops/{shopId}/events/{eventId}", shop_마슬랜.getId(), eventArticle.getId()) + .header("Authorization", "Bearer " + token_현수) + ) + .andExpect(status().isNoContent()); Optional modifiedEventArticle = eventArticleRepository.findById(eventArticle.getId()); assertThat(modifiedEventArticle).isNotPresent(); } @Test - void 이미지_url의_요소가_공백인_채로_상점을_수정하면_400에러가_반환된다() { + void 이미지_url의_요소가_공백인_채로_상점을_수정하면_400에러가_반환된다() throws Exception { // given - RestAssured - .given() - .header("Authorization", "Bearer " + token_현수) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "address": "충청남도 천안시 동남구 병천면 충절로 1600", - "category_ids": [ - %d, %d - ], - "delivery": false, - "delivery_price": 1000, - "description": "이번주 전 메뉴 10%% 할인 이벤트합니다.", - "image_urls": [ - "" - ], - "name": "써니 숯불 도시락", - "open": [ - { - "close_time": "22:30", - "closed": false, - "day_of_week": "MONDAY", - "open_time": "10:00" - }, - { - "close_time": "23:30", - "closed": true, - "day_of_week": "SUNDAY", - "open_time": "11:00" - } - ], - "pay_bank": true, - "pay_card": true, - "phone": "041-123-4567" - } - """, shopCategory_일반.getId(), shopCategory_치킨.getId() - )) - .when() - .put("/owner/shops/{id}", shop_마슬랜.getId()) - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); - + mockMvc.perform( + put("/owner/shops/{shopId}", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_현수) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "address": "충청남도 천안시 동남구 병천면 충절로 1600", + "category_ids": [ + %d, %d + ], + "delivery": false, + "delivery_price": 1000, + "description": "이번주 전 메뉴 10%% 할인 이벤트합니다.", + "image_urls": [ + "" + ], + "name": "써니 숯불 도시락", + "open": [ + { + "close_time": "22:30", + "closed": false, + "day_of_week": "MONDAY", + "open_time": "10:00" + }, + { + "close_time": "23:30", + "closed": true, + "day_of_week": "SUNDAY", + "open_time": "11:00" + } + ], + "pay_bank": true, + "pay_card": true, + "phone": "041-123-4567" + } + """, shopCategory_일반.getId(), shopCategory_치킨.getId() + ))) + .andExpect(status().isBadRequest()); } @Test - void 이미지_url의_요소가_공백인_채로_상점을_생성하면_400에러가_반환된다() { + void 이미지_url의_요소가_공백인_채로_상점을_생성하면_400에러가_반환된다() throws Exception { // given - RestAssured - .given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_현수) - .body(String.format(""" - { - "address": "대전광역시 유성구 대학로 291", - "category_ids": [ - %d - ], - "delivery": true, - "delivery_price": 4000, - "description": "테스트 상점2입니다.", - "image_urls": [ - "", - ], - - "name": "테스트 상점2", - "open": [ - { - "close_time": "21:00", - "closed": false, - "day_of_week": "MONDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "TUESDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "WEDNESDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "THURSDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "FRIDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "SATURDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "SUNDAY", - "open_time": "09:00" - } - ], - "pay_bank": true, - "pay_card": true, - "phone": "010-1234-5678" - } - """, shopCategory_치킨.getId()) - ) - .when() - .post("/owner/shops") - .then() - .log().all() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); + mockMvc.perform(post("/owner/shops") + .header("Authorization", "Bearer " + token_현수) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "address": "대전광역시 유성구 대학로 291", + "category_ids": [ + %d + ], + "delivery": true, + "delivery_price": 4000, + "description": "테스트 상점2입니다.", + "image_urls": [ + "", + ], + "name": "테스트 상점2", + "open": [ + { + "close_time": "21:00", + "closed": false, + "day_of_week": "MONDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "TUESDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "WEDNESDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "THURSDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "FRIDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "SATURDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "SUNDAY", + "open_time": "09:00" + } + ], + "pay_bank": true, + "pay_card": true, + "phone": "010-1234-5678" + } + """, shopCategory_치킨.getId()) + )) + .andExpect(status().isBadRequest()); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/ShopApiTest.java b/src/test/java/in/koreatech/koin/acceptance/ShopApiTest.java index 95f79b5d3..ec4e2144a 100644 --- a/src/test/java/in/koreatech/koin/acceptance/ShopApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/ShopApiTest.java @@ -2,21 +2,20 @@ import static in.koreatech.koin.domain.shop.model.ReportStatus.DISMISSED; import static in.koreatech.koin.domain.shop.model.ReportStatus.UNHANDLED; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.time.LocalDate; -import java.time.LocalDateTime; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.owner.model.Owner; import in.koreatech.koin.domain.shop.model.Menu; -import in.koreatech.koin.domain.shop.model.ReportStatus; import in.koreatech.koin.domain.shop.model.Shop; import in.koreatech.koin.domain.shop.model.ShopReview; import in.koreatech.koin.domain.user.model.Student; @@ -28,11 +27,10 @@ import in.koreatech.koin.fixture.ShopReviewFixture; import in.koreatech.koin.fixture.ShopReviewReportFixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; +@Transactional @SuppressWarnings("NonAsciiCharacters") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class ShopApiTest extends AcceptanceTest { @Autowired @@ -64,29 +62,22 @@ class ShopApiTest extends AcceptanceTest { private Student 익명_학생; - @BeforeEach + @BeforeAll void setUp() { + clear(); owner = userFixture.준영_사장님(); 마슬랜 = shopFixture.마슬랜(owner); 익명_학생 = userFixture.익명_학생(); } @Test - @DisplayName("옵션이 하나 있는 상점의 메뉴를 조회한다.") - void findMenuSingleOption() { - // given + void 옵션이_하나_있는_상점의_메뉴를_조회한다() throws Exception { Menu menu = menuFixture.짜장면_단일메뉴(마슬랜, menuCategoryFixture.메인메뉴(마슬랜)); - - var response = RestAssured - .given() - .when() - .get("/shops/{shopId}/menus/{menuId}", menu.getShopId(), menu.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/shops/{shopId}/menus/{menuId}", menu.getShopId(), menu.getId()) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "id": 1, "shop_id": 1, @@ -104,26 +95,19 @@ void findMenuSingleOption() { "https://test.com/짜장면22.jpg" ] } - """ + """) ); } @Test - @DisplayName("옵션이 여러 개 있는 상점의 메뉴를 조회한다.") - void findMenuMultipleOption() { - // given + void 옵션이_여러_개_있는_상점의_메뉴를_조회한다() throws Exception { Menu menu = menuFixture.짜장면_옵션메뉴(마슬랜, menuCategoryFixture.메인메뉴(마슬랜)); - var response = RestAssured - .given() - .when() - .get("/shops/{shopId}/menus/{menuId}", menu.getShopId(), menu.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/shops/{shopId}/menus/{menuId}", menu.getShopId(), menu.getId()) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "id": 1, "shop_id": 1, @@ -150,241 +134,178 @@ void findMenuMultipleOption() { "https://test.com/짜장면22.jpg" ] } - HTTP/1.1 200\s - Vary: Origin - Vary: Access-Control-Request-Method - Vary: Access-Control-Request-Headers - Content-Type: application/json - Transfer-Encoding: chunked - Date: Mon, 22 Apr 2024 15:59:58 GMT - Keep-Alive: timeout=60 - Connection: keep-alive - - { - "id": 1, - "shop_id": 1, - "name": "짜장면", - "is_hidden": false, - "is_single": false, - "single_price": null, - "option_prices": [ - { - "option": "곱빼기", - "price": 7500 - }, - { - "option": "일반", - "price": 7000 - } - ], - "description": "맛있는 짜장면", - "category_ids": [ - 1 - ], - "image_urls": [ - "https://test.com/짜장면.jpg", - "https://test.com/짜장면22.jpg" - ] - } - """); + """)); } @Test - @DisplayName("상점의 메뉴 카테고리들을 조회한다.") - void findShopMenuCategories() { - // given + void 상점의_메뉴_카테고리들을_조회한다() throws Exception { menuCategoryFixture.사이드메뉴(마슬랜); menuCategoryFixture.세트메뉴(마슬랜); Menu menu = menuFixture.짜장면_단일메뉴(마슬랜, menuCategoryFixture.추천메뉴(마슬랜)); - var response = RestAssured - .given() - .when() - .get("/shops/{shopId}/menus/categories", menu.getShopId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/shops/{shopId}/menus/categories", menu.getShopId()) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { - "count": 3, - "menu_categories": [ - { - "id": 3, - "name": "추천 메뉴" - }, - { - "id": 2, - "name": "세트 메뉴" - }, - { - "id": 1, - "name": "사이드 메뉴" - } - ] - } - """); + "count": 3, + "menu_categories": [ + { + "id": 3, + "name": "추천 메뉴" + }, + { + "id": 2, + "name": "세트 메뉴" + }, + { + "id": 1, + "name": "사이드 메뉴" + } + ] + } + """)); } @Test - @DisplayName("특정 상점 조회") - void getShop() { - // given + void 특정_상점_조회() throws Exception { menuCategoryFixture.사이드메뉴(마슬랜); menuCategoryFixture.세트메뉴(마슬랜); - var response = RestAssured - .given() - .when() - .get("/shops/{shopId}", 마슬랜.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - { - "address": "천안시 동남구 병천면 1600", - "delivery": true, - "delivery_price": 3000, - "description": "마슬랜 치킨입니다.", - "id": 1, - "image_urls": [ - "https://test-image.com/마슬랜.png", - "https://test-image.com/마슬랜2.png" - ], - "menu_categories": [ - { - "id": 2, - "name": "세트 메뉴" - }, - { - "id": 1, - "name": "사이드 메뉴" - } - ], - "name": "마슬랜 치킨", - "open": [ - { - "day_of_week": "MONDAY", - "closed": false, - "open_time": "00:00", - "close_time": "21:00" - }, - { - "day_of_week": "FRIDAY", - "closed": false, - "open_time": "00:00", - "close_time": "00:00" - } - ], - "pay_bank": true, - "pay_card": true, - "phone": "010-7574-1212", - "shop_categories": [ - - ], - "updated_at": "2024-01-15", - "is_event": false, - "bank": "국민", - "account_number": "01022595923" - } - """ - ); + mockMvc.perform( + get("/shops/{shopId}", 마슬랜.getId()) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + + { + "address": "천안시 동남구 병천면 1600", + "delivery": true, + "delivery_price": 3000, + "description": "마슬랜 치킨입니다.", + "id": 1, + "image_urls": [ + "https://test-image.com/마슬랜.png", + "https://test-image.com/마슬랜2.png" + ], + "menu_categories": [ + { + "id": 2, + "name": "세트 메뉴" + }, + { + "id": 1, + "name": "사이드 메뉴" + } + ], + "name": "마슬랜 치킨", + "open": [ + { + "day_of_week": "MONDAY", + "closed": false, + "open_time": "00:00", + "close_time": "21:00" + }, + { + "day_of_week": "FRIDAY", + "closed": false, + "open_time": "00:00", + "close_time": "00:00" + } + ], + "pay_bank": true, + "pay_card": true, + "phone": "010-7574-1212", + "shop_categories": [ + \s + ], + "updated_at": "2024-01-15", + "is_event": false, + "bank": "국민", + "account_number": "01022595923" + } + """)); } @Test - @DisplayName("특정 상점 모든 메뉴 조회") - void getShopMenus() { + void 특정_상점_모든_메뉴_조회() throws Exception { menuFixture.짜장면_단일메뉴(마슬랜, menuCategoryFixture.추천메뉴(마슬랜)); menuFixture.짜장면_옵션메뉴(마슬랜, menuCategoryFixture.세트메뉴(마슬랜)); - var response = RestAssured - .given() - .when() - .get("/shops/{shopId}/menus", 마슬랜.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/shops/{id}/menus", 마슬랜.getId()) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { - "count": 2, - "menu_categories": [ - { - "id": 1, - "name": "추천 메뉴", - "menus": [ - { - "id": 1, - "name": "짜장면", - "is_hidden": false, - "is_single": true, - "single_price": 7000, - "option_prices": null, - "description": "맛있는 짜장면", - "image_urls": [ - "https://test.com/짜장면.jpg", - "https://test.com/짜장면22.jpg" - ] - } - ] - }, - { - "id": 2, - "name": "세트 메뉴", - "menus": [ - { - "id": 2, - "name": "짜장면", - "is_hidden": false, - "is_single": false, - "single_price": null, - "option_prices": [ - { - "option": "곱빼기", - "price": 7500 - }, - { - "option": "일반", - "price": 7000 - } - ], - "description": "맛있는 짜장면", - "image_urls": [ - "https://test.com/짜장면.jpg", - "https://test.com/짜장면22.jpg" - ] - } - ] - } - ], - "updated_at": "2024-01-15" - } - """ - ); + "count": 2, + "menu_categories": [ + { + "id": 1, + "name": "추천 메뉴", + "menus": [ + { + "id": 1, + "name": "짜장면", + "is_hidden": false, + "is_single": true, + "single_price": 7000, + "option_prices": null, + "description": "맛있는 짜장면", + "image_urls": [ + "https://test.com/짜장면.jpg", + "https://test.com/짜장면22.jpg" + ] + } + ] + }, + { + "id": 2, + "name": "세트 메뉴", + "menus": [ + { + "id": 2, + "name": "짜장면", + "is_hidden": false, + "is_single": false, + "single_price": null, + "option_prices": [ + { + "option": "곱빼기", + "price": 7500 + }, + { + "option": "일반", + "price": 7000 + } + ], + "description": "맛있는 짜장면", + "image_urls": [ + "https://test.com/짜장면.jpg", + "https://test.com/짜장면22.jpg" + ] + } + ] + } + ], + "updated_at": "2024-01-15" + } + """)); } @Test - @DisplayName("모든 상점 조회") - void getAllShop() { - // given + void 모든_상점_조회() throws Exception { shopFixture.영업중이_아닌_신전_떡볶이(owner); - var response = RestAssured - .given() - .when() - .get("/shops") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); // 2024-01-15 12:00 월요일 기준 boolean 마슬랜_영업여부 = true; boolean 신전_떡볶이_영업여부 = false; - System.out.println(LocalDateTime.now(clock)); - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/shops") + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 2, "shops": [ @@ -473,67 +394,47 @@ void getAllShop() { } ] } - """, 마슬랜_영업여부, 신전_떡볶이_영업여부)); + """, 마슬랜_영업여부, 신전_떡볶이_영업여부))); } @Test - @DisplayName("상점들의 모든 카테고리를 조회한다.") - void getAllShopCategories() { - // given + void 상점들의_모든_카테고리를_조회한다() throws Exception { shopCategoryFixture.카테고리_일반음식(); shopCategoryFixture.카테고리_치킨(); - var response = RestAssured - .given() - .when() - .get("/shops/categories") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/shops/categories") + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { - "total_count": 2, - "shop_categories": [ - { - "id": 1, - "image_url": "https://test-image.com/normal.jpg", - "name": "일반음식점" - }, - { - "id": 2, - "image_url": "https://test-image.com/ckicken.jpg", - "name": "치킨" - } - ] - } - """); + "total_count": 2, + "shop_categories": [ + { + "id": 1, + "image_url": "https://test-image.com/normal.jpg", + "name": "일반음식점" + }, + { + "id": 2, + "image_url": "https://test-image.com/ckicken.jpg", + "name": "치킨" + } + ] + } + """)); } @Test - @DisplayName("특정 상점의 이벤트들을 조회한다.") - void getShopEvents() { - eventArticleFixture.할인_이벤트( - 마슬랜, - LocalDate.now(clock).minusDays(3), - LocalDate.now(clock).plusDays(3) - ); - eventArticleFixture.참여_이벤트( - 마슬랜, - LocalDate.now(clock).minusDays(3), - LocalDate.now(clock).plusDays(3) - ); - - var response = RestAssured - .given() - .when() - .get("/shops/{shopId}/events", 마슬랜.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 특정_상점의_이벤트들을_조회한다() throws Exception { + eventArticleFixture.할인_이벤트(마슬랜, LocalDate.now(clock).minusDays(3), LocalDate.now(clock).plusDays(3)); + eventArticleFixture.참여_이벤트(마슬랜, LocalDate.now(clock).minusDays(3), LocalDate.now(clock).plusDays(3)); + + mockMvc.perform( + get("/shops/{shopId}/events", 마슬랜.getId()) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "events": [ { @@ -564,84 +465,51 @@ void getShopEvents() { } ] } - """); + """)); } @Test - @DisplayName("이벤트 진행중인 상점의 정보를 조회한다.") - void getShopWithEvents() { - eventArticleFixture.할인_이벤트( - 마슬랜, - LocalDate.now(clock).minusDays(3), - LocalDate.now(clock).plusDays(3) - ); - eventArticleFixture.참여_이벤트( - 마슬랜, - LocalDate.now(clock).minusDays(3), - LocalDate.now(clock).plusDays(3) - ); - - var response = RestAssured - .given() - .when() - .get("/shops/{shopId}", 마슬랜.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - Assertions.assertThat(response.jsonPath().getBoolean("is_event")).isTrue(); + void 이벤트_진행중인_상점의_정보를_조회한다() throws Exception { + eventArticleFixture.할인_이벤트(마슬랜, LocalDate.now(clock).minusDays(3), LocalDate.now(clock).plusDays(3)); + eventArticleFixture.참여_이벤트(마슬랜, LocalDate.now(clock).minusDays(3), LocalDate.now(clock).plusDays(3)); + + mockMvc.perform( + get("/shops/{shopId}", 마슬랜.getId()) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "is_event": true + } + """)); } @Test - @DisplayName("이벤트 진행중이지 않은 상점의 정보를 조회한다.") - void getShopWithoutEvents() { - eventArticleFixture.할인_이벤트( - 마슬랜, - LocalDate.now(clock).plusDays(3), - LocalDate.now(clock).plusDays(5) - ); - eventArticleFixture.참여_이벤트( - 마슬랜, - LocalDate.now(clock).minusDays(5), - LocalDate.now(clock).minusDays(3) - ); - - var response = RestAssured - .given() - .when() - .get("/shops/{shopId}", 마슬랜.getId()) - .then() - - .statusCode(HttpStatus.OK.value()) - .extract(); - - Assertions.assertThat(response.jsonPath().getBoolean("is_event")).isFalse(); + void 이벤트_진행중이지_않은_상점의_정보를_조회한다() throws Exception { + eventArticleFixture.할인_이벤트(마슬랜, LocalDate.now(clock).plusDays(3), LocalDate.now(clock).plusDays(5)); + eventArticleFixture.참여_이벤트(마슬랜, LocalDate.now(clock).minusDays(5), LocalDate.now(clock).minusDays(3)); + + mockMvc.perform( + get("/shops/{shopId}", 마슬랜.getId()) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "is_event": false + } + """)); } @Test - @DisplayName("이벤트 베너 조회") - void ownerShopDeleteEvent() { - eventArticleFixture.참여_이벤트( - 마슬랜, - LocalDate.now(clock), - LocalDate.now(clock).plusDays(10) - ); - eventArticleFixture.할인_이벤트( - 마슬랜, - LocalDate.now(clock).minusDays(10), - LocalDate.now(clock).minusDays(1) - ); - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .when() - .get("/shops/events") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 이벤트_베너_조회() throws Exception { + eventArticleFixture.참여_이벤트(마슬랜, LocalDate.now(clock), LocalDate.now(clock).plusDays(10)); + eventArticleFixture.할인_이벤트(마슬랜, LocalDate.now(clock).minusDays(10), LocalDate.now(clock).minusDays(1)); + + mockMvc.perform( + get("/shops/events") + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "events": [ { @@ -659,30 +527,21 @@ void ownerShopDeleteEvent() { } ] } - """); + """)); } @Test - void 리뷰_평점순으로_정렬하여_모든_상점을_조회한다() { - // given + void 리뷰_평점순으로_정렬하여_모든_상점을_조회한다() throws Exception { Shop 영업중인_티바 = shopFixture.영업중인_티바(owner); shopReviewFixture.리뷰_4점(익명_학생, 영업중인_티바); - - var response = RestAssured - .given() - .queryParam("sorter", "RATING") - .when() - .get("/v2/shops") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - // 2024-01-15 12:00 월요일 기준 boolean 마슬랜_영업여부 = true; boolean 티바_영업여부 = true; - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/v2/shops") + .queryParam("sorter", "RATING") + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 2, "shops": [ @@ -775,33 +634,25 @@ void ownerShopDeleteEvent() { } ] } - """, 티바_영업여부, 마슬랜_영업여부)); + """, 티바_영업여부, 마슬랜_영업여부))); } @Test - void 리뷰_개수순으로_정렬하여_모든_상점을_조회한다() { - // given + void 리뷰_개수순으로_정렬하여_모든_상점을_조회한다() throws Exception { Shop 영업중인_티바 = shopFixture.영업중인_티바(owner); shopReviewFixture.리뷰_4점(익명_학생, 영업중인_티바); shopReviewFixture.리뷰_4점(익명_학생, 마슬랜); shopReviewFixture.리뷰_4점(익명_학생, 마슬랜); - - var response = RestAssured - .given() - .queryParam("sorter", "COUNT") - .when() - .get("/v2/shops") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - // 2024-01-15 12:00 월요일 기준 boolean 마슬랜_영업여부 = true; boolean 티바_영업여부 = true; - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/v2/shops") + .queryParam("sorter", "COUNT") + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 2, "shops": [ @@ -894,33 +745,25 @@ void ownerShopDeleteEvent() { } ] } - """, 티바_영업여부, 마슬랜_영업여부)); + """, 티바_영업여부, 마슬랜_영업여부))); } @Test - void 리뷰_개수가_많아도_영업중이_아니라면_정렬_우선순위가_낮은_상태로_모든_상점을_조회한다() { - // given + void 리뷰_개수가_많아도_영업중이_아니라면_정렬_우선순위가_낮은_상태로_모든_상점을_조회한다() throws Exception { Shop 영업중이_아닌_신전떡볶이 = shopFixture.영업중이_아닌_신전_떡볶이(owner); shopReviewFixture.리뷰_4점(익명_학생, 영업중이_아닌_신전떡볶이); shopReviewFixture.리뷰_4점(익명_학생, 영업중이_아닌_신전떡볶이); shopReviewFixture.리뷰_4점(익명_학생, 마슬랜); - - var response = RestAssured - .given() - .queryParam("sorter", "COUNT") - .when() - .get("/v2/shops") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - // 2024-01-15 12:00 월요일 기준 boolean 신전떡볶이_영업여부 = false; boolean 마슬랜_영업여부 = true; - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/v2/shops") + .queryParam("sorter", "COUNT") + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 2, "shops": [ @@ -1013,32 +856,26 @@ void ownerShopDeleteEvent() { } ] } - """, 마슬랜_영업여부, 신전떡볶이_영업여부)); + """, 마슬랜_영업여부, 신전떡볶이_영업여부))); } @Test - void 운영중인_상점만_필터하여_모든_상점을_조회한다() { - // given + void 운영중인_상점만_필터하여_모든_상점을_조회한다() throws Exception { Shop 영업중이_아닌_신전떡볶이 = shopFixture.영업중이_아닌_신전_떡볶이(owner); shopReviewFixture.리뷰_4점(익명_학생, 영업중이_아닌_신전떡볶이); shopReviewFixture.리뷰_4점(익명_학생, 영업중이_아닌_신전떡볶이); shopReviewFixture.리뷰_4점(익명_학생, 마슬랜); - var response = RestAssured - .given() - .queryParam("filter", "OPEN") - .when() - .get("/v2/shops") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - // 2024-01-15 12:00 월요일 기준 boolean 마슬랜_영업여부 = true; - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/v2/shops") + .queryParam("filter", "OPEN") + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 1, "shops": [ @@ -1073,32 +910,23 @@ void ownerShopDeleteEvent() { } ] } - """, 마슬랜_영업여부)); + """, 마슬랜_영업여부))); } @Test - void 배달_가능한_상점만_필터하여_모든_상점을_조회한다() { - // given + void 배달_가능한_상점만_필터하여_모든_상점을_조회한다() throws Exception { Shop 배달_안되는_신전_떡볶이 = shopFixture.배달_안되는_신전_떡볶이(owner); shopReviewFixture.리뷰_4점(익명_학생, 배달_안되는_신전_떡볶이); - shopReviewFixture.리뷰_4점(익명_학생, 배달_안되는_신전_떡볶이); shopReviewFixture.리뷰_4점(익명_학생, 마슬랜); - - var response = RestAssured - .given() - .queryParam("filter", "DELIVERY") - .when() - .get("/v2/shops") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - // 2024-01-15 12:00 월요일 기준 boolean 마슬랜_영업여부 = true; - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/v2/shops") + .queryParam("filter", "DELIVERY") + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 1, "shops": [ @@ -1133,12 +961,11 @@ void ownerShopDeleteEvent() { } ] } - """, 마슬랜_영업여부)); + """, 마슬랜_영업여부))); } @Test - void 배달_가능하고_영업중인_상점만_필터하여_모든_상점을_조회한다() { - // given + void 배달_가능하고_영업중인_상점만_필터하여_모든_상점을_조회한다() throws Exception { Shop 배달_안되는_신전_떡볶이 = shopFixture.배달_안되는_신전_떡볶이(owner); shopReviewFixture.리뷰_4점(익명_학생, 배달_안되는_신전_떡볶이); shopReviewFixture.리뷰_4점(익명_학생, 배달_안되는_신전_떡볶이); @@ -1146,22 +973,15 @@ void ownerShopDeleteEvent() { shopFixture.영업중이_아닌_신전_떡볶이(owner); shopReviewFixture.리뷰_4점(익명_학생, 마슬랜); - - var response = RestAssured - .given() - .queryParam("filter", "DELIVERY") - .queryParam("filter", "OPEN") - .when() - .get("/v2/shops") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - // 2024-01-15 12:00 월요일 기준 boolean 마슬랜_영업여부 = true; - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/v2/shops") + .queryParam("filter", "DELIVERY") + .queryParam("filter", "OPEN") + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 1, "shops": [ @@ -1196,12 +1016,11 @@ void ownerShopDeleteEvent() { } ] } - """, 마슬랜_영업여부)); + """, 마슬랜_영업여부))); } @Test - void 영업중인_상점만_필터하여_리뷰_개수_순으로_모든_상점을_조회한다() { - // given + void 영업중인_상점만_필터하여_리뷰_개수_순으로_모든_상점을_조회한다() throws Exception { Shop 배달_안되는_신전_떡볶이 = shopFixture.배달_안되는_신전_떡볶이(owner); shopReviewFixture.리뷰_4점(익명_학생, 배달_안되는_신전_떡볶이); shopReviewFixture.리뷰_4점(익명_학생, 배달_안되는_신전_떡볶이); @@ -1209,23 +1028,16 @@ void ownerShopDeleteEvent() { shopFixture.영업중이_아닌_신전_떡볶이(owner); shopReviewFixture.리뷰_4점(익명_학생, 마슬랜); - - var response = RestAssured - .given() - .queryParam("filter", "OPEN") - .queryParam("sorter", "COUNT") - .when() - .get("/v2/shops") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - // 2024-01-15 12:00 월요일 기준 boolean 신전_떡볶이_영업여부 = true; boolean 마슬랜_영업여부 = true; - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/v2/shops") + .queryParam("filter", "OPEN") + .queryParam("sorter", "COUNT") + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 2, "shops": [ @@ -1318,31 +1130,25 @@ void ownerShopDeleteEvent() { } ] } - """, 신전_떡볶이_영업여부, 마슬랜_영업여부)); + """, 신전_떡볶이_영업여부, 마슬랜_영업여부))); } @Test - void 신고된_리뷰의_내용도_반영해서_모든_상점을_조회한다() { - // given + void 신고된_리뷰의_내용도_반영해서_모든_상점을_조회한다() throws Exception { Shop 배달_안되는_신전_떡볶이 = shopFixture.배달_안되는_신전_떡볶이(owner); ShopReview 리뷰_4점 = shopReviewFixture.리뷰_4점(익명_학생, 배달_안되는_신전_떡볶이); shopReviewReportFixture.리뷰_신고(익명_학생, 리뷰_4점, UNHANDLED); shopReviewFixture.리뷰_4점(익명_학생, 마슬랜); - var response = RestAssured - .given() - .when() - .get("/v2/shops") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - // 2024-01-15 12:00 월요일 기준 boolean 신전_떡볶이_영업여부 = true; boolean 마슬랜_영업여부 = true; - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/v2/shops") + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 2, "shops": [ @@ -1435,31 +1241,24 @@ void ownerShopDeleteEvent() { } ] } - """, 마슬랜_영업여부, 신전_떡볶이_영업여부)); + """, 마슬랜_영업여부, 신전_떡볶이_영업여부))); } @Test - void 신고_반려된_리뷰는_반영된_상태로_모든_상점을_조회한다() { - // given + void 신고_반려된_리뷰는_반영된_상태로_모든_상점을_조회한다() throws Exception { Shop 배달_안되는_신전_떡볶이 = shopFixture.배달_안되는_신전_떡볶이(owner); ShopReview 리뷰_4점 = shopReviewFixture.리뷰_4점(익명_학생, 배달_안되는_신전_떡볶이); shopReviewReportFixture.리뷰_신고(익명_학생, 리뷰_4점, DISMISSED); shopReviewFixture.리뷰_4점(익명_학생, 마슬랜); - var response = RestAssured - .given() - .when() - .get("/v2/shops") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - // 2024-01-15 12:00 월요일 기준 boolean 신전_떡볶이_영업여부 = true; boolean 마슬랜_영업여부 = true; - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/v2/shops") + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 2, "shops": [ @@ -1552,6 +1351,7 @@ void ownerShopDeleteEvent() { } ] } - """, 마슬랜_영업여부, 신전_떡볶이_영업여부)); + """, 마슬랜_영업여부, 신전_떡볶이_영업여부))); } + } diff --git a/src/test/java/in/koreatech/koin/acceptance/ShopReviewApiTest.java b/src/test/java/in/koreatech/koin/acceptance/ShopReviewApiTest.java index c4e8b0d0e..7759e0e83 100644 --- a/src/test/java/in/koreatech/koin/acceptance/ShopReviewApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/ShopReviewApiTest.java @@ -1,18 +1,21 @@ package in.koreatech.koin.acceptance; -import static in.koreatech.koin.domain.shop.model.ReportStatus.DISMISSED; import static in.koreatech.koin.domain.shop.model.ReportStatus.UNHANDLED; import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.util.Optional; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; import in.koreatech.koin.AcceptanceTest; @@ -30,11 +33,10 @@ import in.koreatech.koin.fixture.ShopReviewReportCategoryFixture; import in.koreatech.koin.fixture.ShopReviewReportFixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; @SuppressWarnings("NonAsciiCharacters") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Transactional class ShopReviewApiTest extends AcceptanceTest { @Autowired @@ -78,8 +80,9 @@ class ShopReviewApiTest extends AcceptanceTest { private final int INITIAL_REVIEW_COUNT = 2; - @BeforeEach + @BeforeAll void setUp() { + clear(); 준호_학생 = userFixture.준호_학생(); 익명_학생 = userFixture.익명_학생(); 현수_사장님 = userFixture.현수_사장님(); @@ -95,12 +98,10 @@ void setUp() { @Test @DisplayName("사용자가 리뷰를 등록할 수 있다.") - void createReview() { - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준호) - .body(String.format(""" + void createReview() throws Exception { + mockMvc.perform(post("/shops/{shopId}/reviews", 신전_떡볶이.getId()) + .header("Authorization", "Bearer " + token_준호) + .content(String.format(""" { "rating": 4, "content": "정말 맛있어요~!", @@ -113,36 +114,16 @@ void createReview() { ] } """)) - .when() - .pathParam("shopId", 신전_떡볶이.getId()) - .post("/shops/{shopId}/reviews") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - transactionTemplate.executeWithoutResult(status -> { - ShopReview shopReview = shopReviewRepository.getByIdAndIsDeleted(INITIAL_REVIEW_COUNT + 1); - assertSoftly( - softly -> { - softly.assertThat(shopReview.getRating()).isEqualTo(4); - softly.assertThat(shopReview.getContent()).isEqualTo("정말 맛있어요~!"); - softly.assertThat(shopReview.getImages().get(0).getImageUrls()) - .isEqualTo("https://static.koreatech.in/example.png"); - softly.assertThat(shopReview.getMenus().get(0).getMenuName()).isEqualTo("치킨"); - softly.assertThat(shopReview.getMenus().get(1).getMenuName()).isEqualTo("피자"); - verify(reviewEventListener).onReviewRegister(any()); - } - ); - }); + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); } @Test - void 리뷰를_등록할_때_메뉴명을_공백으로_입력하면_예외가_발생한다() { - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준호) - .body(String.format(""" + void 리뷰를_등록할_때_메뉴명을_공백으로_입력하면_예외가_발생한다() throws Exception { + mockMvc.perform(post("/shops/{shopId}/reviews", 신전_떡볶이.getId()) + .header("Authorization", "Bearer " + token_준호) + .content(String.format(""" { "rating": 4, "content": "정말 맛있어요~!", @@ -154,21 +135,18 @@ void createReview() { ] } """)) - .when() - .pathParam("shopId", 신전_떡볶이.getId()) - .post("/shops/{shopId}/reviews") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); + .contentType(MediaType.APPLICATION_JSON) + .content(""" + """) + ) + .andExpect(status().isBadRequest()); } @Test - void 리뷰_내용을_작성하지_않고_리뷰를_등록할_수_있다() { - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준호) - .body(String.format(""" + void 리뷰_내용을_작성하지_않고_리뷰를_등록할_수_있다() throws Exception { + mockMvc.perform(post("/shops/{shopId}/reviews", 신전_떡볶이.getId()) + .header("Authorization", "Bearer " + token_준호) + .content(String.format(""" { "rating": 4, "image_urls": [ @@ -180,38 +158,17 @@ void createReview() { ] } """)) - .when() - .pathParam("shopId", 신전_떡볶이.getId()) - .post("/shops/{shopId}/reviews") - .then() - .log().all() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - transactionTemplate.executeWithoutResult(status -> { - ShopReview shopReview = shopReviewRepository.getByIdAndIsDeleted(INITIAL_REVIEW_COUNT + 1); - assertSoftly( - softly -> { - softly.assertThat(shopReview.getRating()).isEqualTo(4); - softly.assertThat(shopReview.getContent()).isNull(); - softly.assertThat(shopReview.getImages().get(0).getImageUrls()) - .isEqualTo("https://static.koreatech.in/example.png"); - softly.assertThat(shopReview.getMenus().get(0).getMenuName()).isEqualTo("치킨"); - softly.assertThat(shopReview.getMenus().get(1).getMenuName()).isEqualTo("피자"); - verify(reviewEventListener).onReviewRegister(any()); - } - ); - }); + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); } @Test @DisplayName("사용자가 본인의 리뷰를 수정할 수 있다.") - void modifyReview() { - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준호) - .body(String.format(""" + void modifyReview() throws Exception { + mockMvc.perform(put("/shops/{shopId}/reviews/{reviewId}", 신전_떡볶이.getId(), 준호_학생_리뷰.getId()) + .header("Authorization", "Bearer " + token_준호) + .content(String.format(""" { "rating": 3, "content": "정말 맛있어요!", @@ -223,46 +180,33 @@ void modifyReview() { ] } """)) - .when() - .pathParam("shopId", 신전_떡볶이.getId()) - .pathParam("reviewId", 준호_학생_리뷰.getId()) - .put("/shops/{shopId}/reviews/{reviewId}") - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); - transactionTemplate.executeWithoutResult(status -> { - ShopReview shopReview = shopReviewRepository.getByIdAndIsDeleted(준호_학생_리뷰.getId()); - assertSoftly( - softly -> { - softly.assertThat(shopReview.getRating()).isEqualTo(3); - softly.assertThat(shopReview.getContent()).isEqualTo("정말 맛있어요!"); - softly.assertThat(shopReview.getImages().get(0).getImageUrls()) - .isEqualTo("https://static.koreatech.in/example1.png"); - softly.assertThat(shopReview.getMenus().size()).isEqualTo(1); - } - ); - }); + ShopReview shopReview = shopReviewRepository.getByIdAndIsDeleted(준호_학생_리뷰.getId()); + assertSoftly( + softly -> { + softly.assertThat(shopReview.getRating()).isEqualTo(3); + softly.assertThat(shopReview.getContent()).isEqualTo("정말 맛있어요!"); + softly.assertThat(shopReview.getImages().get(0).getImageUrls()) + .isEqualTo("https://static.koreatech.in/example1.png"); + softly.assertThat(shopReview.getMenus().size()).isEqualTo(1); + } + ); } @Test @DisplayName("로그인한 사용자가 리뷰를 조회할 수 있다.") - void getReview() { - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준호) - .when() - .queryParam("limit", 10) - .queryParam("page", 1) - .pathParam("shopId", 준호_학생_리뷰.getShop().getId()) - .get("/shops/{shopId}/reviews") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + void getReview() throws Exception { + mockMvc.perform(get("/shops/{shopId}/reviews", 준호_학생_리뷰.getShop().getId()) + .header("Authorization", "Bearer " + token_준호) + .queryParam("limit", "10") + .queryParam("page", "1") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "total_count": 2, "current_count": 2, @@ -326,27 +270,20 @@ void getReview() { 익명_학생_리뷰.getContent(), 익명_학생_리뷰.getImages().get(0).getImageUrls(), 익명_학생_리뷰.getMenus().get(0).getMenuName()) - ); + )); } @Test - void 신고된_리뷰는_is_reported_가_true_이다() { + void 신고된_리뷰는_is_reported_가_true_이다() throws Exception { ShopReviewReport shopReviewReport = shopReviewReportFixture.리뷰_신고(준호_학생, 익명_학생_리뷰, UNHANDLED); - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준호) - .when() - .queryParam("limit", 10) - .queryParam("page", 1) - .pathParam("shopId", 신전_떡볶이.getId()) - .get("/shops/{shopId}/reviews") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform(get("/shops/{shopId}/reviews", 신전_떡볶이.getId()) + .header("Authorization", "Bearer " + token_준호) + .queryParam("limit", "10") + .queryParam("page", "1") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "total_count": 2, "current_count": 2, @@ -410,26 +347,19 @@ void getReview() { 익명_학생_리뷰.getContent(), 익명_학생_리뷰.getImages().get(0).getImageUrls(), 익명_학생_리뷰.getMenus().get(0).getMenuName()) - ); + )); } @Test @DisplayName("비회원이 리뷰를 조회할 수 있다.") - void getReviewByUnauthenticatedUser() { - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .when() - .queryParam("limit", 10) - .queryParam("page", 1) - .pathParam("shopId", 신전_떡볶이.getId()) - .get("/shops/{shopId}/reviews") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + void getReviewByUnauthenticatedUser() throws Exception { + mockMvc.perform(get("/shops/{shopId}/reviews", 신전_떡볶이.getId()) + .queryParam("limit", "10") + .queryParam("page", "1") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "total_count": 2, "current_count": 2, @@ -493,23 +423,19 @@ void getReviewByUnauthenticatedUser() { 익명_학생_리뷰.getContent(), 익명_학생_리뷰.getImages().get(0).getImageUrls(), 익명_학생_리뷰.getMenus().get(0).getMenuName()) - ); + )); } @Test @DisplayName("리뷰 신고 카테고리를 조회할 수 있다.") - void getReviewReportCategories() { - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .when() - .get("/shops/reviews/reports/categories") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + void getReviewReportCategories() throws Exception { + mockMvc.perform(get("/shops/reviews/reports/categories") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + """) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 4, "categories": [ @@ -540,18 +466,16 @@ void getReviewReportCategories() { 신고_카테고리_3.getDetail(), 신고_카테고리_4.getName(), 신고_카테고리_4.getDetail()) - ); + )); } @Test @DisplayName("특정 리뷰를 신고한다.") - void reportReview() { - - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준호) - .body(""" + void reportReview() throws Exception { + mockMvc.perform(post("/shops/{shopId}/reviews/{reviewId}/reports", 준호_학생_리뷰.getShop().getId(), 준호_학생_리뷰.getId()) + .header("Authorization", "Bearer " + token_준호) + .contentType(MediaType.APPLICATION_JSON) + .content(""" { "reports": [ { @@ -565,157 +489,66 @@ void reportReview() { ] } """) - .when() - .pathParam("shopId", 준호_학생_리뷰.getShop().getId()) - .pathParam("reviewId", 준호_학생_리뷰.getId()) - .post("/shops/{shopId}/reviews/{reviewId}/reports") - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); + ) + .andExpect(status().isNoContent()); - transactionTemplate.executeWithoutResult(status -> { - Optional shopReviewReport1 = shopReviewReportRepository.findById(1); - Optional shopReviewReport2 = shopReviewReportRepository.findById(2); - assertSoftly( - softly -> { - softly.assertThat(shopReviewReport1.isPresent()).isTrue(); - softly.assertThat(shopReviewReport1.get().getTitle()).isEqualTo("기타"); - softly.assertThat(shopReviewReport1.get().getContent()).isEqualTo("적절치 못한 리뷰인 것 같습니다."); - softly.assertThat(shopReviewReport1.get().getReportStatus()).isEqualTo(UNHANDLED); - softly.assertThat(shopReviewReport2.get().getTitle()).isEqualTo("스팸"); - softly.assertThat(shopReviewReport2.get().getContent()).isEqualTo("광고가 포함된 리뷰입니다."); - softly.assertThat(shopReviewReport2.get().getReportStatus()).isEqualTo(UNHANDLED); - verify(reviewEventListener).onReviewReportRegister(any()); - } - ); - }); + assertSoftly( + softly -> { + Optional shopReviewReport1 = shopReviewReportRepository.findById(1); + Optional shopReviewReport2 = shopReviewReportRepository.findById(2); + softly.assertThat(shopReviewReport1.isPresent()).isTrue(); + softly.assertThat(shopReviewReport1.get().getTitle()).isEqualTo("기타"); + softly.assertThat(shopReviewReport1.get().getContent()).isEqualTo("적절치 못한 리뷰인 것 같습니다."); + softly.assertThat(shopReviewReport1.get().getReportStatus()).isEqualTo(UNHANDLED); + softly.assertThat(shopReviewReport2.get().getTitle()).isEqualTo("스팸"); + softly.assertThat(shopReviewReport2.get().getContent()).isEqualTo("광고가 포함된 리뷰입니다."); + softly.assertThat(shopReviewReport2.get().getReportStatus()).isEqualTo(UNHANDLED); + forceVerify(() -> verify(reviewEventListener).onReviewReportRegister(any())); + clear(); + setUp(); + } + ); } @Test @DisplayName("학생이 자신이 작성한 리뷰를 삭제한다.") - void deleteMyReview() { - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준호) - .when() - .pathParam("shopId", 준호_학생_리뷰.getShop().getId()) - .pathParam("reviewId", 준호_학생_리뷰.getId()) - .delete("/shops/{shopId}/reviews/{reviewId}") - .then() - .log().all() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); + void deleteMyReview() throws Exception { + mockMvc.perform(delete("/shops/{shopId}/reviews/{reviewId}", 준호_학생_리뷰.getShop().getId(), 준호_학생_리뷰.getId()) + .header("Authorization", "Bearer " + token_준호) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); - transactionTemplate.executeWithoutResult(status -> { - Optional shopReview = shopReviewRepository.findById(1); - assertSoftly( - softly -> { - softly.assertThat(shopReview.get().isDeleted()).isTrue(); - } - ); - }); + Optional shopReview = shopReviewRepository.findById(1); + assertSoftly( + softly -> { + softly.assertThat(shopReview.get().isDeleted()).isTrue(); + } + ); } @Test - void 신고가_반려된_리뷰는_is_reported_가_false_이다() { - ShopReviewReport shopReviewReport = shopReviewReportFixture.리뷰_신고(준호_학생, 익명_학생_리뷰, DISMISSED); - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준호) - .when() - .queryParam("limit", 10) - .queryParam("page", 1) - .pathParam("shopId", 신전_떡볶이.getId()) - .get("/shops/{shopId}/reviews") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + void 신고가_반려된_리뷰는_is_reported_가_false_이다() throws Exception { + mockMvc.perform(get("/shops/{shopId}/reviews", 신전_떡볶이.getId()) + .header("Authorization", "Bearer " + token_준호) + .queryParam("limit", "10") + .queryParam("page", "1") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + """) + ) + .andExpect(status().isOk()); - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" - { - "total_count": 2, - "current_count": 2, - "total_page": 1, - "current_page": 1, - "statistics": { - "average_rating": 4.0, - "ratings": { - "1": 0, - "2": 0, - "3": 0, - "4": 2, - "5": 0 - } - }, - "reviews": [ - { - "review_id": %d, - "rating": %d, - "nick_name": "%s", - "content": "%s", - "image_urls": [ - "%s" - ], - "menu_names": [ - "%s" - ], - "is_mine": true, - "is_modified": false, - "is_reported": false, - "created_at": "2024-01-15" - },{ - "review_id": %d, - "rating": %d, - "nick_name": "%s", - "content": "%s", - "image_urls": [ - "%s" - ], - "menu_names": [ - "%s" - ], - "is_mine": false, - "is_modified": false, - "is_reported": false, - "created_at": "2024-01-15" - } - ] - } - """, - 준호_학생_리뷰.getId(), - 준호_학생_리뷰.getRating(), - 준호_학생_리뷰.getReviewer().getUser().getNickname(), - 준호_학생_리뷰.getContent(), - 준호_학생_리뷰.getImages().get(0).getImageUrls(), - 준호_학생_리뷰.getMenus().get(0).getMenuName(), - 익명_학생_리뷰.getId(), - 익명_학생_리뷰.getRating(), - 익명_학생_리뷰.getReviewer().getAnonymousNickname(), - 익명_학생_리뷰.getContent(), - 익명_학생_리뷰.getImages().get(0).getImageUrls(), - 익명_학생_리뷰.getMenus().get(0).getMenuName()) - ); } @Test - void 단일_리뷰를_조회할_수_있다() { - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .header("Authorization", "Bearer " + token_준호) - .when() - .pathParam("shopId", 신전_떡볶이.getId()) - .pathParam("reviewId", 준호_학생_리뷰.getId()) - .get("/shops/{shopId}/reviews/{reviewId}") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + void 단일_리뷰를_조회할_수_있다() throws Exception { + mockMvc.perform(get("/shops/{shopId}/reviews/{reviewId}", 신전_떡볶이.getId(), 준호_학생_리뷰.getId()) + .header("Authorization", "Bearer " + token_준호) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "review_id": %d, "rating": %d, @@ -737,27 +570,20 @@ void deleteMyReview() { 준호_학생_리뷰.getContent(), 준호_학생_리뷰.getImages().get(0).getImageUrls(), 준호_학생_리뷰.getMenus().get(0).getMenuName()) - ); + )); } @Test - void 최신순으로_정렬하여_리뷰를_조회한다() { + void 최신순으로_정렬하여_리뷰를_조회한다() throws Exception { ShopReview 최신_리뷰_2024_08_07 = shopReviewFixture.최신_리뷰_2024_08_07(준호_학생, 신전_떡볶이); - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .when() - .queryParam("limit", 10) - .queryParam("page", 1) - .queryParam("sorter", "LATEST") - .pathParam("shopId", 신전_떡볶이.getId()) - .get("/shops/{shopId}/reviews") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform(get("/shops/{shopId}/reviews", 신전_떡볶이.getId()) + .queryParam("limit", "10") + .queryParam("page", "1") + .queryParam("sorter", "LATEST") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "total_count": 3, "current_count": 3, @@ -843,27 +669,22 @@ void deleteMyReview() { 익명_학생_리뷰.getContent(), 익명_학생_리뷰.getImages().get(0).getImageUrls(), 익명_학생_리뷰.getMenus().get(0).getMenuName()) - ); + )); } @Test - void 오래된_순으로_정렬하여_리뷰를_조회한다() { + void 오래된_순으로_정렬하여_리뷰를_조회한다() throws Exception { ShopReview 최신_리뷰_2024_08_07 = shopReviewFixture.최신_리뷰_2024_08_07(준호_학생, 신전_떡볶이); - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .when() - .queryParam("limit", 10) - .queryParam("page", 1) - .queryParam("sorter", "OLDEST") - .pathParam("shopId", 신전_떡볶이.getId()) - .get("/shops/{shopId}/reviews") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform(get("/shops/{shopId}/reviews", 신전_떡볶이.getId()) + .queryParam("limit", "10") + .queryParam("page", "1") + .queryParam("sorter", "OLDEST") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + """) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "total_count": 3, "current_count": 3, @@ -949,27 +770,20 @@ void deleteMyReview() { 최신_리뷰_2024_08_07.getContent(), 최신_리뷰_2024_08_07.getImages().get(0).getImageUrls(), 최신_리뷰_2024_08_07.getMenus().get(0).getMenuName()) - ); + )); } @Test - void 별점이_높은_순으로_정렬하여_리뷰를_조회한다() { + void 별점이_높은_순으로_정렬하여_리뷰를_조회한다() throws Exception { ShopReview 리뷰_5점 = shopReviewFixture.리뷰_5점(준호_학생, 신전_떡볶이); - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .when() - .queryParam("limit", 10) - .queryParam("page", 1) - .queryParam("sorter", "HIGHEST_RATING") - .pathParam("shopId", 신전_떡볶이.getId()) - .get("/shops/{shopId}/reviews") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform(get("/shops/{shopId}/reviews", 신전_떡볶이.getId()) + .queryParam("limit", "10") + .queryParam("page", "1") + .queryParam("sorter", "HIGHEST_RATING") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "total_count": 3, "current_count": 3, @@ -1055,27 +869,20 @@ void deleteMyReview() { 익명_학생_리뷰.getContent(), 익명_학생_리뷰.getImages().get(0).getImageUrls(), 익명_학생_리뷰.getMenus().get(0).getMenuName()) - ); + )); } @Test - void 별점이_낮은_순으로_정렬하여_리뷰를_조회한다() { + void 별점이_낮은_순으로_정렬하여_리뷰를_조회한다() throws Exception { ShopReview 리뷰_5점 = shopReviewFixture.리뷰_5점(준호_학생, 신전_떡볶이); - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .when() - .queryParam("limit", 10) - .queryParam("page", 1) - .queryParam("sorter", "LOWEST_RATING") - .pathParam("shopId", 신전_떡볶이.getId()) - .get("/shops/{shopId}/reviews") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform(get("/shops/{shopId}/reviews", 신전_떡볶이.getId()) + .queryParam("limit", "10") + .queryParam("page", "1") + .queryParam("sorter", "LOWEST_RATING") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "total_count": 3, "current_count": 3, @@ -1161,26 +968,19 @@ void deleteMyReview() { 리뷰_5점.getContent(), 리뷰_5점.getImages().get(0).getImageUrls(), 리뷰_5점.getMenus().get(0).getMenuName()) - ); + )); } @Test - void 자신의_리뷰를_최신순으로_조회한다() { + void 자신의_리뷰를_최신순으로_조회한다() throws Exception { ShopReview 최신_리뷰_2024_08_07 = shopReviewFixture.최신_리뷰_2024_08_07(준호_학생, 신전_떡볶이); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_준호) - .contentType(ContentType.JSON) - .when() - .pathParam("shopId", 신전_떡볶이.getId()) - .queryParam("sorter", "LATEST") - .get("/shops/{shopId}/reviews/me") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform(get("/shops/{shopId}/reviews/me", 신전_떡볶이.getId()) + .header("Authorization", "Bearer " + token_준호) + .queryParam("sorter", "LATEST") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 2, "reviews": [ @@ -1229,26 +1029,19 @@ void deleteMyReview() { 준호_학생_리뷰.getContent(), 준호_학생_리뷰.getImages().get(0).getImageUrls(), 준호_학생_리뷰.getMenus().get(0).getMenuName()) - ); + )); } @Test - void 자신의_리뷰를_오래된_순으로_조회한다() { + void 자신의_리뷰를_오래된_순으로_조회한다() throws Exception { ShopReview 최신_리뷰_2024_08_07 = shopReviewFixture.최신_리뷰_2024_08_07(준호_학생, 신전_떡볶이); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_준호) - .contentType(ContentType.JSON) - .when() - .pathParam("shopId", 신전_떡볶이.getId()) - .queryParam("sorter", "OLDEST") - .get("/shops/{shopId}/reviews/me") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform(get("/shops/{shopId}/reviews/me", 신전_떡볶이.getId()) + .header("Authorization", "Bearer " + token_준호) + .queryParam("sorter", "OLDEST") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 2, "reviews": [ @@ -1297,26 +1090,19 @@ void deleteMyReview() { 최신_리뷰_2024_08_07.getContent(), 최신_리뷰_2024_08_07.getImages().get(0).getImageUrls(), 최신_리뷰_2024_08_07.getMenus().get(0).getMenuName()) - ); + )); } @Test - void 자신의_리뷰를_별점이_높은_순으로_조회한다() { + void 자신의_리뷰를_별점이_높은_순으로_조회한다() throws Exception { ShopReview 리뷰_5점 = shopReviewFixture.리뷰_5점(준호_학생, 신전_떡볶이); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_준호) - .contentType(ContentType.JSON) - .when() - .pathParam("shopId", 신전_떡볶이.getId()) - .queryParam("sorter", "HIGHEST_RATING") - .get("/shops/{shopId}/reviews/me") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform(get("/shops/{shopId}/reviews/me", 신전_떡볶이.getId()) + .header("Authorization", "Bearer " + token_준호) + .queryParam("sorter", "HIGHEST_RATING") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 2, "reviews": [ @@ -1365,26 +1151,19 @@ void deleteMyReview() { 준호_학생_리뷰.getContent(), 준호_학생_리뷰.getImages().get(0).getImageUrls(), 준호_학생_리뷰.getMenus().get(0).getMenuName()) - ); + )); } @Test - void 자신의_리뷰를_별점이_낮은_순으로_조회한다() { + void 자신의_리뷰를_별점이_낮은_순으로_조회한다() throws Exception { ShopReview 리뷰_5점 = shopReviewFixture.리뷰_5점(준호_학생, 신전_떡볶이); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_준호) - .contentType(ContentType.JSON) - .when() - .pathParam("shopId", 신전_떡볶이.getId()) - .queryParam("sorter", "LOWEST_RATING") - .get("/shops/{shopId}/reviews/me") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform(get("/shops/{shopId}/reviews/me", 신전_떡볶이.getId()) + .header("Authorization", "Bearer " + token_준호) + .queryParam("sorter", "LOWEST_RATING") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "count": 2, "reviews": [ @@ -1433,7 +1212,7 @@ void deleteMyReview() { 리뷰_5점.getContent(), 리뷰_5점.getImages().get(0).getImageUrls(), 리뷰_5점.getMenus().get(0).getMenuName()) - ); + )); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/TimetableApiTest.java b/src/test/java/in/koreatech/koin/acceptance/TimetableApiTest.java index 0b152c932..105a5c35f 100644 --- a/src/test/java/in/koreatech/koin/acceptance/TimetableApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/TimetableApiTest.java @@ -1,12 +1,15 @@ package in.koreatech.koin.acceptance; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import io.restassured.response.Response; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.timetable.model.Lecture; @@ -17,17 +20,10 @@ import in.koreatech.koin.fixture.SemesterFixture; import in.koreatech.koin.fixture.TimeTableV2Fixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class TimetableApiTest extends AcceptanceTest { @Autowired @@ -45,26 +41,26 @@ class TimetableApiTest extends AcceptanceTest { @Autowired private SemesterFixture semesterFixture; + @BeforeAll + void setup() { + clear(); + } + @Test - @DisplayName("특정 학기 강의를 조회한다") - void getSemesterLecture() { + void 특정_학기_강의를_조회한다() throws Exception { semesterFixture.semester("20192"); semesterFixture.semester("20201"); String semester = "20201"; lectureFixture.HRD_개론(semester); lectureFixture.건축구조의_이해_및_실습("20192"); - var response = RestAssured - .given() - .when() - .param("semester_date", semester) - .get("/lectures") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/lectures") + .param("semester_date", semester) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" [ { "id" : 1, @@ -84,29 +80,24 @@ void getSemesterLecture() { ] } ] - """); + """)); } @Test - @DisplayName("특정 학기 강의들을 조회한다") - void getSemesterLectures() { + void 특정_학기_강의들을_조회한다() throws Exception { semesterFixture.semester("20201"); String semester = "20201"; lectureFixture.HRD_개론(semester); lectureFixture.건축구조의_이해_및_실습(semester); lectureFixture.재료역학(semester); - var response = RestAssured - .given() - .when() - .param("semester_date", semester) - .get("/lectures") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/lectures") + .param("semester_date", semester) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" [ { "id" : 1, @@ -160,50 +151,41 @@ void getSemesterLectures() { ] } ] - """); + """)); } @Test - @DisplayName("존재하지 않는 학기를 조회하면 404") - void isNotSemester() { + void 존재하지_않는_학기를_조회하면_404() throws Exception { String semester = "20201"; lectureFixture.HRD_개론(semester); lectureFixture.건축구조의_이해_및_실습(semester); - RestAssured - .given() - .when() - .param("semester_date", "20193") - .get("/lectures") - .then() - .statusCode(HttpStatus.NOT_FOUND.value()) - .extract(); + mockMvc.perform( + get("/lectures") + .param("semester_date", "20193") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNotFound()); } @Test - @DisplayName("계절학기를 조회하면 빈 리스트로 반환한다.") - void getSeasonLecture() { + void 계절학기를_조회하면_빈_리스트로_반환한다() throws Exception { semesterFixture.semester("20241"); semesterFixture.semester("20242"); semesterFixture.semester("2024-여름"); semesterFixture.semester("2024-겨울"); - var Response = RestAssured - .given() - .when() - .param("semester_date", "2024-여름") - .get("/lectures") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(Response.asPrettyString()) - .isEqualTo("[]"); + mockMvc.perform( + get("/lectures") + .param("semester_date", "2024-여름") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json("[]")); } @Test - @DisplayName("모든 학기를 조회한다.") - void findAllSemesters() { + void 모든_학기를_조회한다() throws Exception { semesterFixture.semester("20241"); semesterFixture.semester("20242"); semesterFixture.semester("2024-여름"); @@ -212,16 +194,12 @@ void findAllSemesters() { semesterFixture.semester("2023-여름"); semesterFixture.semester("2023-겨울"); - var response = RestAssured - .given() - .when() - .get("/semesters") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/semesters") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" [ { "id": 2, @@ -252,13 +230,11 @@ void findAllSemesters() { "semester": "20231" } ] - """); + """)); } @Test - @DisplayName("시간표를 조회한다.") - void getTimeTables() { - // given + void 시간표를_조회한다() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); @@ -268,19 +244,14 @@ void getTimeTables() { timetableV2Fixture.시간표6(user, semester, 건축구조의_이해_및_실습, HRD_개론); - // when & then - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .param("semester", semester.getSemester()) - .get("/timetables") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/timetables") + .header("Authorization", "Bearer " + token) + .param("semester", semester.getSemester()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "semester": "20192", "timetable": [ @@ -318,31 +289,23 @@ void getTimeTables() { "grades": 6, "total_grades": 6 } - """ - )); + """)); } @Test - @DisplayName("시간표를 조회한다. - 시간표 프레임 없으면 생성") - void getTimeTablesAfterCreate() { - // given + void 시간표를_조회한다_시간표_프레임_없으면_생성() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); - // when & then - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .param("semester", semester.getSemester()) - .get("/timetables") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/timetables") + .header("Authorization", "Bearer " + token) + .param("semester", semester.getSemester()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "semester": "20192", "timetable": [ @@ -350,13 +313,11 @@ void getTimeTablesAfterCreate() { "grades": 0, "total_grades": 0 } - """ - )); + """)); } @Test - @DisplayName("학생이 가진 시간표의 학기를 조회한다.") - void getStudentCheckSemester() { + void 학생이_가진_시간표의_학기를_조회한다() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester1 = semesterFixture.semester("20192"); @@ -366,18 +327,13 @@ void getStudentCheckSemester() { timetableV2Fixture.시간표6(user, semester1, HRD_개론, null); timetableV2Fixture.시간표6(user, semester2, 건축구조의_이해_및_실습, null); - - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/semesters/check") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/semesters/check") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "user_id": 1, "semesters": [ @@ -385,13 +341,11 @@ void getStudentCheckSemester() { "20192" ] } - """ - ); + """)); } @Test - @DisplayName("시간표를 생성한다.") - void createTimeTables() { + void 시간표를_생성한다() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); @@ -399,99 +353,92 @@ void createTimeTables() { lectureFixture.건축구조의_이해_및_실습(semester.getSemester()); lectureFixture.HRD_개론(semester.getSemester()); - timetableV2Fixture.시간표1(user, semester); + timetableV2Fixture.시간표1(user, semester); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" - { - "timetable": [ - { - "regular_number": "25", - "code": "ARB244", - "design_score": "0", - "class_time": [200, 201, 202, 203, 204, 205, 206, 207], - "class_place": null, - "memo": null, - "grades": "3", - "class_title": "건축구조의 이해 및 실습", - "lecture_class": "01", - "target": "디자 1 건축", - "professor": "황현식", - "department": "디자인ㆍ건축공학부" - }, - { - "regular_number": "22", - "code": "BSM590", - "design_score": "0", - "class_time": [12, 13, 14, 15, 210, 211, 212, 213], - "class_place": null, - "memo": null, - "grades": "3", - "class_title": "컴퓨팅사고", - "lecture_class": "06", - "target": "기공1", - "professor": "박한수,최준호", - "department": "기계공학부" - } - ], - "semester": "20192" - } - """) - .when() - .post("/timetables") - .then() - .statusCode(HttpStatus.OK.value()) - .extract() - .response(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - { - "semester": "20192", - "timetable": [ - { - "id": 1, - "regular_number": "25", - "code": "ARB244", - "design_score": "0", - "class_time": [200, 201, 202, 203, 204, 205, 206, 207], - "class_place": null, - "memo": null, - "grades": "3", - "class_title": "건축구조의 이해 및 실습", - "lecture_class": "01", - "target": "디자 1 건축", - "professor": "황현식", - "department": "디자인ㆍ건축공학부" - }, + mockMvc.perform( + post("/timetables") + .header("Authorization", "Bearer " + token) + .content(""" + { + "timetable": [ + { + "regular_number": "25", + "code": "ARB244", + "design_score": "0", + "class_time": [200, 201, 202, 203, 204, 205, 206, 207], + "class_place": null, + "memo": null, + "grades": "3", + "class_title": "건축구조의 이해 및 실습", + "lecture_class": "01", + "target": "디자 1 건축", + "professor": "황현식", + "department": "디자인ㆍ건축공학부" + }, + { + "regular_number": "22", + "code": "BSM590", + "design_score": "0", + "class_time": [12, 13, 14, 15, 210, 211, 212, 213], + "class_place": null, + "memo": null, + "grades": "3", + "class_title": "컴퓨팅사고", + "lecture_class": "06", + "target": "기공1", + "professor": "박한수,최준호", + "department": "기계공학부" + } + ], + "semester": "20192" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { - "id": 2, - "regular_number": "22", - "code": "BSM590", - "design_score": "0", - "class_time": [12, 13, 14, 15, 210, 211, 212, 213], - "class_place": null, - "memo": null, - "grades": "3", - "class_title": "컴퓨팅사고", - "lecture_class": "06", - "target": "기공1", - "professor": "박한수,최준호", - "department": "기계공학부" + "semester": "20192", + "timetable": [ + { + "id": 1, + "regular_number": "25", + "code": "ARB244", + "design_score": "0", + "class_time": [200, 201, 202, 203, 204, 205, 206, 207], + "class_place": null, + "memo": null, + "grades": "3", + "class_title": "건축구조의 이해 및 실습", + "lecture_class": "01", + "target": "디자 1 건축", + "professor": "황현식", + "department": "디자인ㆍ건축공학부" + }, + { + "id": 2, + "regular_number": "22", + "code": "BSM590", + "design_score": "0", + "class_time": [12, 13, 14, 15, 210, 211, 212, 213], + "class_place": null, + "memo": null, + "grades": "3", + "class_title": "컴퓨팅사고", + "lecture_class": "06", + "target": "기공1", + "professor": "박한수,최준호", + "department": "기계공학부" + } + ], + "grades": 6, + "total_grades": 6 } - ], - "grades": 6, - "total_grades": 6 - } - """); + """)); } @Test - @DisplayName("시간표를 단일 생성한다. - 전체 반환") - void createTimeTablesReturnAll() { + void 시간표를_단일_생성한다_전체_반환() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); @@ -501,115 +448,104 @@ void createTimeTablesReturnAll() { timetableV2Fixture.시간표1(user, semester); - var response1 = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" - { - "timetable": [ - { - "regular_number": "25", - "code": "ARB244", - "design_score": "0", - "class_time": [200, 201, 202, 203, 204, 205, 206, 207], - "class_place": null, - "memo": null, - "grades": "3", - "class_title": "건축구조의 이해 및 실습", - "lecture_class": "01", - "target": "디자 1 건축", - "professor": "황현식", - "department": "디자인ㆍ건축공학부" - } - ], - "semester": "20192" - } - """) - .when() - .post("/timetables") - .then() - .statusCode(HttpStatus.OK.value()) - .extract() - .response(); - - var response2 = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" - { - "timetable": [ - { - "regular_number": "22", - "code": "BSM590", - "design_score": "0", - "class_time": [12, 13, 14, 15, 210, 211, 212, 213], - "class_place": null, - "memo": null, - "grades": "3", - "class_title": "컴퓨팅사고", - "lecture_class": "06", - "target": "기공1", - "professor": "박한수,최준호", - "department": "기계공학부" - } - ], - "semester": "20192" - } - """) - .when() - .post("/timetables") - .then() - .statusCode(HttpStatus.OK.value()) - .extract() - .response(); - - JsonAssertions.assertThat(response2.asPrettyString()) - .isEqualTo(""" - { - "semester": "20192", - "timetable": [ - { - "id": 1, - "regular_number": "25", - "code": "ARB244", - "design_score": "0", - "class_time": [200, 201, 202, 203, 204, 205, 206, 207], - "class_place": null, - "memo": null, - "grades": "3", - "class_title": "건축구조의 이해 및 실습", - "lecture_class": "01", - "target": "디자 1 건축", - "professor": "황현식", - "department": "디자인ㆍ건축공학부" - }, + mockMvc.perform( + post("/timetables") + .header("Authorization", "Bearer " + token) + .content(""" + { + "timetable": [ + { + "regular_number": "25", + "code": "ARB244", + "design_score": "0", + "class_time": [200, 201, 202, 203, 204, 205, 206, 207], + "class_place": null, + "memo": null, + "grades": "3", + "class_title": "건축구조의 이해 및 실습", + "lecture_class": "01", + "target": "디자 1 건축", + "professor": "황현식", + "department": "디자인ㆍ건축공학부" + } + ], + "semester": "20192" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); + + mockMvc.perform( + post("/timetables") + .header("Authorization", "Bearer " + token) + .content(""" + { + "timetable": [ + { + "regular_number": "22", + "code": "BSM590", + "design_score": "0", + "class_time": [12, 13, 14, 15, 210, 211, 212, 213], + "class_place": null, + "memo": null, + "grades": "3", + "class_title": "컴퓨팅사고", + "lecture_class": "06", + "target": "기공1", + "professor": "박한수,최준호", + "department": "기계공학부" + } + ], + "semester": "20192" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { - "id": 2, - "regular_number": "22", - "code": "BSM590", - "design_score": "0", - "class_time": [12, 13, 14, 15, 210, 211, 212, 213], - "class_place": null, - "memo": null, - "grades": "3", - "class_title": "컴퓨팅사고", - "lecture_class": "06", - "target": "기공1", - "professor": "박한수,최준호", - "department": "기계공학부" + "semester": "20192", + "timetable": [ + { + "id": 1, + "regular_number": "25", + "code": "ARB244", + "design_score": "0", + "class_time": [200, 201, 202, 203, 204, 205, 206, 207], + "class_place": null, + "memo": null, + "grades": "3", + "class_title": "건축구조의 이해 및 실습", + "lecture_class": "01", + "target": "디자 1 건축", + "professor": "황현식", + "department": "디자인ㆍ건축공학부" + }, + { + "id": 2, + "regular_number": "22", + "code": "BSM590", + "design_score": "0", + "class_time": [12, 13, 14, 15, 210, 211, 212, 213], + "class_place": null, + "memo": null, + "grades": "3", + "class_title": "컴퓨팅사고", + "lecture_class": "06", + "target": "기공1", + "professor": "박한수,최준호", + "department": "기계공학부" + } + ], + "grades": 6, + "total_grades": 6 } - ], - "grades": 6, - "total_grades": 6 - } - """); + """)); } @Test - @DisplayName("시간표를 삭제한다.") - void deleteTimetable() { + void 시간표를_삭제한다() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); @@ -619,21 +555,20 @@ void deleteTimetable() { timetableV2Fixture.시간표6(user, semester, 건축구조의_이해_및_실습, HRD_개론); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .param("id", 2) - .delete("/timetable") - .then() - .statusCode(HttpStatus.OK.value()); + mockMvc.perform( + delete("/timetable") + .header("Authorization", "Bearer " + token) + .param("id", "2") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); assertThat(timetableRepository.findById(2)).isNotPresent(); } - @Test - @DisplayName("시간표 삭제 동시성 예외 적절하게 처리하는지 테스트한다.") - void deleteTimetableConcurrency() throws InterruptedException { +/* @Test + @Transactional(propagation = Propagation.NOT_SUPPORTED) + void 시간표_삭제_동시성_예외_적절하게_처리하는지_테스트한다() throws InterruptedException { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); @@ -670,5 +605,6 @@ void deleteTimetableConcurrency() throws InterruptedException { assertThat(timetableRepository.findById(2)).isNotPresent(); executor.shutdown(); - } + + }*/ } diff --git a/src/test/java/in/koreatech/koin/acceptance/TimetableV2ApiTest.java b/src/test/java/in/koreatech/koin/acceptance/TimetableV2ApiTest.java index ab36e67ff..f1ab28b8a 100644 --- a/src/test/java/in/koreatech/koin/acceptance/TimetableV2ApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/TimetableV2ApiTest.java @@ -1,15 +1,15 @@ package in.koreatech.koin.acceptance; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.timetable.model.Lecture; @@ -22,11 +22,10 @@ import in.koreatech.koin.fixture.SemesterFixture; import in.koreatech.koin.fixture.TimeTableV2Fixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class TimetableV2ApiTest extends AcceptanceTest { @Autowired @@ -47,78 +46,70 @@ public class TimetableV2ApiTest extends AcceptanceTest { @Autowired private TimetableLectureRepositoryV2 timetableLectureRepositoryV2; + @BeforeAll + void setup() { + clear(); + } + @Test - @DisplayName("특정 시간표 frame을 생성한다") - void createTimeTablesFrame() { + void 특정_시간표_frame을_생성한다() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "semester": "%s" - } - """, semester.getSemester() - )) - .when() - .post("/v2/timetables/frame") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + post("/v2/timetables/frame") + .header("Authorization", "Bearer " + token) + .content(String.format(""" + { + "semester": "%s" + } + """, semester.getSemester() + )) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "id": 1, "timetable_name": "시간표1", "is_main": true } - """); + """)); } @Test - @DisplayName("특정 시간표 frame을 수정한다") - void updateTimetableFrame() { + void 특정_시간표_frame을_수정한다() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); TimetableFrame frame = timetableV2Fixture.시간표1(user, semester); Integer frameId = frame.getId(); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "name": "새로운 이름", - "is_main": true - } - """ - )) - .when() - .put("/v2/timetables/frame/{id}", frameId) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + put("/v2/timetables/frame/{id}", frameId) + .header("Authorization", "Bearer " + token) + .content(String.format(""" + { + "name": "새로운 이름", + "is_main": true + } + """ + )) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "id": 1, "name": "새로운 이름", "is_main": true } - """); + """)); } @Test - @DisplayName("모든 시간표 frame을 조회한다") - void getAllTimeTablesFrame() { + void 모든_시간표_frame을_조회한다() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); @@ -126,18 +117,14 @@ void getAllTimeTablesFrame() { timetableV2Fixture.시간표1(user, semester); timetableV2Fixture.시간표2(user, semester); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .param("semester", semester.getSemester()) - .get("/v2/timetables/frames") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/v2/timetables/frames") + .header("Authorization", "Bearer " + token) + .param("semester", semester.getSemester()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" [ { "id": 1, @@ -150,12 +137,11 @@ void getAllTimeTablesFrame() { "is_main": false } ] - """); + """)); } @Test - @DisplayName("강의를 담고 있는 특정 시간표 frame을 삭제한다") - void deleteTimeTablesFrame() { + void 강의를_담고_있는_특정_시간표_frame을_삭제한다() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); @@ -163,22 +149,20 @@ void deleteTimeTablesFrame() { TimetableFrame frame1 = timetableV2Fixture.시간표5(user, semester, lecture); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .param("id", frame1.getId()) - .delete("/v2/timetables/frame") - .then() - .statusCode(HttpStatus.NO_CONTENT.value()); + mockMvc.perform( + delete("/v2/timetables/frame") + .header("Authorization", "Bearer " + token) + .param("id", String.valueOf(frame1.getId())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); assertThat(timetableFrameRepositoryV2.findById(frame1.getId())).isNotPresent(); assertThat(timetableLectureRepositoryV2.findById(frame1.getTimetableLectures().get(1).getId())).isNotPresent(); } @Test - @DisplayName("isMain인 frame을 삭제한다 - 다른 frame이 main으로 됨") - void deleteMainTimeTablesFrame() { + void isMain인_frame을_삭제한다_다른_frame이_main으로_됨() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); @@ -186,15 +170,13 @@ void deleteMainTimeTablesFrame() { TimetableFrame frame1 = timetableV2Fixture.시간표1(user, semester); TimetableFrame frame2 = timetableV2Fixture.시간표2(user, semester); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .param("id", frame1.getId()) - .delete("/v2/timetables/frame") - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); + mockMvc.perform( + delete("/v2/timetables/frame") + .header("Authorization", "Bearer " + token) + .param("id", String.valueOf(frame1.getId())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); assertThat(timetableFrameRepositoryV2.findById(frame1.getId())).isNotPresent(); @@ -203,8 +185,7 @@ void deleteMainTimeTablesFrame() { } @Test - @DisplayName("특정 시간표 frame을 삭제한다 - 본인 삭제가 아니면 403 반환") - void deleteTimeTablesFrameNoAuth() { + void 특정_시간표_frame을_삭제한다_본인_삭제가_아니면_403_반환() throws Exception { User user1 = userFixture.준호_학생().getUser(); User user2 = userFixture.성빈_학생().getUser(); String token = userFixture.getToken(user2); @@ -212,59 +193,52 @@ void deleteTimeTablesFrameNoAuth() { TimetableFrame frame1 = timetableV2Fixture.시간표1(user1, semester); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .param("id", frame1.getId()) - .delete("/v2/timetables/frame") - .then() - .statusCode(HttpStatus.FORBIDDEN.value()); + mockMvc.perform( + delete("/v2/timetables/frame") + .header("Authorization", "Bearer " + token) + .param("id", String.valueOf(frame1.getId())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isForbidden()); } @Test - @DisplayName("시간표를 생성한다 - TimetableLecture") - void createTimetableLecture() { + void 시간표를_생성한다_TimetableLecture() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); timetableV2Fixture.시간표1(user, semester); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(""" - { - "timetable_frame_id" : 1, - "timetable_lecture": [ - { - "class_title": "커스텀생성1", - "class_time" : [200, 201], - "class_place" : "한기대", - "professor" : "서정빈", - "grades": "2", - "memo" : "메모" - }, - { - "class_title": "커스텀생성2", - "class_time" : [202, 203], - "class_place" : "참빛관 편의점", - "professor" : "감사 서정빈", - "grades": "1", - "memo" : "메모" - } - ] - } - """) - .when() - .post("/v2/timetables/lecture") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + post("/v2/timetables/lecture") + .header("Authorization", "Bearer " + token) + .content(""" + { + "timetable_frame_id" : 1, + "timetable_lecture": [ + { + "class_title": "커스텀생성1", + "class_time" : [200, 201], + "class_place" : "한기대", + "professor" : "서정빈", + "grades": "2", + "memo" : "메모" + }, + { + "class_title": "커스텀생성2", + "class_time" : [202, 203], + "class_place" : "참빛관 편의점", + "professor" : "감사 서정빈", + "grades": "1", + "memo" : "메모" + } + ] + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "timetable_frame_id": 1, "timetable": [ @@ -302,55 +276,49 @@ void createTimetableLecture() { "grades": 3, "total_grades": 3 } - """); + """)); } @Test - @DisplayName("시간표를 수정한다 - TimetableLecture") - void updateTimetableLecture() { + void 시간표를_수정한다_TimetableLecture() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); TimetableFrame frame = timetableV2Fixture.시간표3(user, semester); Integer frameId = frame.getId(); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(""" - { - "timetable_frame_id" : 1, - "timetable_lecture": [ - { - "id": 1, - "class_title": "커스텀바꿔요1", - "class_time" : [200, 201], - "class_place" : "한기대", - "professor" : "서정빈", - "grades" : "0", - "memo" : "메모한당 히히" - }, - { - "id": 2, - "class_title": "커스텀바꿔요2", - "class_time" : [202, 203], - "class_place" : "참빛관 편의점", - "professor" : "알바 서정빈", - "grades" : "0", - "memo" : "메모한당 히히" - } - ] - } - """) - .when() - .put("/v2/timetables/lecture") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + put("/v2/timetables/lecture") + .header("Authorization", "Bearer " + token) + .content(""" + { + "timetable_frame_id" : 1, + "timetable_lecture": [ + { + "id": 1, + "class_title": "커스텀바꿔요1", + "class_time" : [200, 201], + "class_place" : "한기대", + "professor" : "서정빈", + "grades" : "0", + "memo" : "메모한당 히히" + }, + { + "id": 2, + "class_title": "커스텀바꿔요2", + "class_time" : [202, 203], + "class_place" : "참빛관 편의점", + "professor" : "알바 서정빈", + "grades" : "0", + "memo" : "메모한당 히히" + } + ] + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "timetable_frame_id": 1, "timetable": [ @@ -388,12 +356,11 @@ void updateTimetableLecture() { "grades": 0, "total_grades": 0 } - """); + """)); } @Test - @DisplayName("시간표를 조회한다 - TimetableLecture") - void getTimetableLecture() { + void 시간표를_조회한다_TimetableLecture() throws Exception { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); @@ -403,19 +370,14 @@ void getTimetableLecture() { TimetableFrame frame = timetableV2Fixture.시간표6(user, semester, 건축구조의_이해_및_실습, HRD_개론); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .when() - .param("timetable_frame_id", frame.getId()) - .get("/v2/timetables/lecture") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/v2/timetables/lecture") + .header("Authorization", "Bearer " + token) + .param("timetable_frame_id", String.valueOf(frame.getId())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "timetable_frame_id": 1, "timetable": [ @@ -453,12 +415,11 @@ void getTimetableLecture() { "grades": 6, "total_grades": 6 } - """); + """)); } @Test - @DisplayName("시간표에서 특정 강의를 삭제한다") - void deleteTimetableLecture() { + void 시간표에서_특정_강의를_삭제한다() throws Exception { User user1 = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user1); Semester semester = semesterFixture.semester("20192"); @@ -468,18 +429,18 @@ void deleteTimetableLecture() { Integer lectureId = lecture1.getId(); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .delete("/v2/timetables/lecture/{id}", lectureId) - .then() - .statusCode(HttpStatus.NO_CONTENT.value()); + mockMvc.perform( + delete("/v2/timetables/lecture/{id}", lectureId) + .header("Authorization", "Bearer " + token) + .param("timetable_frame_id", String.valueOf(frame.getId())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); } - @Test - @DisplayName("isMain이 false인 frame과 true인 frame을 동시에 삭제한다.") - void deleteNotMainAndMainTimeTablesFrame() { + /*@Test + @Transactional(propagation = Propagation.NOT_SUPPORTED) + void isMain이_false인_frame과_true인_frame을_동시에_삭제한다() { User user = userFixture.준호_학생().getUser(); String token = userFixture.getToken(user); Semester semester = semesterFixture.semester("20192"); @@ -532,5 +493,5 @@ void deleteNotMainAndMainTimeTablesFrame() { TimetableFrame reloadedFrame2 = timetableFrameRepositoryV2.findById(frame2.getId()).orElseThrow(); assertThat(reloadedFrame2.isMain()).isTrue(); - } + }*/ } diff --git a/src/test/java/in/koreatech/koin/acceptance/TrackApiTest.java b/src/test/java/in/koreatech/koin/acceptance/TrackApiTest.java index bb724fa27..73395f629 100644 --- a/src/test/java/in/koreatech/koin/acceptance/TrackApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/TrackApiTest.java @@ -1,19 +1,24 @@ package in.koreatech.koin.acceptance; -import org.junit.jupiter.api.DisplayName; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.member.model.Track; import in.koreatech.koin.fixture.MemberFixture; import in.koreatech.koin.fixture.TechStackFixture; import in.koreatech.koin.fixture.TrackFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class TrackApiTest extends AcceptanceTest { @Autowired @@ -25,23 +30,23 @@ class TrackApiTest extends AcceptanceTest { @Autowired private TechStackFixture techStackFixture; + @BeforeAll + void setup() { + clear(); + } + @Test - @DisplayName("BCSDLab 트랙 정보를 조회한다") - void findTracks() { + void BCSDLab_트랙_정보를_조회한다() throws Exception { trackFixture.backend(); trackFixture.frontend(); trackFixture.ios(); - var response = RestAssured - .given() - .when() - .get("/tracks") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/tracks") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" [ { "id": 1, @@ -68,27 +73,22 @@ void findTracks() { "updated_at": "2024-01-15 12:00:00" } ] - """); + """)); } @Test - @DisplayName("BCSDLab 트랙 정보 단건 조회 - 삭제된 멤버는 조회하지 않는다.") - void findTrackWithoutDeletedMember() { + void BCSDLab_트랙_정보_단건_조회_삭제된_멤버는_조회하지_않는다() throws Exception { Track track = trackFixture.backend(); memberFixture.배진호(track); // 삭제된 멤버 memberFixture.최준호(track); techStackFixture.java(track); - var response = RestAssured - .given() - .when() - .get("/tracks/{id}", track.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/tracks/{id}", track.getId()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "TrackName": "BackEnd", "TechStacks": [ @@ -118,26 +118,21 @@ void findTrackWithoutDeletedMember() { } ] } - """); + """)); } @Test - @DisplayName("BCSDLab 트랙 정보 단건 조회") - void findTrack() { + void BCSDLab_트랙_정보_단건_조회() throws Exception { Track track = trackFixture.backend(); memberFixture.최준호(track); techStackFixture.java(track); - var response = RestAssured - .given() - .when() - .get("/tracks/{id}", track.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/tracks/{id}", track.getId()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "TrackName": "BackEnd", "TechStacks": [ @@ -167,24 +162,19 @@ void findTrack() { } ] } - """); + """)); } @Test - @DisplayName("BCSDLab 트랙 정보 단건 조회 - 트랙에 속한 멤버와 기술스택이 없을 때") - void findTrackWithEmptyMembersAndTechStacks() { + void BCSDLab_트랙_정보_단건_조회_트랙에_속한_멤버와_기술스택이_없을_때() throws Exception { Track track = trackFixture.frontend(); - var response = RestAssured - .given() - .when() - .get("/tracks/{id}", track.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/tracks/{id}", track.getId()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "TrackName": "FrontEnd", "TechStacks": [ @@ -194,6 +184,6 @@ void findTrackWithEmptyMembersAndTechStacks() { ] } - """); + """)); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/UserApiTest.java b/src/test/java/in/koreatech/koin/acceptance/UserApiTest.java index 54a78c1cd..61864152b 100644 --- a/src/test/java/in/koreatech/koin/acceptance/UserApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/UserApiTest.java @@ -6,24 +6,18 @@ import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import in.koreatech.koin.domain.user.model.redis.StudentTemporaryStatus; -import in.koreatech.koin.domain.user.repository.StudentRedisRepository; import org.assertj.core.api.SoftAssertions; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.system.CapturedOutput; -import org.springframework.boot.test.system.OutputCaptureExtension; -import org.springframework.http.HttpStatus; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; import in.koreatech.koin.AcceptanceTest; @@ -32,15 +26,16 @@ import in.koreatech.koin.domain.user.model.Student; import in.koreatech.koin.domain.user.model.User; import in.koreatech.koin.domain.user.model.UserGender; +import in.koreatech.koin.domain.user.model.redis.StudentTemporaryStatus; +import in.koreatech.koin.domain.user.repository.StudentRedisRepository; import in.koreatech.koin.domain.user.repository.StudentRepository; import in.koreatech.koin.domain.user.repository.UserRepository; import in.koreatech.koin.fixture.UserFixture; import in.koreatech.koin.global.auth.JwtProvider; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class UserApiTest extends AcceptanceTest { @Autowired @@ -61,108 +56,93 @@ class UserApiTest extends AcceptanceTest { @Autowired private UserFixture userFixture; + @BeforeAll + void setup() { + clear(); + } + @Test - @DisplayName("학생이 로그인을 진행한다(구 API(/user/login))") - void login() { + void 학생이_로그인을_진행한다_구_API_user_login() throws Exception { Student student = userFixture.성빈_학생(); String email = student.getUser().getEmail(); String password = "1234"; - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .body(""" - { - "email" : "%s", - "password" : "%s" - } - """.formatted(email, password)) - .when() - .post("/user/login") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + mockMvc.perform( + post("/user/login") + .content(""" + { + "email" : "%s", + "password" : "%s" + } + """.formatted(email, password)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); } @Test - @DisplayName("학생이 로그인을 진행한다(신규 API(/student/login))") - void studentLogin() { + void 학생이_로그인을_진행한다_신규_API_student_login() throws Exception { Student student = userFixture.성빈_학생(); String email = student.getUser().getEmail(); String password = "1234"; - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .body(""" - { - "email" : "%s", - "password" : "%s" - } - """.formatted(email, password)) - .when() - .post("/student/login") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + mockMvc.perform( + post("/student/login") + .content(""" + { + "email" : "%s", + "password" : "%s" + } + """.formatted(email, password)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); } @Test - @DisplayName("영양사가 로그인을 진행한다") - void coopLogin() { + void 영양사가_로그인을_진행한다() throws Exception { Coop coop = userFixture.준기_영양사(); String id = coop.getCoopId(); String password = "1234"; - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .body(""" - { - "id" : "%s", - "password" : "%s" - } - """.formatted(id, password)) - .when() - .post("/coop/login") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + mockMvc.perform( + post("/coop/login") + .content(""" + { + "id" : "%s", + "password" : "%s" + } + """.formatted(id, password)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()); } @Test - @DisplayName("올바른 영양사 계정인지 확인한다") - void coopCheckMe() { + void 올바른_영양사_계정인지_확인한다() throws Exception { Coop coop = userFixture.준기_영양사(); String token = userFixture.getToken(coop.getUser()); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/user/coop/me") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + get("/user/coop/me") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); } @Test - @DisplayName("올바른 학생계정인지 확인한다") - void studentCheckMe() { + void 올바른_학생계정인지_확인한다() throws Exception { Student student = userFixture.준호_학생(); String token = userFixture.getToken(student.getUser()); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/user/student/me") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/user/student/me") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "anonymous_nickname": "익명", "email": "juno@koreatech.ac.kr", @@ -173,70 +153,72 @@ void studentCheckMe() { "phone_number": "01012345678", "student_number": "2019136135" } - """); + """)); } @Test - @DisplayName("올바른 학생계정인지 확인한다 - 토큰 정보가 올바르지 않으면 401") - void studentCheckMeUnAuthorized() { + void 올바른_학생계정인지_확인한다_토큰_정보가_올바르지_않으면_401() throws Exception { userFixture.준호_학생(); String token = "invalidToken"; - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/user/student/me") - .then() - .statusCode(HttpStatus.UNAUTHORIZED.value()) - .extract(); + mockMvc.perform( + get("/user/student/me") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isUnauthorized()); } @Test - @DisplayName("올바른 학생계정인지 확인한다 - 회원을 찾을 수 없으면 404") - void studentCheckMeNotFound() { + void 올바른_학생계정인지_확인한다_회원을_찾을_수_없으면_404() throws Exception { Student student = userFixture.준호_학생(); String token = jwtProvider.createToken(student.getUser()); transactionTemplate.executeWithoutResult(status -> studentRepository.deleteByUserId(student.getId()) ); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/user/student/me") - .then() - .statusCode(HttpStatus.NOT_FOUND.value()) - .extract(); + mockMvc.perform( + get("/user/student/me") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNotFound()); } @Test - @DisplayName("학생이 정보를 수정한다") - void studentUpdateMe() { + void 학생이_정보를_수정한다() throws Exception { Student student = userFixture.준호_학생(); String token = userFixture.getToken(student.getUser()); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" - { - "gender" : 1, - "major" : "기계공학부", - "name" : "서정빈", - "password" : "0c4be6acaba1839d3433c1ccf04e1eec4d1fa841ee37cb019addc269e8bc1b77", - "nickname" : "duehee", - "phone_number" : "01023456789", - "student_number" : "2019136136" - } - """) - .when() - .put("/user/student/me") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + put("/user/student/me") + .header("Authorization", "Bearer " + token) + .content(""" + { + "gender" : 1, + "major" : "기계공학부", + "name" : "서정빈", + "password" : "0c4be6acaba1839d3433c1ccf04e1eec4d1fa841ee37cb019addc269e8bc1b77", + "nickname" : "duehee", + "phone_number" : "01023456789", + "student_number" : "2019136136" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "anonymous_nickname": "익명", + "email": "juno@koreatech.ac.kr", + "gender": 1, + "major": "기계공학부", + "name": "서정빈", + "nickname": "duehee", + "phone_number": "01023456789", + "student_number": "2019136136" + } + """)); transactionTemplate.executeWithoutResult(status -> { Student result = studentRepository.getById(student.getId()); @@ -250,33 +232,17 @@ void studentUpdateMe() { } ); }); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - { - "anonymous_nickname": "익명", - "email": "juno@koreatech.ac.kr", - "gender": 1, - "major": "기계공학부", - "name": "서정빈", - "nickname": "duehee", - "phone_number": "01023456789", - "student_number": "2019136136" - } - """); } @Test - @DisplayName("학생이 정보를 수정한다 - 학번의 형식이 맞지 않으면 400") - void studentUpdateMeNotValidStudentNumber() { + void 학생이_정보를_수정한다_학번의_형식이_맞지_않으면_400() throws Exception { Student student = userFixture.준호_학생(); String token = userFixture.getToken(student.getUser()); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" + mockMvc.perform( + put("/user/student/me") + .header("Authorization", "Bearer " + token) + .content(""" { "gender" : 0, "major" : "메카트로닉스공학부", @@ -286,24 +252,20 @@ void studentUpdateMeNotValidStudentNumber() { "student_number" : "201913613" } """) - .when() - .put("/user/student/me") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("학생이 정보를 수정한다 - 학부의 형식이 맞지 않으면 400") - void studentUpdateMeNotValidDepartment() { + void 학생이_정보를_수정한다_학부의_형식이_맞지_않으면_400() throws Exception { Student student = userFixture.준호_학생(); String token = userFixture.getToken(student.getUser()); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" + mockMvc.perform( + put("/user/student/me") + .header("Authorization", "Bearer " + token) + .content(""" { "gender" : 0, "major" : "경영학과", @@ -313,24 +275,20 @@ void studentUpdateMeNotValidDepartment() { "student_number" : "2019136136" } """) - .when() - .put("/user/student/me") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("학생이 정보를 수정한다 - 토큰이 올바르지 않다면 401") - void studentUpdateMeUnAuthorized() { + void 학생이_정보를_수정한다_토큰이_올바르지_않다면_401() throws Exception { userFixture.준호_학생(); String token = "invalidToken"; - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" + mockMvc.perform( + put("/user/student/me") + .header("Authorization", "Bearer " + token) + .content(""" { "gender" : 0, "major" : "메카트로닉스공학부", @@ -340,27 +298,23 @@ void studentUpdateMeUnAuthorized() { "student_number" : "2019136136" } """) - .when() - .put("/user/student/me") - .then() - .statusCode(HttpStatus.UNAUTHORIZED.value()) - .extract(); + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isUnauthorized()); } @Test - @DisplayName("학생이 정보를 수정한다 - 회원을 찾을 수 없다면 404") - void studentUpdateMeNotFound() { + void 학생이_정보를_수정한다_회원을_찾을_수_없다면_404() throws Exception { Student student = userFixture.준호_학생(); String token = userFixture.getToken(student.getUser()); transactionTemplate.executeWithoutResult(status -> studentRepository.deleteByUserId(student.getId()) ); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" + mockMvc.perform( + put("/user/student/me") + .header("Authorization", "Bearer " + token) + .content(""" { "gender" : 0, "major" : "메카트로닉스공학부", @@ -370,25 +324,21 @@ void studentUpdateMeNotFound() { "student_number" : "2019136136" } """) - .when() - .put("/user/student/me") - .then() - .statusCode(HttpStatus.NOT_FOUND.value()) - .extract(); + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNotFound()); } @Test - @DisplayName("학생이 정보를 수정한다 - 이미 있는 닉네임이라면 409") - void studentUpdateMeDuplicationNickname() { + void 학생이_정보를_수정한다_이미_있는_닉네임이라면_409() throws Exception { Student 준호 = userFixture.준호_학생(); Student 성빈 = userFixture.성빈_학생(); String token = userFixture.getToken(준호.getUser()); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(String.format(""" + mockMvc.perform( + put("/user/student/me") + .header("Authorization", "Bearer " + token) + .content(String.format(""" { "gender" : 0, "major" : "테스트학과", @@ -398,128 +348,101 @@ void studentUpdateMeDuplicationNickname() { "student_number" : "2019136136" } """, 성빈.getUser().getNickname())) - .when() - .put("/user/student/me") - .then() - .statusCode(HttpStatus.CONFLICT.value()) - .extract(); + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isConflict()); } @Test - @DisplayName("회원이 탈퇴한다") - void userWithdraw() { + void 회원이_탈퇴한다() throws Exception { Student student = userFixture.성빈_학생(); String token = userFixture.getToken(student.getUser()); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .delete("/user") - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); + mockMvc.perform( + delete("/user") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); assertThat(userRepository.findById(student.getId())).isNotPresent(); } @Test - @DisplayName("이메일이 중복인지 확인한다") - void emailCheckExists() { + void 이메일이_중복인지_확인한다() throws Exception { String email = "notduplicated@koreatech.ac.kr"; - RestAssured - .given() - .param("address", email) - .when() - .get("/user/check/email") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + get("/user/check/email") + .param("address", email) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); assertThat(userRepository.findByEmail(email)).isNotPresent(); } @Test - @DisplayName("이메일이 중복인지 확인한다 - 파라미터에 이메일을 포함하지 않으면 400") - void emailCheckExistsNull() { - RestAssured - .when() - .get("/user/check/email") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); + void 이메일이_중복인지_확인한다_파라미터에_이메일을_포함하지_않으면_400() throws Exception { + mockMvc.perform( + get("/user/check/email") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("이메일이 중복인지 확인한다 - 잘못된 이메일 형식이면 400") - void emailCheckExistsWrongFormat() { + void 이메일이_중복인지_확인한다_잘못된_이메일_형식이면_400() throws Exception { String email = "wrong email format"; - RestAssured - .given() - .param("address", email) - .when() - .get("/user/check/email") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); + mockMvc.perform( + get("/user/check/email") + .param("address", email) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("이메일이 중복인지 확인한다 - 중복이면 422") - void emailCheckExistsAlreadyExists() { + void 이메일이_중복인지_확인한다_중복이면_422() throws Exception { User user = userFixture.성빈_학생().getUser(); - var response = RestAssured - .given() - .param("address", user.getEmail()) - .when() - .get("/user/check/email") - .then() - .statusCode(HttpStatus.CONFLICT.value()) - .extract(); - - assertThat(response.body().jsonPath().getString("message")) - .contains("존재하는 이메일입니다."); + mockMvc.perform( + get("/user/check/email") + .param("address", user.getEmail()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isConflict()) + .andExpect(jsonPath("$.message").value("존재하는 이메일입니다.")); } @Test - @DisplayName("닉네임 중복일때 상태코드 409를 반환한다.") - void checkDuplicationOfNicknameConflict() { + void 닉네임_중복일때_상태코드_409를_반환한다() throws Exception { User user = userFixture.성빈_학생().getUser(); - var response = RestAssured - .given() - .when() - .param("nickname", user.getNickname()) - .get("/user/check/nickname") - .then() - .statusCode(HttpStatus.CONFLICT.value()) - .extract(); - - assertThat(response.body().jsonPath().getString("message")) - .contains("이미 존재하는 닉네임입니다."); + mockMvc.perform( + get("/user/check/nickname") + .param("nickname", user.getNickname()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isConflict()) + .andExpect(jsonPath("$.message").value("이미 존재하는 닉네임입니다.")); } @Test - @DisplayName("닉네임 중복이 아닐시 상태코드 200을 반환한다.") - void checkDuplicationOfNickname() { + void 닉네임_중복이_아닐시_상태코드_200을_반환한다() throws Exception { User user = userFixture.성빈_학생().getUser(); - RestAssured - .given() - .when() - .param("nickname", "철수") - .get("/user/check/nickname") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + get("/user/check/nickname") + .param("nickname", "철수") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); } @Test - @DisplayName("닉네임 제약조건 위반시 상태코드 400를 반환한다.") - void checkDuplicationOfNicknameBadRequest() { + void 닉네임_제약조건_위반시_상태코드_400를_반환한다() throws Exception { User user = User.builder() .password("1234") .nickname("주노") @@ -534,28 +457,23 @@ void checkDuplicationOfNicknameBadRequest() { userRepository.save(user); - RestAssured - .given() - .when() - .param("nickname", "철".repeat(11)) - .get("/user/check/nickname") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); - - RestAssured - .given() - .when() - .param("nickname", "") - .get("/user/check/nickname") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); + mockMvc.perform( + get("/user/check/nickname") + .param("nickname", "철".repeat(11)) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); + + mockMvc.perform( + get("/user/check/nickname") + .param("nickname", "") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("로그인된 사용자의 권한을 조회한다.") - void getAuth() { + void 로그인된_사용자의_권한을_조회한다() throws Exception { Student student = Student.builder() .studentNumber("2019136135") .anonymousNickname("익명") @@ -579,248 +497,213 @@ void getAuth() { studentRepository.save(student); String token = jwtProvider.createToken(student.getUser()); - - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/user/auth") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - User user = student.getUser(); - assertSoftly( - softly -> { - softly.assertThat(response.body().jsonPath().getString("user_type")) - .isEqualTo(user.getUserType().getValue()); - } - ); + mockMvc.perform( + get("/user/auth") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.user_type").value(user.getUserType().getValue())); } @Test - @DisplayName("학생 회원가입 후 학교 이메일요청 이벤트가 발생하고 Redis에 저장된다.") - void studentRegister() { - var response = RestAssured - .given() - .body(""" - { - "major": "컴퓨터공학부", - "email": "koko123@koreatech.ac.kr", - "name": "김철수", - "password": "cd06f8c2b0dd065faf6ef910c7f15934363df71c33740fd245590665286ed268", - "nickname": "koko", - "gender": "0", - "is_graduated": false, - "student_number": "2021136012", - "phone_number": "01000000000" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/user/student/register") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - transactionTemplate.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - Optional student = studentRedisRepository.findById("koko123@koreatech.ac.kr"); - - assertSoftly( - softly -> { - softly.assertThat(student).isNotNull(); - softly.assertThat(student.get().getNickname()).isEqualTo("koko"); - softly.assertThat(student.get().getName()).isEqualTo("김철수"); - softly.assertThat(student.get().getPhoneNumber()).isEqualTo("01000000000"); - softly.assertThat(student.get().getEmail()).isEqualTo("koko123@koreatech.ac.kr"); - softly.assertThat(student.get().getStudentNumber()).isEqualTo("2021136012"); - softly.assertThat(student.get().getDepartment()).isEqualTo(Dept.COMPUTER_SCIENCE.getName()); - verify(studentEventListener).onStudentEmailRequest(any()); + void 학생_회원가입_후_학교_이메일요청_이벤트가_발생하고_Redis에_저장된다() throws Exception { + mockMvc.perform( + post("/user/student/register") + .content(""" + { + "major": "컴퓨터공학부", + "email": "koko123@koreatech.ac.kr", + "name": "김철수", + "password": "cd06f8c2b0dd065faf6ef910c7f15934363df71c33740fd245590665286ed268", + "nickname": "koko", + "gender": "0", + "is_graduated": false, + "student_number": "2021136012", + "phone_number": "01000000000" } - ); - } - }); - } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); - @Test - @DisplayName("이메일 요청을 확인 후 회원가입 이벤트가 발생하고 Redis에 저장된 정보가 삭제된다.") - void authenticate() { - RestAssured - .given() - .body(""" - { - "major": "컴퓨터공학부", - "email": "koko123@koreatech.ac.kr", - "name": "김철수", - "password": "cd06f8c2b0dd065faf6ef910c7f15934363df71c33740fd245590665286ed268", - "nickname": "koko", - "gender": "0", - "is_graduated": false, - "student_number": "2021136012", - "phone_number": "01000000000" + Optional student = studentRedisRepository.findById("koko123@koreatech.ac.kr"); + + assertSoftly( + softly -> { + softly.assertThat(student).isNotNull(); + softly.assertThat(student.get().getNickname()).isEqualTo("koko"); + softly.assertThat(student.get().getName()).isEqualTo("김철수"); + softly.assertThat(student.get().getPhoneNumber()).isEqualTo("01000000000"); + softly.assertThat(student.get().getEmail()).isEqualTo("koko123@koreatech.ac.kr"); + softly.assertThat(student.get().getStudentNumber()).isEqualTo("2021136012"); + softly.assertThat(student.get().getDepartment()).isEqualTo(Dept.COMPUTER_SCIENCE.getName()); + forceVerify(() -> verify(studentEventListener).onStudentEmailRequest(any())); + clear(); + setup(); } - """) - .contentType(ContentType.JSON) - .when() - .post("/user/student/register") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + ); +} + + @Test + void 이메일_요청을_확인_후_회원가입_이벤트가_발생하고_Redis에_저장된_정보가_삭제된다() throws Exception { + mockMvc.perform( + post("/user/student/register") + .content(""" + { + "major": "컴퓨터공학부", + "email": "koko123@koreatech.ac.kr", + "name": "김철수", + "password": "cd06f8c2b0dd065faf6ef910c7f15934363df71c33740fd245590665286ed268", + "nickname": "koko", + "gender": "0", + "is_graduated": false, + "student_number": "2021136012", + "phone_number": "01000000000" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); - Optional student = studentRedisRepository.findById("koko123@koreatech.ac.kr"); - RestAssured - .given() - .param("auth_token", student.get().getAuthToken()) - .when() - .get("/user/authenticate") - .then(); + Optional student = studentRedisRepository.findById("koko123@koreatech.ac.kr"); + mockMvc.perform( + get("/user/authenticate") + .queryParam("auth_token", student.get().getAuthToken()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andReturn(); User user = userRepository.getByEmail("koko123@koreatech.ac.kr"); assertThat(studentRedisRepository.findById("koko123@koreatech.ac.kr")).isEmpty(); assertThat(user.isAuthed()).isTrue(); - verify(studentEventListener).onStudentRegister(any()); + forceVerify(() -> verify(studentEventListener).onStudentRegister(any())); + clear(); + setup(); } @Test - @DisplayName("회원 가입 필수 파라미터를 안넣을시 400에러코드를 반환한다.") - void studentRegisterBadRequest() { - RestAssured - .given() - .body(""" - { - "major": "컴퓨터공학부", - "email": null, - "name": "김철수", - "password": "cd06f8c2b0dd065faf6ef910c7f15934363df71c33740fd245590665286ed268", - "nickname": "koko", - "gender": "0", - "is_graduated": false, - "student_number": "2021136012", - "phone_number": "01000000000" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/user/student/register") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()); + void 회원_가입_필수_파라미터를_안넣을시_400에러코드를_반환한다() throws Exception { + mockMvc.perform( + post("/user/student/register") + .content(""" + { + "major": "컴퓨터공학부", + "email": null, + "name": "김철수", + "password": "cd06f8c2b0dd065faf6ef910c7f15934363df71c33740fd245590665286ed268", + "nickname": "koko", + "gender": "0", + "is_graduated": false, + "student_number": "2021136012", + "phone_number": "01000000000" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("한기대 이메일이 아닐시 400에러코드를 반환한다.") - void studentRegisterInvalid() { - RestAssured - .given() - .body(""" - { - "major": "컴퓨터공학부", - "email": "koko123@gmail.com", - "name": "김철수", - "password": "cd06f8c2b0dd065faf6ef910c7f15934363df71c33740fd245590665286ed268", - "nickname": "koko", - "gender": "0", - "is_graduated": false, - "student_number": "2021136012", - "phone_number": "01000000000" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/user/student/register") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()); + void 한기대_이메일이_아닐시_400에러코드를_반환한다() throws Exception { + mockMvc.perform( + post("/user/student/register") + .content(""" + { + "major": "컴퓨터공학부", + "email": "koko123@gmail.com", + "name": "김철수", + "password": "cd06f8c2b0dd065faf6ef910c7f15934363df71c33740fd245590665286ed268", + "nickname": "koko", + "gender": "0", + "is_graduated": false, + "student_number": "2021136012", + "phone_number": "01000000000" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("유효한 학번의 형식이 아닐시 400에러코드를 반환한다.") - void studentRegisterStudentNumberInvalid() { - RestAssured - .given() - .body(""" - { - "major": "컴퓨터공학부", - "email": "koko123@gmail.com", - "name": "김철수", - "password": "cd06f8c2b0dd065faf6ef910c7f15934363df71c33740fd245590665286ed268", - "nickname": "koko", - "gender": "0", - "is_graduated": false, - "student_number": "20211360123324231", - "phone_number": "01000000000" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/user/student/register") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()); - - RestAssured - .given() - .body(""" - { - "major": "컴퓨터공학부", - "email": "koko123@gmail.com", - "name": "김철수", - "password": "cd06f8c2b0dd065faf6ef910c7f15934363df71c33740fd245590665286ed268", - "nickname": "koko", - "gender": "0", - "is_graduated": false, - "student_number": "19911360123", - "phone_number": "01000000000" - } - """) - .contentType(ContentType.JSON) - .when() - .post("/user/student/register") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()); + void 유효한_학번의_형식이_아닐시_400에러코드를_반환한다() throws Exception { + mockMvc.perform( + post("/user/student/register") + .content(""" + { + "major": "컴퓨터공학부", + "email": "koko123@gmail.com", + "name": "김철수", + "password": "cd06f8c2b0dd065faf6ef910c7f15934363df71c33740fd245590665286ed268", + "nickname": "koko", + "gender": "0", + "is_graduated": false, + "student_number": "20211360123324231", + "phone_number": "01000000000" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); + + mockMvc.perform( + post("/user/student/register") + .content(""" + { + "major": "컴퓨터공학부", + "email": "koko123@gmail.com", + "name": "김철수", + "password": "cd06f8c2b0dd065faf6ef910c7f15934363df71c33740fd245590665286ed268", + "nickname": "koko", + "gender": "0", + "is_graduated": false, + "student_number": "19911360123", + "phone_number": "01000000000" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("사용자가 비밀번호를 통해 자신이 맞는지 인증한다.") - void userCheckPassword() { + void 사용자가_비밀번호를_통해_자신이_맞는지_인증한다() throws Exception { Student student = userFixture.준호_학생(); String token = userFixture.getToken(student.getUser()); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" - { - "password": "1234" - } - """) - .when() - .post("/user/check/password") - .then() - .statusCode(HttpStatus.OK.value()); + mockMvc.perform( + post("/user/check/password") + .header("Authorization", "Bearer " + token) + .content(""" + { + "password": "1234" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); } @Test - @DisplayName("사용자가 비밀번호를 통해 자신이 맞는지 인증한다. - 비밀번호가 다르면 400 반환") - void userCheckPasswordInvalid() { + void 사용자가_비밀번호를_통해_자신이_맞는지_인증한다_비밀번호가_다르면_400_반환() throws Exception { Student student = userFixture.준호_학생(); String token = userFixture.getToken(student.getUser()); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" - { - "password": "1233" - } - """) - .when() - .post("/user/check/password") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()); + mockMvc.perform( + post("/user/check/password") + .header("Authorization", "Bearer " + token) + .content(""" + { + "password": "123" + } + """) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isBadRequest()); } } diff --git a/src/test/java/in/koreatech/koin/acceptance/VersionApiTest.java b/src/test/java/in/koreatech/koin/acceptance/VersionApiTest.java index aa03398a0..14664f789 100644 --- a/src/test/java/in/koreatech/koin/acceptance/VersionApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/VersionApiTest.java @@ -1,26 +1,35 @@ package in.koreatech.koin.acceptance; -import org.junit.jupiter.api.DisplayName; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.version.model.Version; import in.koreatech.koin.domain.version.model.VersionType; import in.koreatech.koin.domain.version.repository.VersionRepository; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class VersionApiTest extends AcceptanceTest { @Autowired private VersionRepository versionRepository; + @BeforeAll + void setup() { + clear(); + } + @Test - @DisplayName("버전 타입을 통해 버전 정보를 조회한다.") - void findVersionByType() { + void 버전_타입을_통해_버전_정보를_조회한다() throws Exception { Version version = versionRepository.save( Version.builder() .version("1.0.0") @@ -28,17 +37,12 @@ void findVersionByType() { .build() ); - // when then - var response = RestAssured - .given() - .when() - .get("/versions/" + version.getType()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/versions/" + version.getType()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "id": %d, "version": "1.0.0", @@ -47,28 +51,24 @@ void findVersionByType() { "updated_at": "2024-01-15" } """, version.getId() - )); + ))); } @Test - @DisplayName("버전 타입을 통해 버전 정보를 조회한다. - 저장되지 않은 버전 타입을 요청한 경우 에러가 발생한다.") - void findVersionByTypeError() { + void 버전_타입을_통해_버전_정보를_조회한다_저장되지_않은_버전_타입을_요청한_경우_에러가_발생한다() throws Exception { VersionType failureType = VersionType.TIMETABLE; - RestAssured - .given() - .when() - .get("/versions/" + failureType.getValue()) - .then() - .statusCode(HttpStatus.NOT_FOUND.value()) - .extract(); - String undefinedType = "undefined"; - RestAssured - .given() - .when() - .get("/versions/" + undefinedType) - .then() - .statusCode(HttpStatus.NOT_FOUND.value()) - .extract(); + + mockMvc.perform( + get("/versions/" + failureType.getValue()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNotFound()); + + mockMvc.perform( + get("/versions/" + undefinedType) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNotFound()); } } diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminCoopShopTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminCoopShopTest.java index 23168cc49..ff8998188 100644 --- a/src/test/java/in/koreatech/koin/admin/acceptance/AdminCoopShopTest.java +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminCoopShopTest.java @@ -1,11 +1,13 @@ package in.koreatech.koin.admin.acceptance; -import static io.restassured.RestAssured.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.domain.coopshop.model.CoopShop; @@ -13,10 +15,10 @@ import in.koreatech.koin.domain.user.model.User; import in.koreatech.koin.fixture.CoopShopFixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; @SuppressWarnings("NonAsciiCharacters") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Transactional class AdminCoopShopTest extends AcceptanceTest { @Autowired @@ -33,8 +35,9 @@ class AdminCoopShopTest extends AcceptanceTest { private User admin; private String token_admin; - @BeforeEach + @BeforeAll void setUp() { + clear(); 학생식당 = coopShopFixture.학생식당(); 세탁소 = coopShopFixture.세탁소(); admin = userFixture.코인_운영자(); @@ -42,23 +45,16 @@ void setUp() { } @Test - public void getCoopShops() { - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .when() - .param("page", 1) - .param("is_deleted", false) - .get("/admin/coopshop") - .then() - .log().all() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo( - """ - { + void 생협의_모든_상점을_조회한다() throws Exception { + mockMvc.perform( + get("/admin/coopshop") + .header("Authorization", "Bearer " + token_admin) + .param("page", "1") + .param("is_deleted", "false") + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { "totalCount": 2, "currentCount": 2, "totalPage": 1, @@ -124,25 +120,18 @@ public void getCoopShops() { } ] } - """ - ); + """)); } @Test - public void getCoopShop() { - var response = given() - .header("Authorization", "Bearer " + token_admin) - .when() - .get("/coopshop/2") - .then() - .log().all() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo( - """ - { + void 생협의_특정_상점을_조회한다() throws Exception { + mockMvc.perform( + get("/coopshop/2") + .header("Authorization", "Bearer " + token_admin) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { "id": 2, "name": "세탁소", "semester": "학기", @@ -164,8 +153,7 @@ public void getCoopShop() { "location": "학생회관 2층", "remarks": "연중무휴", "updated_at" : "2024-01-15" - } - """ - ); + } + """)); } } diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminLandApiTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminLandApiTest.java index cad868d9d..55e48daf8 100644 --- a/src/test/java/in/koreatech/koin/admin/acceptance/AdminLandApiTest.java +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminLandApiTest.java @@ -2,14 +2,16 @@ import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.hibernate.validator.internal.util.Contracts.assertNotNull; -import static org.junit.Assert.assertEquals; -import static org.junit.jupiter.api.Assertions.assertAll; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import org.assertj.core.api.SoftAssertions; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.admin.land.repository.AdminLandRepository; @@ -17,10 +19,10 @@ import in.koreatech.koin.domain.user.model.User; import in.koreatech.koin.fixture.LandFixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; @SuppressWarnings("NonAsciiCharacters") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Transactional class AdminLandApiTest extends AcceptanceTest { @Autowired @@ -32,9 +34,13 @@ class AdminLandApiTest extends AcceptanceTest { @Autowired private UserFixture userFixture; + @BeforeAll + void setup() { + clear(); + } + @Test - @DisplayName("관리자 권한으로 복덕방 목록을 검색한다.") - void getLands() { + void 관리자_권한으로_복덕방_목록을_검색한다() throws Exception { for (int i = 0; i < 11; i++) { Land request = Land.builder() .internalName("복덕방" + i) @@ -52,31 +58,22 @@ void getLands() { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .param("page", 1) - .param("is_deleted", false) - .get("/admin/lands") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertSoftly( - softly -> { - softly.assertThat(response.body().jsonPath().getInt("total_count")).isEqualTo(11); - softly.assertThat(response.body().jsonPath().getInt("current_count")).isEqualTo(10); - softly.assertThat(response.body().jsonPath().getInt("total_page")).isEqualTo(2); - softly.assertThat(response.body().jsonPath().getInt("current_page")).isEqualTo(1); - softly.assertThat(response.body().jsonPath().getList("lands").size()).isEqualTo(10); - } - ); + mockMvc.perform( + get("/admin/lands") + .header("Authorization", "Bearer " + token) + .param("page", "1") + .param("is_deleted", "false") + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.total_count").value(11)) + .andExpect(jsonPath("$.current_count").value(10)) + .andExpect(jsonPath("$.total_page").value(2)) + .andExpect(jsonPath("$.current_page").value(1)) + .andExpect(jsonPath("$.lands.length()").value(10)); } @Test - @DisplayName("관리자 권한으로 복덕방을 추가한다.") - void postLands() { + void 관리자_권한으로_복덕방을_추가한다() throws Exception { String jsonBody = """ { "name": "금실타운", @@ -108,16 +105,13 @@ void postLands() { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(jsonBody) - .when() - .post("/admin/lands") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract().asString(); + mockMvc.perform( + post("/admin/lands") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonBody) + ) + .andExpect(status().isCreated()); Land savedLand = adminLandRepository.getByName("금실타운"); assertNotNull(savedLand); @@ -132,8 +126,7 @@ void postLands() { } @Test - @DisplayName("관리자 권한으로 복덕방을 삭제한다.") - void deleteLand() { + void 관리자_권한으로_복덕방을_삭제한다() throws Exception { // 복덕방 생성 Land request = Land.builder() .internalName("금실타운") @@ -151,14 +144,11 @@ void deleteLand() { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .delete("/admin/lands/{id}", landId) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + delete("/admin/lands/{id}", landId) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()); Land deletedLand = adminLandRepository.getById(landId); @@ -169,8 +159,7 @@ void deleteLand() { } @Test - @DisplayName("관리자의 권한으로 특정 복덕방 정보를 조회한다.") - void getLand() { + void 관리자의_권한으로_특정_복덕방_정보를_조회한다() throws Exception { // 복덕방 생성 Land request = Land.builder() .internalName("금실타운") @@ -191,58 +180,52 @@ void getLand() { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/admin/lands/{id}", landId) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" - { - "id": %d, - "name": "금실타운", - "internal_name": "금실타운", - "size": 9.0, - "room_type": "원룸", - "latitude": 37.555, - "longitude": 126.555, - "phone": null, - "image_urls": [], - "address": "가전리 123", - "description": "테스트용 복덕방", - "floor": null, - "deposit": null, - "monthly_fee": "100", - "charter_fee": "1000", - "management_fee": null, - "opt_closet": false, - "opt_tv": false, - "opt_microwave": false, - "opt_gas_range": false, - "opt_induction": false, - "opt_water_purifier": false, - "opt_air_conditioner": false, - "opt_washer": false, - "opt_bed": false, - "opt_bidet": false, - "opt_desk": false, - "opt_electronic_door_locks": false, - "opt_elevator": false, - "opt_refrigerator": false, - "opt_shoe_closet": false, - "opt_veranda": false, - "is_deleted": false - } - """, landId)); + mockMvc.perform( + get("/admin/lands/{id}", landId) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" + { + "id": %d, + "name": "금실타운", + "internal_name": "금실타운", + "size": 9.0, + "room_type": "원룸", + "latitude": 37.555, + "longitude": 126.555, + "phone": null, + "image_urls": [], + "address": "가전리 123", + "description": "테스트용 복덕방", + "floor": null, + "deposit": null, + "monthly_fee": "100", + "charter_fee": "1000", + "management_fee": null, + "opt_closet": false, + "opt_tv": false, + "opt_microwave": false, + "opt_gas_range": false, + "opt_induction": false, + "opt_water_purifier": false, + "opt_air_conditioner": false, + "opt_washer": false, + "opt_bed": false, + "opt_bidet": false, + "opt_desk": false, + "opt_electronic_door_locks": false, + "opt_elevator": false, + "opt_refrigerator": false, + "opt_shoe_closet": false, + "opt_veranda": false, + "is_deleted": false + } + """, landId))); } @Test - @DisplayName("관리자 권한으로 복덕방 정보를 수정한다.") - void updateLand() { + void 관리자_권한으로_복덕방_정보를_수정한다() throws Exception { Land land = landFixture.신안빌(); Integer landId = land.getId(); @@ -277,16 +260,13 @@ void updateLand() { } """; - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(jsonBody) - .when() - .put("/admin/lands/{id}", landId) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + put("/admin/lands/{id}", landId) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonBody) + ) + .andExpect(status().isOk()); Land updatedLand = adminLandRepository.getById(landId); @@ -319,22 +299,18 @@ void updateLand() { } @Test - @DisplayName("관리자 권한으로 복덕방 삭제를 취소한다.") - void undeleteLand() { + void 관리자_권한으로_복덕방_삭제를_취소한다() throws Exception { Land deletedLand = landFixture.삭제된_복덕방(); Integer landId = deletedLand.getId(); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .post("/admin/lands/{id}/undelete", landId) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + post("/admin/lands/{id}/undelete", landId) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()); Land undeletedLand = adminLandRepository.getById(landId); @@ -344,5 +320,4 @@ void undeleteLand() { softly.assertThat(undeletedLand.isDeleted()).isFalse(); }); } - } diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminMemberApiTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminMemberApiTest.java index d65b45b6e..e706914ee 100644 --- a/src/test/java/in/koreatech/koin/admin/acceptance/AdminMemberApiTest.java +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminMemberApiTest.java @@ -1,12 +1,15 @@ package in.koreatech.koin.admin.acceptance; -import static org.hibernate.validator.internal.util.Contracts.assertNotNull; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import org.assertj.core.api.SoftAssertions; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.admin.member.repository.AdminMemberRepository; @@ -15,10 +18,10 @@ import in.koreatech.koin.fixture.MemberFixture; import in.koreatech.koin.fixture.TrackFixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; @SuppressWarnings("NonAsciiCharacters") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Transactional public class AdminMemberApiTest extends AcceptanceTest { @Autowired @@ -33,28 +36,27 @@ public class AdminMemberApiTest extends AcceptanceTest { @Autowired private AdminMemberRepository adminMemberRepository; + @BeforeAll + void setup() { + clear(); + } + @Test - @DisplayName("BCSDLab 회원들의 정보를 조회한다") - void getMembers() { + void BCSDLab_회원들의_정보를_조회한다() throws Exception { memberFixture.최준호(trackFixture.backend()); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .param("page", 1) - .param("track", "BACKEND") - .param("is_deleted", false) - .get("/admin/members") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/admin/members") + .header("Authorization", "Bearer " + token) + .param("page", "1") + .param("track", "BACKEND") + .param("is_deleted", "false") + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "total_count": 1, "current_count": 1, @@ -73,13 +75,11 @@ void getMembers() { } ] } - """ - ); + """)); } @Test - @DisplayName("관리자 권한으로 BCSDLab 회원을 추가한다.") - void postMember() { + void 관리자_권한으로_BCSDLab_회원을_추가한다() throws Exception { trackFixture.backend(); User adminUser = userFixture.코인_운영자(); @@ -96,16 +96,13 @@ void postMember() { } """; - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(jsonBody) - .when() - .post("/admin/members") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract().asString(); + mockMvc.perform( + post("/admin/members") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonBody) + ) + .andExpect(status().isCreated()); Member savedMember = adminMemberRepository.getByName("최준호"); @@ -121,24 +118,18 @@ void postMember() { } @Test - @DisplayName("BCSDLab 회원 정보를 조회한다") - void getMember() { + void BCSDLab_회원_정보를_조회한다() throws Exception { memberFixture.최준호(trackFixture.backend()); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/admin/members/{id}", 1) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/admin/members/{id}", 1) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "id": 1, "name": "최준호", @@ -149,27 +140,22 @@ void getMember() { "image_url": "https://imagetest.com/juno.jpg", "is_deleted": false } - """ - ); + """)); } @Test - @DisplayName("BCSDLab 회원 정보를 삭제한다") - void deleteMember() { + void BCSDLab_회원_정보를_삭제한다() throws Exception { Member member = memberFixture.최준호(trackFixture.backend()); Integer memberId = member.getId(); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .delete("/admin/members/{id}", memberId) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + delete("/admin/members/{id}", memberId) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()); Member savedMember = adminMemberRepository.getById(memberId); @@ -185,8 +171,7 @@ void deleteMember() { } @Test - @DisplayName("BCSDLab 회원 정보를 수정한다") - void updateMember() { + void BCSDLab_회원_정보를_수정한다() throws Exception { Member member = memberFixture.최준호(trackFixture.backend()); Integer memberId = member.getId(); @@ -204,16 +189,13 @@ void updateMember() { } """; - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(jsonBody) - .when() - .put("/admin/members/{id}", memberId) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + put("/admin/members/{id}", memberId) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonBody) + ) + .andExpect(status().isOk()); Member updatedMember = adminMemberRepository.getById(memberId); @@ -229,8 +211,7 @@ void updateMember() { } @Test - @DisplayName("BCSDLab 회원 정보를 트랙과 함께 수정한다") - void updateMemberWithTrack() { + void BCSDLab_회원_정보를_트랙과_함께_수정한다() throws Exception { Member member = memberFixture.최준호(trackFixture.backend()); trackFixture.frontend(); Integer memberId = member.getId(); @@ -249,16 +230,13 @@ void updateMemberWithTrack() { } """; - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(jsonBody) - .when() - .put("/admin/members/{id}", memberId) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + put("/admin/members/{id}", memberId) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonBody) + ) + .andExpect(status().isOk()); Member updatedMember = adminMemberRepository.getById(memberId); @@ -274,22 +252,18 @@ void updateMemberWithTrack() { } @Test - @DisplayName("BCSDLab 회원 정보를 삭제를 취소한다") - void undeleteMember() { + void BCSDLab_회원_정보를_삭제를_취소한다() throws Exception { Member member = memberFixture.최준호_삭제(trackFixture.backend()); Integer memberId = member.getId(); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .post("/admin/members/{id}/undelete", memberId) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + post("/admin/members/{id}/undelete", memberId) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()); Member savedMember = adminMemberRepository.getById(memberId); diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminShopApiTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminShopApiTest.java index 41f5e0a11..13e8ceaa0 100644 --- a/src/test/java/in/koreatech/koin/admin/acceptance/AdminShopApiTest.java +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminShopApiTest.java @@ -2,15 +2,19 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.util.List; import java.util.Set; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; import in.koreatech.koin.AcceptanceTest; @@ -35,13 +39,16 @@ import in.koreatech.koin.fixture.ShopCategoryFixture; import in.koreatech.koin.fixture.ShopFixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; +import jakarta.persistence.EntityManager; @SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class AdminShopApiTest extends AcceptanceTest { + @Autowired + EntityManager entityManager; + @Autowired private TransactionTemplate transactionTemplate; @@ -82,8 +89,9 @@ class AdminShopApiTest extends AcceptanceTest { private MenuCategory menuCategory_메인; private MenuCategory menuCategory_사이드; - @BeforeEach + @BeforeAll void setUp() { + clear(); admin = userFixture.코인_운영자(); token_admin = userFixture.getToken(admin); owner_현수 = userFixture.현수_사장님(); @@ -96,8 +104,7 @@ void setUp() { } @Test - @DisplayName("어드민이 모든 상점을 조회한다.") - void findAllShops() { + void 어드민이_모든_상점을_조회한다() throws Exception { for (int i = 0; i < 12; i++) { Shop request = shopFixture.builder() .owner(owner_현수) @@ -118,42 +125,28 @@ void findAllShops() { adminShopRepository.save(request); } - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .when() - .param("page", 1) - .param("is_deleted", false) - .get("/admin/shops") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertSoftly( - softly -> { - softly.assertThat(response.body().jsonPath().getInt("total_count")).isEqualTo(13); - softly.assertThat(response.body().jsonPath().getInt("current_count")).isEqualTo(10); - softly.assertThat(response.body().jsonPath().getInt("total_page")).isEqualTo(2); - softly.assertThat(response.body().jsonPath().getInt("current_page")).isEqualTo(1); - softly.assertThat(response.body().jsonPath().getList("shops").size()).isEqualTo(10); - } - ); + mockMvc.perform( + get("/admin/shops") + .header("Authorization", "Bearer " + token_admin) + .param("page", "1") + .param("is_deleted", "false") + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.total_count").value(13)) + .andExpect(jsonPath("$.current_count").value(10)) + .andExpect(jsonPath("$.total_page").value(2)) + .andExpect(jsonPath("$.current_page").value(1)) + .andExpect(jsonPath("$.shops.length()").value(10)); } @Test - @DisplayName("어드민이 특정 상점을 조회한다.") - void findShop() { - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .when() - .get("/admin/shops/{shopId}", shop_마슬랜.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + void 어드민이_특정_상점을_조회한다() throws Exception { + mockMvc.perform( + get("/admin/shops/{shopId}", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_admin) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "address": "천안시 동남구 병천면 1600", "delivery": true, @@ -201,85 +194,62 @@ void findShop() { "bank": "국민", "account_number": "01022595923" } - """); + """)); } @Test - @DisplayName("어드민이 상점의 모든 카테고리를 조회한다.") - void findShopCategories() { + void 어드민이_상점의_모든_카테고리를_조회한다() throws Exception { for (int i = 0; i < 12; i++) { ShopCategory request = ShopCategory.builder() .name("카테고리" + i) .isDeleted(false) .build(); adminShopCategoryRepository.save(request); - System.out.println(i); } - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .when() - .param("page", 1) - .param("is_deleted", false) - .get("/admin/shops/categories") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertSoftly( - softly -> { - softly.assertThat(response.body().jsonPath().getInt("total_count")).isEqualTo(14); - softly.assertThat(response.body().jsonPath().getInt("current_count")).isEqualTo(10); - softly.assertThat(response.body().jsonPath().getInt("total_page")).isEqualTo(2); - softly.assertThat(response.body().jsonPath().getInt("current_page")).isEqualTo(1); - softly.assertThat(response.body().jsonPath().getList("categories").size()).isEqualTo(10); - } - ); + mockMvc.perform( + get("/admin/shops/categories") + .header("Authorization", "Bearer " + token_admin) + .param("page", "1") + .param("is_deleted", "false") + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.total_count").value(14)) + .andExpect(jsonPath("$.current_count").value(10)) + .andExpect(jsonPath("$.total_page").value(2)) + .andExpect(jsonPath("$.current_page").value(1)) + .andExpect(jsonPath("$.categories.length()").value(10)); } @Test - @DisplayName("어드민이 상점의 특정 카테고리를 조회한다.") - void findShopCategory() { + void 어드민이_상점의_특정_카테고리를_조회한다() throws Exception { ShopCategory shopCategory = shopCategoryFixture.카테고리_치킨(); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .pathParam("id", shopCategory.getId()) - .when() - .get("/admin/shops/categories/{id}") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/admin/shops/categories/{id}", shopCategory.getId()) + .header("Authorization", "Bearer " + token_admin) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "id": 3, "image_url": "https://test-image.com/ckicken.jpg", "name": "치킨" } - """); + """)); } @Test - @DisplayName("어드민이 특정 상점의 모든 메뉴를 조회한다.") - void findShopMenus() { + void 어드민이_특정_상점의_모든_메뉴를_조회한다() throws Exception { // given menuFixture.짜장면_옵션메뉴(shop_마슬랜, menuCategory_메인); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .pathParam("id", shop_마슬랜.getId()) - .when() - .get("/admin/shops/{id}/menus") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + + mockMvc.perform( + get("/admin/shops/{id}/menus", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_admin) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "count": 1, "menu_categories": [ @@ -314,25 +284,20 @@ void findShopMenus() { ], "updated_at": "2024-01-15" } - """); + """)); } @Test - @DisplayName("어드민이 특정 상점의 메뉴 카테고리들을 조회한다.") - void findShopMenuCategories() { + void 어드민이_특정_상점의_메뉴_카테고리들을_조회한다() throws Exception { // given menuFixture.짜장면_단일메뉴(shop_마슬랜, menuCategory_메인); - var response = RestAssured - .given() - .pathParam("id", shop_마슬랜.getId()) - .header("Authorization", "Bearer " + token_admin) - .when() - .get("/admin/shops/{id}/menus/categories") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + + mockMvc.perform( + get("/admin/shops/{id}/menus/categories", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_admin) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "count": 2, "menu_categories": [ @@ -346,27 +311,20 @@ void findShopMenuCategories() { } ] } - """); + """)); } @Test - @DisplayName("어드민이 특정 상점의 특정 메뉴를 조회한다.") - void findShopMenu() { + void 어드민이_특정_상점의_특정_메뉴를_조회한다() throws Exception { // given Menu menu = menuFixture.짜장면_옵션메뉴(shop_마슬랜, menuCategory_메인); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .pathParam("shopId", shop_마슬랜.getId()) - .pathParam("menuId", menu.getId()) - .when() - .get("/admin/shops/{shopId}/menus/{menuId}", menu.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - System.out.println(JsonAssertions.assertThat(response.asPrettyString())); - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + + mockMvc.perform( + get("/admin/shops/{shopId}/menus/{menuId}", menu.getId(), shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_admin) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "category_ids": [1], "description": "맛있는 짜장면", @@ -391,122 +349,108 @@ void findShopMenu() { "shop_id": 1, "single_price": null } - """); + """)); } @Test - @DisplayName("어드민이 상점을 생성한다.") - void createShop() { - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "address": "대전광역시 유성구 대학로 291", - "category_ids": [ - %d - ], - "delivery": true, - "delivery_price": 4000, - "description": "테스트 상점2입니다.", - "image_urls": [ - "https://test.com/test1.jpg", - "https://test.com/test2.jpg", - "https://test.com/test3.jpg" - ], - "name": "테스트 상점2", - "open": [ - { - "close_time": "21:00", - "closed": false, - "day_of_week": "MONDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "TUESDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "WEDNESDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "THURSDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "FRIDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "SATURDAY", - "open_time": "09:00" - }, + void 어드민이_상점을_생성한다() throws Exception { + mockMvc.perform( + post("/admin/shops") + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" { - "close_time": "21:00", - "closed": false, - "day_of_week": "SUNDAY", - "open_time": "09:00" + "address": "대전광역시 유성구 대학로 291", + "category_ids": [ + %d + ], + "delivery": true, + "delivery_price": 4000, + "description": "테스트 상점2입니다.", + "image_urls": [ + "https://test.com/test1.jpg", + "https://test.com/test2.jpg", + "https://test.com/test3.jpg" + ], + "name": "테스트 상점2", + "open": [ + { + "close_time": "21:00", + "closed": false, + "day_of_week": "MONDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "TUESDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "WEDNESDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "THURSDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "FRIDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "SATURDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "SUNDAY", + "open_time": "09:00" + } + ], + "pay_bank": true, + "pay_card": true, + "phone": "010-1234-5678" } - ], - "pay_bank": true, - "pay_card": true, - "phone": "010-1234-5678" - } - """, shopCategory_치킨.getId()) + """, shopCategory_치킨.getId())) ) - .when() - .post("/admin/shops") - .then() - .log().all() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - - transactionTemplate.executeWithoutResult(status -> { - Shop result = adminShopRepository.getById(2); - assertSoftly( - softly -> { - softly.assertThat(result.getAddress()).isEqualTo("대전광역시 유성구 대학로 291"); - softly.assertThat(result.getDeliveryPrice()).isEqualTo(4000); - softly.assertThat(result.getDescription()).isEqualTo("테스트 상점2입니다."); - softly.assertThat(result.getName()).isEqualTo("테스트 상점2"); - softly.assertThat(result.getShopImages()).hasSize(3); - softly.assertThat(result.getShopOpens()).hasSize(7); - softly.assertThat(result.getShopCategories()).hasSize(1); - } - ); + .andExpect(status().isCreated()); + + Shop savedShop = adminShopRepository.getById(2); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(savedShop.getAddress()).isEqualTo("대전광역시 유성구 대학로 291"); + softly.assertThat(savedShop.getDeliveryPrice()).isEqualTo(4000); + softly.assertThat(savedShop.getDescription()).isEqualTo("테스트 상점2입니다."); + softly.assertThat(savedShop.getName()).isEqualTo("테스트 상점2"); + softly.assertThat(savedShop.getShopImages()).hasSize(3); + softly.assertThat(savedShop.getShopOpens()).hasSize(7); + softly.assertThat(savedShop.getShopCategories()).hasSize(1); }); } @Test - @DisplayName("어드민이 상점 카테고리를 생성한다.") - void createShopCategory() { - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .contentType(ContentType.JSON) - .body(""" - { - "image_url": "https://image.png", - "name": "새로운 카테고리" - } - """) - .when() - .post("/admin/shops/categories") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + void 어드민이_상점_카테고리를_생성한다() throws Exception { + mockMvc.perform( + post("/admin/shops/categories") + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "image_url": "https://image.png", + "name": "새로운 카테고리" + } + """) + ) + .andExpect(status().isCreated()); transactionTemplate.executeWithoutResult(status -> { ShopCategory result = adminShopCategoryRepository.getById(3); @@ -521,128 +465,114 @@ void createShopCategory() { } @Test - @DisplayName("어드민이 옵션이 여러개인 메뉴를 추가한다.") - void createManyOptionMenu() { + void 어드민이_옵션이_여러개인_메뉴를_추가한다() throws Exception { // given MenuCategory menuCategory = menuCategory_메인; - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "category_ids": [ - %s - ], - "description": "테스트메뉴입니다.", - "image_urls": [ - "https://test-image.com/짜장면.jpg" - ], - "is_single": false, - "name": "짜장면", - "option_prices": [ - { - "option": "중", - "price": 10000 - }, - { - "option": "소", - "price": 5000 - } - ] - } - """, menuCategory.getId())) - .when() - .post("/admin/shops/{id}/menus", shop_마슬랜.getId()) - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - transactionTemplate.executeWithoutResult(status -> { - Menu menu = adminMenuRepository.getById(1); - assertSoftly( - softly -> { - List menuCategoryMaps = menu.getMenuCategoryMaps(); - List menuOptions = menu.getMenuOptions(); - List menuImages = menu.getMenuImages(); - softly.assertThat(menu.getDescription()).isEqualTo("테스트메뉴입니다."); - softly.assertThat(menu.getName()).isEqualTo("짜장면"); - softly.assertThat(menuImages.get(0).getImageUrl()).isEqualTo("https://test-image.com/짜장면.jpg"); - softly.assertThat(menuCategoryMaps.get(0).getMenuCategory().getId()).isEqualTo(1); - softly.assertThat(menuOptions).hasSize(2); - } - ); - }); + mockMvc.perform( + post("/admin/shops/{id}/menus", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "category_ids": [ + %s + ], + "description": "테스트메뉴입니다.", + "image_urls": [ + "https://test-image.com/짜장면.jpg" + ], + "is_single": false, + "name": "짜장면", + "option_prices": [ + { + "option": "중", + "price": 10000 + }, + { + "option": "소", + "price": 5000 + } + ] + } + """, menuCategory.getId())) + ) + .andExpect(status().isCreated()); + + Menu menu = adminMenuRepository.getById(1); + assertSoftly( + softly -> { + List menuCategoryMaps = menu.getMenuCategoryMaps(); + List menuOptions = menu.getMenuOptions(); + List menuImages = menu.getMenuImages(); + softly.assertThat(menu.getDescription()).isEqualTo("테스트메뉴입니다."); + softly.assertThat(menu.getName()).isEqualTo("짜장면"); + softly.assertThat(menuImages.get(0).getImageUrl()).isEqualTo("https://test-image.com/짜장면.jpg"); + softly.assertThat(menuCategoryMaps.get(0).getMenuCategory().getId()).isEqualTo(1); + softly.assertThat(menuOptions).hasSize(2); + } + ); } @Test - @DisplayName("어드민이 옵션이 한개인 메뉴를 추가한다.") - void createOneOptionMenu() { + void 어드민이_옵션이_한개인_메뉴를_추가한다() throws Exception { // given MenuCategory menuCategory = menuCategory_메인; - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "category_ids": [ - %s - ], - "description": "테스트메뉴입니다.", - "image_urls": [ - "https://test-image.com/짜장면.jpg" - ], - "is_single": true, - "name": "짜장면", - "option_prices": null, - "single_price": 10000 - } - """, menuCategory.getId())) - .when() - .post("/admin/shops/{id}/menus", shop_마슬랜.getId()) - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); - transactionTemplate.executeWithoutResult(status -> { - Menu menu = adminMenuRepository.getById(1); - assertSoftly( - softly -> { - List menuCategoryMaps = menu.getMenuCategoryMaps(); - List menuOptions = menu.getMenuOptions(); - List menuImages = menu.getMenuImages(); - softly.assertThat(menu.getDescription()).isEqualTo("테스트메뉴입니다."); - softly.assertThat(menu.getName()).isEqualTo("짜장면"); + mockMvc.perform( + post("/admin/shops/{id}/menus", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "category_ids": [ + %s + ], + "description": "테스트메뉴입니다.", + "image_urls": [ + "https://test-image.com/짜장면.jpg" + ], + "is_single": true, + "name": "짜장면", + "option_prices": null, + "single_price": 10000 + } + """, menuCategory.getId())) + ) + .andExpect(status().isCreated()); - softly.assertThat(menuImages.get(0).getImageUrl()).isEqualTo("https://test-image.com/짜장면.jpg"); - softly.assertThat(menuCategoryMaps.get(0).getMenuCategory().getId()).isEqualTo(1); + Menu menu = adminMenuRepository.getById(1); + assertSoftly( + softly -> { + List menuCategoryMaps = menu.getMenuCategoryMaps(); + List menuOptions = menu.getMenuOptions(); + List menuImages = menu.getMenuImages(); + System.out.println("transaction test"); + softly.assertThat(menu.getDescription()).isEqualTo("테스트메뉴입니다."); + softly.assertThat(menu.getName()).isEqualTo("짜장면"); - softly.assertThat(menuOptions.get(0).getPrice()).isEqualTo(10000); - } - ); - }); + softly.assertThat(menuImages.get(0).getImageUrl()).isEqualTo("https://test-image.com/짜장면.jpg"); + softly.assertThat(menuCategoryMaps.get(0).getMenuCategory().getId()).isEqualTo(1); + + softly.assertThat(menuOptions.get(0).getPrice()).isEqualTo(10000); + } + ); } @Test - @DisplayName("어드민이 메뉴 카테고리를 추가한다.") - void createMenuCategory() { + void 어드민이_메뉴_카테고리를_추가한다() throws Exception { // given - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .pathParam("id", shop_마슬랜.getId()) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "name": "대박메뉴" - } - """)) - .when() - .post("/admin/shops/{id}/menus/categories") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + mockMvc.perform( + post("/admin/shops/{id}/menus/categories", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "name": "대박메뉴" + } + """)) + ) + .andExpect(status().isCreated()); var menuCategories = adminMenuCategoryRepository.findAllByShopId(shop_마슬랜.getId()); @@ -650,100 +580,90 @@ void createMenuCategory() { } @Test - @DisplayName("어드민이 상점 삭제를 해제한다.") - void cancelShopDeleted() { + void 어드민이_상점_삭제를_해제한다() throws Exception { // given - System.out.println("qwe"); adminShopRepository.deleteById(shop_마슬랜.getId()); - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .pathParam("id", shop_마슬랜.getId()) - .contentType(ContentType.JSON) - .when() - .post("/admin/shops/{id}/undelete") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + + mockMvc.perform( + post("/admin/shops/{id}/undelete", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); var shop = adminShopRepository.getById(shop_마슬랜.getId()); assertSoftly(softly -> softly.assertThat(shop.isDeleted()).isFalse()); } @Test - @DisplayName("어드민이 상점을 수정한다.") - void modifyShop() { - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "address": "충청남도 천안시 동남구 병천면 충절로 1600", - "category_ids": [ - %d, %d - ], - "delivery": false, - "delivery_price": 1000, - "description": "이번주 전 메뉴 10%% 할인 이벤트합니다.", - "image_urls": [ - "https://fixed-shopimage.com/수정된_상점_이미지.png" - ], - "name": "써니 숯불 도시락", - "open": [ - { - "close_time": "00:00", - "closed": false, - "day_of_week": "MONDAY", - "open_time": "01:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "TUESDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "WEDNESDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "THURSDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "FRIDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "SATURDAY", - "open_time": "09:00" - }, - { - "close_time": "21:00", - "closed": false, - "day_of_week": "SUNDAY", - "open_time": "09:00" - } - ], - "pay_bank": true, - "pay_card": true, - "phone": "041-123-4567" - } - """, shopCategory_일반.getId(), shopCategory_치킨.getId() - )) - .when() - .put("/admin/shops/{id}", shop_마슬랜.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + void 어드민이_상점을_수정한다() throws Exception { + mockMvc.perform( + put("/admin/shops/{id}", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "address": "충청남도 천안시 동남구 병천면 충절로 1600", + "category_ids": [ + %d, %d + ], + "delivery": false, + "delivery_price": 1000, + "description": "이번주 전 메뉴 10%% 할인 이벤트합니다.", + "image_urls": [ + "https://fixed-shopimage.com/수정된_상점_이미지.png" + ], + "name": "써니 숯불 도시락", + "open": [ + { + "close_time": "00:00", + "closed": false, + "day_of_week": "MONDAY", + "open_time": "01:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "TUESDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "WEDNESDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "THURSDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "FRIDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "SATURDAY", + "open_time": "09:00" + }, + { + "close_time": "21:00", + "closed": false, + "day_of_week": "SUNDAY", + "open_time": "09:00" + } + ], + "pay_bank": true, + "pay_card": true, + "phone": "041-123-4567" + } + """, shopCategory_일반.getId(), shopCategory_치킨.getId())) + ) + .andExpect(status().isOk()); transactionTemplate.executeWithoutResult(status -> { Shop result = adminShopRepository.getById(shop_마슬랜.getId()); @@ -778,26 +698,21 @@ void modifyShop() { @Test - @DisplayName("어드민이 상점 카테고리를 수정한다.") - void modifyShopCategory() { + void 어드민이_상점_카테고리를_수정한다() throws Exception { ShopCategory shopCategory = shopCategoryFixture.카테고리_일반음식(); - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .contentType(ContentType.JSON) - .pathParam("id", shopCategory.getId()) - .body(""" - { - "image_url": "http://image.png", - "name": "수정된 카테고리 이름" - } - """) - .when() - .put("/admin/shops/categories/{id}") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + put("/admin/shops/categories/{id}", shopCategory.getId()) + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "image_url": "http://image.png", + "name": "수정된 카테고리 이름" + } + """) + ) + .andExpect(status().isOk()); transactionTemplate.executeWithoutResult(status -> { ShopCategory updatedCategory = adminShopCategoryRepository.getById(shopCategory.getId()); @@ -812,62 +727,51 @@ void modifyShopCategory() { } @Test - @DisplayName("어드민이 특점 상점의 메뉴 카테고리를 수정한다.") - void modifyMenuCategory() { + void 어드민이_특정_상점의_메뉴_카테고리를_수정한다() throws Exception { // given Menu menu = menuFixture.짜장면_단일메뉴(shop_마슬랜, menuCategory_메인); - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .contentType(ContentType.JSON) - .pathParam("shopId", shop_마슬랜.getId()) - .body(String.format(""" - { - "id": %s, - "name": "사이드 메뉴" - } - """, menuCategory_메인.getId())) - .when() - .put("/admin/shops/{shopId}/menus/categories") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + mockMvc.perform( + put("/admin/shops/{shopId}/menus/categories", shop_마슬랜.getId()) + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "id": %s, + "name": "사이드 메뉴" + } + """, menuCategory_메인.getId())) + ) + .andExpect(status().isCreated()); MenuCategory menuCategory = adminMenuCategoryRepository.getById(menuCategory_메인.getId()); assertSoftly(softly -> softly.assertThat(menuCategory.getName()).isEqualTo("사이드 메뉴")); } @Test - @DisplayName("어드민이 특정 삼점의 메뉴를 단일 메뉴로 수정한다.") - void modifyOneMenu() { + void 어드민이_특정_상점의_메뉴를_단일_메뉴로_수정한다() throws Exception { // given Menu menu = menuFixture.짜장면_단일메뉴(shop_마슬랜, menuCategory_메인); - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .contentType(ContentType.JSON) - .pathParam("shopId", shop_마슬랜.getId()) - .pathParam("menuId", menu.getId()) - .body(String.format(""" - { - "category_ids": [ - %d - ], - "description": "테스트메뉴수정", - "image_urls": [ - "https://test-image.net/테스트메뉴.jpeg" - ], - "is_single": true, - "name": "짜장면2", - "single_price": 10000 - } - """, shopCategory_일반.getId())) - .when() - .put("/admin/shops/{shopId}/menus/{menuId}") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + mockMvc.perform( + put("/admin/shops/{shopId}/menus/{menuId}", shop_마슬랜.getId(), menu.getId()) + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "category_ids": [ + %d + ], + "description": "테스트메뉴수정", + "image_urls": [ + "https://test-image.net/테스트메뉴.jpeg" + ], + "is_single": true, + "name": "짜장면2", + "single_price": 10000 + } + """, shopCategory_일반.getId())) + ) + .andExpect(status().isCreated()); transactionTemplate.executeWithoutResult(status -> { Menu result = adminMenuRepository.getById(1); @@ -890,45 +794,38 @@ void modifyOneMenu() { } @Test - @DisplayName("어드민이 특정 상점의 메뉴를 여러옵션을 가진 메뉴로 수정한다.") - void modifyManyOptionMenu() { + void 어드민이_특정_상점의_메뉴를_여러옵션을_가진_메뉴로_수정한다() throws Exception { // given Menu menu = menuFixture.짜장면_옵션메뉴(shop_마슬랜, menuCategory_메인); - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .pathParam("shopId", shop_마슬랜.getId()) - .pathParam("menuId", menu.getId()) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "category_ids": [ - %d, %d - ], - "description": "테스트메뉴입니다.", - "image_urls": [ - "https://fixed-testimage.com/수정된짜장면.png" - ], - "is_single": false, - "name": "짜장면", - "option_prices": [ - { - "option": "중", - "price": 10000 - }, - { - "option": "소", - "price": 5000 - } - ] - } - """, menuCategory_메인.getId(), menuCategory_사이드.getId()) + mockMvc.perform( + put("/admin/shops/{shopId}/menus/{menuId}", shop_마슬랜.getId(), menu.getId()) + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "category_ids": [ + %d, %d + ], + "description": "테스트메뉴입니다.", + "image_urls": [ + "https://fixed-testimage.com/수정된짜장면.png" + ], + "is_single": false, + "name": "짜장면", + "option_prices": [ + { + "option": "중", + "price": 10000 + }, + { + "option": "소", + "price": 5000 + } + ] + } + """, menuCategory_메인.getId(), menuCategory_사이드.getId())) ) - .when() - .put("/admin/shops/{shopId}/menus/{menuId}") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + .andExpect(status().isCreated()); transactionTemplate.executeWithoutResult(status -> { Menu result = adminMenuRepository.getById(1); @@ -949,107 +846,87 @@ void modifyManyOptionMenu() { } @Test - @DisplayName("어드민이 특정 상점의 메뉴를 여러옵션을 가진 메뉴로 수정한다. - 가격 옵션이 비어있거나 null이면 400 에러 발생") - void modifyManyOptionMenuWithEmptyOptionPrices() { + void 어드민이_특정_상점의_메뉴를_여러옵션을_가진_메뉴로_수정한다_가격_옵션이_비어있거나_null이면_400_에러_발생() throws Exception { // given Menu menu = menuFixture.짜장면_옵션메뉴(shop_마슬랜, menuCategory_메인); - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .pathParam("shopId", shop_마슬랜.getId()) - .pathParam("menuId", menu.getId()) - .contentType(ContentType.JSON) - .body(String.format(""" - { - "category_ids": [ - %d, %d - ], - "description": "테스트메뉴입니다.", - "image_urls": [ - "https://fixed-testimage.com/수정된짜장면.png" - ], - "is_single": false, - "name": "짜장면", - "option_prices": [] - } - """, menuCategory_메인.getId(), menuCategory_사이드.getId())) - .when() - .put("/admin/shops/{shopId}/menus/{menuId}") - .then() - .statusCode(HttpStatus.BAD_REQUEST.value()) - .extract(); + mockMvc.perform( + put("/admin/shops/{shopId}/menus/{menuId}", shop_마슬랜.getId(), menu.getId()) + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "category_ids": [ + %d, %d + ], + "description": "테스트메뉴입니다.", + "image_urls": [ + "https://fixed-testimage.com/수정된짜장면.png" + ], + "is_single": false, + "name": "짜장면", + "option_prices": [] + } + """, menuCategory_메인.getId(), menuCategory_사이드.getId())) + ) + .andExpect(status().isBadRequest()); } @Test - @DisplayName("어드민이 상점을 삭제한다.") - void deleteShop() { + void 어드민이_상점을_삭제한다() throws Exception { Shop shop = shopFixture.영업중이_아닌_신전_떡볶이(owner_현수); - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .when() - .delete("/admin/shops/{id}", shop.getId()) - .then() - .statusCode(HttpStatus.OK.value()); + mockMvc.perform( + delete("/admin/shops/{id}", shop.getId()) + .header("Authorization", "Bearer " + token_admin) + ) + .andExpect(status().isOk()); Shop deletedShop = adminShopRepository.getById(shop.getId()); assertSoftly(softly -> softly.assertThat(deletedShop.isDeleted()).isTrue()); } @Test - @DisplayName("어드민이 상점 카테고리를 삭제한다.") - void deleteShopCategory() { + void 어드민이_상점_카테고리를_삭제한다() throws Exception { ShopCategory shopCategory = shopCategoryFixture.카테고리_일반음식(); - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .when() - .delete("/admin/shops/categories/{id}", shopCategory.getId()) - .then() - .statusCode(HttpStatus.OK.value()); + mockMvc.perform( + delete("/admin/shops/categories/{id}", shopCategory.getId()) + .header("Authorization", "Bearer " + token_admin) + ) + .andExpect(status().isOk()); ShopCategory deletedCategory = adminShopCategoryRepository.getById(shopCategory.getId()); assertSoftly(softly -> softly.assertThat(deletedCategory.isDeleted()).isTrue()); } @Test - @DisplayName("어드민이 특정 상점의 메뉴 카테고리를 삭제한다.") - void deleteMenuCategory() { + void 어드민이_특정_상점의_메뉴_카테고리를_삭제한다() throws Exception { // when & then - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .pathParam("shopId", shop_마슬랜.getId()) - .pathParam("categoryId", menuCategory_메인.getId()) - .when() - .delete("/admin/shops/{shopId}/menus/categories/{categoryId}") - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); + mockMvc.perform( + delete( + "/admin/shops/{shopId}/menus/categories/{categoryId}", + shop_마슬랜.getId(), + menuCategory_메인.getId() + ) + .header("Authorization", "Bearer " + token_admin) + ) + .andExpect(status().isNoContent()); assertThat(adminMenuCategoryRepository.findById(menuCategory_메인.getId())).isNotPresent(); } @Test - @DisplayName("어드민이 메뉴를 삭제한다.") - void deleteMenu() { + void 어드민이_메뉴를_삭제한다() throws Exception { // given Menu menu = menuFixture.짜장면_단일메뉴(shop_마슬랜, menuCategory_메인); - RestAssured - .given() - .header("Authorization", "Bearer " + token_admin) - .pathParam("shopId", shop_마슬랜.getId()) - .pathParam("menuId", menu.getId()) - .when() - .delete("/admin/shops/{shopId}/menus/{menuId}", menu.getId()) - .then() - .statusCode(HttpStatus.NO_CONTENT.value()) - .extract(); + mockMvc.perform( + delete("/admin/shops/{shopId}/menus/{menuId}", shop_마슬랜.getId(), menu.getId()) + .header("Authorization", "Bearer " + token_admin) + ) + .andExpect(status().isNoContent()); assertThat(adminMenuRepository.findById(menu.getId())).isNotPresent(); } diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminTrackApiTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminTrackApiTest.java index c9ea7ecc9..1f1c7fdb2 100644 --- a/src/test/java/in/koreatech/koin/admin/acceptance/AdminTrackApiTest.java +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminTrackApiTest.java @@ -1,10 +1,15 @@ package in.koreatech.koin.admin.acceptance; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + import org.assertj.core.api.SoftAssertions; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.admin.member.repository.AdminTechStackRepository; @@ -17,10 +22,10 @@ import in.koreatech.koin.fixture.TechStackFixture; import in.koreatech.koin.fixture.TrackFixture; import in.koreatech.koin.fixture.UserFixture; -import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; @SuppressWarnings("NonAsciiCharacters") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Transactional public class AdminTrackApiTest extends AcceptanceTest { @Autowired @@ -41,25 +46,25 @@ public class AdminTrackApiTest extends AcceptanceTest { @Autowired private AdminTechStackRepository adminTechStackRepository; + @BeforeAll + void setup() { + clear(); + } + @Test - @DisplayName("관리자가 BCSDLab 트랙 정보를 조회한다. - 관리자가 아니면 403 반환") - void findTracksAdminNoAuth() { + void 관리자가_BCSDLab_트랙_정보를_조회한다_관리자가_아니면_403_반환() throws Exception { Student student = userFixture.준호_학생(); String token = userFixture.getToken(student.getUser()); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/admin/tracks") - .then() - .statusCode(HttpStatus.FORBIDDEN.value()) - .extract(); + mockMvc.perform( + get("/admin/tracks") + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isForbidden()); } @Test - @DisplayName("관리자가 BCSDLab 트랙 정보를 조회한다.") - void findTracks() { + void 관리자가_BCSDLab_트랙_정보를_조회한다() throws Exception { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); @@ -67,17 +72,12 @@ void findTracks() { trackFixture.frontend(); trackFixture.ios(); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/admin/tracks") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/admin/tracks") + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" [ { "id": 1, @@ -104,33 +104,27 @@ void findTracks() { "updated_at": "2024-01-15 12:00:00" } ] - """); + """)); } @Test - @DisplayName("관리자가 BCSDLab 트랙 정보를 생성한다.") - void createTrack() { + void 관리자가_BCSDLab_트랙_정보를_생성한다() throws Exception { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(""" - { - "name": "BackEnd", - "headcount": 20 - } - """) - .when() - .post("/admin/tracks") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + post("/admin/tracks") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "name": "BackEnd", + "headcount": 20 + } + """) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "id": 1, "name": "BackEnd", @@ -139,37 +133,32 @@ void createTrack() { "created_at": "2024-01-15 12:00:00", "updated_at": "2024-01-15 12:00:00" } - """); + """)); } @Test - @DisplayName("관리자가 BCSDLab 트랙 정보를 생성한다. - 이미 있는 트랙명이면 409반환") - void createTrackDuplication() { + void 관리자가_BCSDLab_트랙_정보를_생성한다_이미_있는_트랙명이면_409_반환() throws Exception { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); trackFixture.backend(); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(""" - { - "name": "BackEnd", - "headcount": 20 - } - """) - .when() - .post("/admin/tracks") - .then() - .statusCode(HttpStatus.CONFLICT.value()) - .extract(); + mockMvc.perform( + post("/admin/tracks") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "name": "BackEnd", + "headcount": 20 + } + """) + ) + .andExpect(status().isConflict()); } @Test - @DisplayName("관리자가 BCSDLab 트랙 단건 정보를 조회한다.") - void findTrack() { + void 관리자가_BCSDLab_트랙_단거_정보를_조회한다() throws Exception { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); @@ -180,17 +169,12 @@ void findTrack() { techStackFixture.java(backend); techStackFixture.adobeFlash(backend); //삭제된 기술스택 - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/admin/tracks/{id}", backend.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/admin/tracks/{id}", backend.getId()) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "TrackName": "BackEnd", "Members": [ @@ -242,35 +226,29 @@ void findTrack() { } ] } - """); + """)); } @Test - @DisplayName("관리자가 BCSDLab 트랙 정보를 수정한다.") - void updateTrack() { + void 관리자가_BCSDLab_트랙_정보를_수정한다() throws Exception { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); Track backEnd = trackFixture.backend(); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(""" - { - "name": "frontEnd", - "headcount": 20 - } - """) - .when() - .put("/admin/tracks/{id}", backEnd.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + put("/admin/tracks/{id}", backEnd.getId()) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "name": "frontEnd", + "headcount": 20 + } + """) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "id": 1, "name": "frontEnd", @@ -279,50 +257,42 @@ void updateTrack() { "created_at": "2024-01-15 12:00:00", "updated_at": "2024-01-15 12:00:00" } - """); + """)); } @Test - @DisplayName("관리자가 BCSDLab 트랙 정보를 수정한다. - 이미 있는 트랙명이면 409반환") - void updateTrackDuplication() { + void 관리자가_BCSDLab_트랙_정보를_수정한다_이미_있는_트랙명이면_409_반환() throws Exception { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); Track backEnd = trackFixture.backend(); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(""" - { - "name": "BackEnd", - "headcount": 20 - } - """) - .when() - .put("/admin/tracks/{id}", backEnd.getId()) - .then() - .statusCode(HttpStatus.CONFLICT.value()) - .extract(); + mockMvc.perform( + put("/admin/tracks/{id}", backEnd.getId()) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "name": "BackEnd", + "headcount": 20 + } + """) + ) + .andExpect(status().isConflict()); } @Test - @DisplayName("관리자가 BCSDLab 트랙 정보를 삭제한다.") - void deleteTrack() { + void 관리자가_BCSDLab_트랙_정보를_삭제한다() throws Exception { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); Track backEnd = trackFixture.backend(); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .delete("/admin/tracks/{id}", backEnd.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + delete("/admin/tracks/{id}", backEnd.getId()) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()); Track updatedTrack = adminTrackRepository.getById(backEnd.getId()); @@ -334,34 +304,28 @@ void deleteTrack() { } @Test - @DisplayName("관리자가 BCSDLab 기술스택 정보를 생성한다.") - void createTechStack() { + void 관리자가_BCSDLab_기술스택_정보를_생성한다() throws Exception { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); trackFixture.frontend(); Track backEnd = trackFixture.backend(); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(""" - { - "image_url": "https://url.com", - "name": "Spring", - "description": "스프링은 웹 프레임워크이다" - } - """) - .when() - .queryParam("trackName", backEnd.getName()) - .post("/admin/techStacks") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + post("/admin/techStacks") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "image_url": "https://url.com", + "name": "Spring", + "description": "스프링은 웹 프레임워크이다" + } + """) + .param("trackName", backEnd.getName()) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "id": 1, "image_url": "https://url.com", @@ -372,39 +336,33 @@ void createTechStack() { "created_at": "2024-01-15 12:00:00", "updated_at": "2024-01-15 12:00:00" } - """); + """)); } @Test - @DisplayName("관리자가 BCSDLab 기술스택 정보를 수정한다.") - void updateTechStack() { + void 관리자가_BCSDLab_기술스택_정보를_수정한다() throws Exception { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); TechStack java = techStackFixture.java(trackFixture.frontend()); Track backEnd = trackFixture.backend(); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType("application/json") - .body(""" - { - "image_url": "https://java.com", - "name": "JAVA", - "description": "java의 TrackID를 BackEnd로 수정한다.", - "is_deleted": true - } - """) - .when() - .queryParam("trackName", backEnd.getName()) - .put("/admin/techStacks/{id}", java.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + put("/admin/techStacks/{id}", java.getId()) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "image_url": "https://java.com", + "name": "JAVA", + "description": "java의 TrackID를 BackEnd로 수정한다.", + "is_deleted": true + } + """) + .param("trackName", backEnd.getName()) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "id": 1, "image_url": "https://java.com", @@ -415,26 +373,23 @@ void updateTechStack() { "created_at": "2024-01-15 12:00:00", "updated_at": "2024-01-15 12:00:00" } - """); + """)); } @Test - @DisplayName("관리자가 기술스택 정보를 삭제한다.") - void deleteTechStack() { + void 관리자가_기술스택_정보를_삭제한다() throws Exception { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); Track backEnd = trackFixture.backend(); TechStack java = techStackFixture.java(backEnd); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .delete("/admin/techStacks/{id}", java.getId()) - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + delete("/admin/techStacks/{id}", java.getId()) + .header("Authorization", "Bearer " + token) + + ) + .andExpect(status().isOk()); TechStack updatedtechStack = adminTechStackRepository.getById(java.getId()); diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminUserApiTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminUserApiTest.java index 1c889fa92..152312a09 100644 --- a/src/test/java/in/koreatech/koin/admin/acceptance/AdminUserApiTest.java +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminUserApiTest.java @@ -6,17 +6,24 @@ import static org.assertj.core.api.Assertions.assertThat; import static in.koreatech.koin.domain.user.model.UserType.STUDENT; import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.util.ArrayList; import org.assertj.core.api.SoftAssertions; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; +import com.fasterxml.jackson.databind.JsonNode; + import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.admin.user.repository.AdminOwnerRepository; import in.koreatech.koin.admin.user.repository.AdminOwnerShopRedisRepository; @@ -33,10 +40,10 @@ import in.koreatech.koin.fixture.ShopFixture; import in.koreatech.koin.fixture.UserFixture; import in.koreatech.koin.support.JsonAssertions; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; @SuppressWarnings("NonAsciiCharacters") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Transactional public class AdminUserApiTest extends AcceptanceTest { @Autowired @@ -66,26 +73,25 @@ public class AdminUserApiTest extends AcceptanceTest { @Autowired private PasswordEncoder passwordEncoder; + @BeforeAll + void setup() { + clear(); + } + @Test - @DisplayName("관리자가 학생 리스트를 파라미터가 없이 조회한다.(페이지네이션)") - void getStudentsWithoutParameterAdmin() { + void 관리자가_학생_리스트를_파라미터가_없이_조회한다_페이지네이션() throws Exception { Student student = userFixture.준호_학생(); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .when() - .get("/admin/students") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/admin/students") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "current_count": 1, "current_page": 1, @@ -102,12 +108,11 @@ void getStudentsWithoutParameterAdmin() { "total_count": 1, "total_page": 1 } - """); + """)); } @Test - @DisplayName("관리자가 학생 리스트를 페이지 수와 limit으로 조회한다.(페이지네이션)") - void getStudentsWithPageAndLimitAdmin() { + void 관리자가_학생_리스트를_페이지_수와_limits으로_조회한다_페이지네이션() throws Exception { for (int i = 0; i < 11; i++) { Student student = Student.builder() .studentNumber("2019136135") @@ -137,20 +142,14 @@ void getStudentsWithPageAndLimitAdmin() { String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .queryParam("page", 2) - .queryParam("limit", 10) - .when() - .get("/admin/students") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/admin/students") + .header("Authorization", "Bearer " + token) + .param("page", "2") + .param("limit", "10") + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "current_count": 1, "current_page": 2, @@ -167,31 +166,25 @@ void getStudentsWithPageAndLimitAdmin() { "total_count": 11, "total_page": 2 } - """); + """)); } @Test - @DisplayName("관리자가 학생 리스트를 닉네임으로 조회한다.(페이지네이션)") - void getStudentsWithNicknameAdmin() { + void 관리자가_학생_리스트를_닉네임으로_조회한다_페이지네이션() throws Exception { Student student1 = userFixture.성빈_학생(); Student student2 = userFixture.준호_학생(); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .when() - .queryParam("nickname", "준호") - .get("/admin/students") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/admin/students") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .param("nickname", "준호") + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "current_count": 1, "current_page": 1, @@ -208,120 +201,99 @@ void getStudentsWithNicknameAdmin() { "total_count": 1, "total_page": 1 } - """); + """)); } @Test - @DisplayName("관리자가 로그인 한다.") - void adminLogin() { + void 관리자가_로그인_한다() throws Exception { User adminUser = userFixture.코인_운영자(); String email = adminUser.getEmail(); String password = "1234"; - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .body(""" - { - "email" : "%s", - "password" : "%s" - } - """.formatted(email, password)) - .when() - .post("/admin/user/login") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + mockMvc.perform( + post("/admin/user/login") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "email" : "%s", + "password" : "%s" + } + """.formatted(email, password)) + ) + .andExpect(status().isCreated()); } @Test - @DisplayName("관리자가 로그인 한다. - 관리자가 아니면 404 반환") - void adminLoginNoAuth() { + void 관리자가_로그인_한다_관리자가_아니면_404_반환() throws Exception { Student student = userFixture.준호_학생(); String email = student.getUser().getEmail(); String password = "1234"; - var response = RestAssured - .given() - .contentType(ContentType.JSON) - .body(""" - { - "email" : "%s", - "password" : "%s" - } - """.formatted(email, password)) - .when() - .post("/admin/user/login") - .then() - .statusCode(HttpStatus.NOT_FOUND.value()) - .extract(); + mockMvc.perform( + post("/admin/user/login") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "email" : "%s", + "password" : "%s" + } + """.formatted(email, password)) + ) + .andExpect(status().isNotFound()); } @Test - @DisplayName("관리자가 로그아웃한다") - void adminLogout() { + void 관리자가_로그아웃한다() throws Exception { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .when() - .post("/admin/user/logout") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + post("/admin/user/logout") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); } @Test - @DisplayName("관리자가 액세스 토큰 재발급 한다") - void adminRefresh() { + void 관리자가_액세스_토큰_재발급_한다() throws Exception { User adminUser = userFixture.코인_운영자(); String email = adminUser.getEmail(); String password = "1234"; String token = userFixture.getToken(adminUser); - var loginResponse = RestAssured - .given() - .contentType(ContentType.JSON) - .body(""" - { - "email" : "%s", - "password" : "%s" - } - """.formatted(email, password)) - .when() - .post("/admin/user/login") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract() - .response(); - - String refreshToken = loginResponse.jsonPath().getString("refresh_token"); - - var refreshResponse = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" - { - "refresh_token" : "%s" - } - """.formatted(refreshToken)) - .when() - .post("/admin/user/refresh") - .then() - .statusCode(HttpStatus.CREATED.value()) - .extract(); + MvcResult result = mockMvc.perform( + post("/admin/user/login") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "email" : "%s", + "password" : "%s" + } + """.formatted(email, password)) + ) + .andExpect(status().isCreated()) + .andReturn(); + + JsonNode loginJsonNode = JsonAssertions.convertJsonNode(result); + + mockMvc.perform( + post("/admin/user/refresh") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "refresh_token" : "%s" + } + """.formatted(loginJsonNode.get("refresh_token").asText())) + ) + .andExpect(status().isCreated()); } - @Test - @DisplayName("관리자가 사장님 권한 요청을 허용한다.") - void allowOwnerPermission() { + void 관리자가_사장님_권한_요청을_허용한다() throws Exception { Owner owner = userFixture.철수_사장님(); Shop shop = shopFixture.마슬랜(null); @@ -335,15 +307,11 @@ void allowOwnerPermission() { ownerShopRedisRepository.save(ownerShop); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .pathParam("id", owner.getUser().getId()) - .put("/admin/owner/{id}/authed") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + put("/admin/owner/{id}/authed", owner.getUser().getId()) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()); //영속성 컨테스트 동기화 Owner updatedOwner = adminOwnerRepository.getById(owner.getId()); @@ -359,44 +327,32 @@ void allowOwnerPermission() { } @Test - @DisplayName("관리자가 특정 학생 정보를 조회한다. - 관리자가 아니면 403 반환") - void studentUpdateAdminNoAuth() { + void 관리자가_특정_학생_정보를_조회한다_관리자가_아니면_403_반환() throws Exception { Student student = userFixture.준호_학생(); String token = userFixture.getToken(student.getUser()); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .when() - .pathParam("id", student.getUser().getId()) - .get("/admin/users/student/{id}") - .then() - .statusCode(HttpStatus.FORBIDDEN.value()) - .extract(); + mockMvc.perform( + get("/admin/users/student/{id}", student.getUser().getId()) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isForbidden()); } @Test - @DisplayName("관리자가 특정 학생 정보를 조회한다.") - void studentGetAdmin() { + void 관리자가_특정_학생_정보를_조회한다() throws Exception { Student student = userFixture.준호_학생(); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .when() - .pathParam("id", student.getUser().getId()) - .get("/admin/users/student/{id}") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + get("/admin/users/student/{id}", student.getUser().getId()) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "anonymous_nickname": "익명", "created_at": "2024-01-15 12:00:00", @@ -414,38 +370,45 @@ void studentGetAdmin() { "updated_at": "2024-01-15 12:00:00", "user_type": "STUDENT" } - """); + """)); } @Test - @DisplayName("관리자가 특정 학생 정보를 수정한다.") - void studentUpdateAdmin() { + void 관리자가_특정_학생_정보를_수정한다() throws Exception { Student student = userFixture.준호_학생(); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" - { - "gender" : 1, - "major" : "기계공학부", - "name" : "서정빈", - "password" : "0c4be6acaba1839d3433c1ccf04e1eec4d1fa841ee37cb019addc269e8bc1b77", - "nickname" : "duehee", - "phone_number" : "01023456789", - "student_number" : "2019136136" - } - """) - .when() - .pathParam("id", student.getUser().getId()) - .put("/admin/users/student/{id}") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + put("/admin/users/student/{id}", student.getUser().getId()) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "gender" : 1, + "major" : "기계공학부", + "name" : "서정빈", + "password" : "0c4be6acaba1839d3433c1ccf04e1eec4d1fa841ee37cb019addc269e8bc1b77", + "nickname" : "duehee", + "phone_number" : "01023456789", + "student_number" : "2019136136" + } + """) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "anonymous_nickname": "익명", + "email": "juno@koreatech.ac.kr", + "gender": 1, + "major": "기계공학부", + "name": "서정빈", + "nickname": "duehee", + "phone_number": "01023456789", + "student_number": "2019136136" + } + """)); transactionTemplate.executeWithoutResult(status -> { Student result = adminStudentRepository.getById(student.getId()); @@ -458,43 +421,22 @@ void studentUpdateAdmin() { } ); }); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" - { - "anonymous_nickname": "익명", - "email": "juno@koreatech.ac.kr", - "gender": 1, - "major": "기계공학부", - "name": "서정빈", - "nickname": "duehee", - "phone_number": "01023456789", - "student_number": "2019136136" - } - """); } @Test - @DisplayName("관리자가 특정 사장을 조회한다.") - void getOwnerAdmin() { + void 관리자가_특정_사장을_조회한다() throws Exception { Owner owner = userFixture.현수_사장님(); Shop shop = shopFixture.마슬랜(owner); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .pathParam("id", owner.getUser().getId()) - .get("/admin/users/owner/{id}") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/admin/users/owner/{id}", owner.getUser().getId()) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()) + .andExpect(content().json(String.format(""" { "id": 1, "email": "hysoo@naver.com", @@ -516,39 +458,31 @@ void getOwnerAdmin() { "updated_at" : "2024-01-15 12:00:00", "last_logged_at" : null } - """, shop.getId() - )); + """, shop.getId()))); } @Test - @DisplayName("관리자가 특정 사장을 수정한다.") - void updateOwner() { + void 관리자가_특정_사장을_수정한다() throws Exception { Owner owner = userFixture.현수_사장님(); Shop shop = shopFixture.마슬랜(owner); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .body(""" - { - "company_registration_number" : "123-45-67190", - "grant_shop" : "false", - "grant_event" : "false" - } - """) - .when() - .pathParam("id", owner.getUser().getId()) - .put("/admin/users/owner/{id}") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(""" + mockMvc.perform( + put("/admin/users/owner/{id}", owner.getUser().getId()) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "company_registration_number" : "123-45-67190", + "grant_shop" : "false", + "grant_event" : "false" + } + """) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "company_registration_number" : "123-45-67190", "email" : "hysoo@naver.com", @@ -559,12 +493,11 @@ void updateOwner() { "nickname" : "현수", "phone_number" : "01098765432" } - """); + """)); } @Test - @DisplayName("관리자가 가입 신청한 사장님 리스트 조회한다.") - void getNewOwnersAdmin() { + void 관리자가_가입_신청한_사장님_리스트_조회한다() throws Exception { Owner owner = userFixture.철수_사장님(); Shop shop = shopFixture.마슬랜(null); @@ -572,26 +505,21 @@ void getNewOwnersAdmin() { String token = userFixture.getToken(adminUser); OwnerShop ownerShop = OwnerShop.builder() - .ownerId(owner.getId()) - .shopId(shop.getId()) - .build(); + .ownerId(owner.getId()) + .shopId(shop.getId()) + .build(); ownerShopRedisRepository.save(ownerShop); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .param("searchType", "NAME") - .param("query", "철수") - .param("sort", "CREATED_AT_DESC") - .get("/admin/users/new-owners") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - JsonAssertions.assertThat(response.asPrettyString()) - .isEqualTo(String.format(""" + mockMvc.perform( + get("/admin/users/new-owners") + .header("Authorization", "Bearer " + token) + .param("searchType", "NAME") + .param("query", "철수") + .param("sort", "CREATED_AT_DESC") + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" { "total_count": 1, "current_count": 1, @@ -609,14 +537,11 @@ void getNewOwnersAdmin() { } ] } - """ - )); + """)); } @Test - @DisplayName("관리자가 가입 신청한 사장님 리스트 조회한다 - V2") - void getNewOwnersAdminV2() { - + void 관리자가_가입_신청한_사장님_리스트_조회한다_V2() throws Exception { for (int i = 0; i < 11; i++) { User user = User.builder() .password(passwordEncoder.encode("1234")) @@ -659,29 +584,20 @@ void getNewOwnersAdminV2() { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/admin/users/new-owners") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertSoftly( - softly -> { - softly.assertThat(response.body().jsonPath().getInt("total_count")).isEqualTo(11); - softly.assertThat(response.body().jsonPath().getInt("current_count")).isEqualTo(10); - softly.assertThat(response.body().jsonPath().getInt("total_page")).isEqualTo(2); - softly.assertThat(response.body().jsonPath().getInt("current_page")).isEqualTo(1); - softly.assertThat(response.body().jsonPath().getList("owners").size()).isEqualTo(10); - } - ); + mockMvc.perform( + get("/admin/users/new-owners") + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.total_count").value(11)) + .andExpect(jsonPath("$.current_count").value(10)) + .andExpect(jsonPath("$.total_page").value(2)) + .andExpect(jsonPath("$.current_page").value(1)) + .andExpect(jsonPath("$.owners.length()").value(10)); } @Test - @DisplayName("관리자가 가입 사장님 리스트 조회한다") - void getOwnersAdmin() { + void 관리자가_가입_사장님_리스트_조회한다() throws Exception { for (int i = 0; i < 11; i++) { User user = User.builder() .password(passwordEncoder.encode("1234")) @@ -724,72 +640,50 @@ void getOwnersAdmin() { User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .get("/admin/users/owners") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertSoftly( - softly -> { - softly.assertThat(response.body().jsonPath().getInt("total_count")).isEqualTo(11); - softly.assertThat(response.body().jsonPath().getInt("current_count")).isEqualTo(10); - softly.assertThat(response.body().jsonPath().getInt("total_page")).isEqualTo(2); - softly.assertThat(response.body().jsonPath().getInt("current_page")).isEqualTo(1); - softly.assertThat(response.body().jsonPath().getList("owners").size()).isEqualTo(10); - } - ); + mockMvc.perform( + get("/admin/users/owners") + .header("Authorization", "Bearer " + token) + + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.total_count").value(11)) + .andExpect(jsonPath("$.current_count").value(10)) + .andExpect(jsonPath("$.total_page").value(2)) + .andExpect(jsonPath("$.current_page").value(1)) + .andExpect(jsonPath("$.owners.length()").value(10)); } @Test - @DisplayName("관리자가 회원을 조회한다.") - void getUser() { + void 관리자가_회원을_조회한다() throws Exception { Student student = userFixture.준호_학생(); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - var response = RestAssured - .given() - .header("Authorization", "Bearer " + token) - .when() - .pathParam("id", student.getUser().getId()) - .get("/admin/users/{id}") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); - - assertSoftly( - softly -> { - softly.assertThat(response.body().jsonPath().getString("nickname")).isEqualTo("준호"); - softly.assertThat(response.body().jsonPath().getString("name")).isEqualTo("테스트용_준호"); - softly.assertThat(response.body().jsonPath().getString("phoneNumber")).isEqualTo("01012345678"); - softly.assertThat(response.body().jsonPath().getString("email")).isEqualTo("juno@koreatech.ac.kr"); - } - ); + mockMvc.perform( + get("/admin/users/{id}", student.getUser().getId()) + .header("Authorization", "Bearer " + token) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.nickname").value("준호")) + .andExpect(jsonPath("$.name").value("테스트용_준호")) + .andExpect(jsonPath("$.phoneNumber").value("01012345678")) + .andExpect(jsonPath("$.email").value("juno@koreatech.ac.kr")); } @Test - @DisplayName("관리자가 회원을 삭제한다.") - void deleteUser() { + void 관리자가_회원을_삭제한다() throws Exception { Student student = userFixture.준호_학생(); User adminUser = userFixture.코인_운영자(); String token = userFixture.getToken(adminUser); - RestAssured - .given() - .header("Authorization", "Bearer " + token) - .contentType(ContentType.JSON) - .when() - .pathParam("id", student.getUser().getId()) - .delete("/admin/users/{id}") - .then() - .statusCode(HttpStatus.OK.value()) - .extract(); + mockMvc.perform( + delete("/admin/users/{id}", student.getUser().getId()) + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()); assertThat(adminUserRepository.findById(student.getId())).isNotPresent(); } diff --git a/src/test/java/in/koreatech/koin/fixture/MenuFixture.java b/src/test/java/in/koreatech/koin/fixture/MenuFixture.java index 3b89c474d..2921d45dd 100644 --- a/src/test/java/in/koreatech/koin/fixture/MenuFixture.java +++ b/src/test/java/in/koreatech/koin/fixture/MenuFixture.java @@ -29,13 +29,11 @@ public MenuFixture( } public Menu 짜장면_옵션메뉴(Shop shop, MenuCategory menuCategory) { - Menu menu = menuRepository.save( - Menu.builder() - .shopId(shop.getId()) - .name("짜장면") - .description("맛있는 짜장면") - .build() - ); + Menu menu = Menu.builder() + .shopId(shop.getId()) + .name("짜장면") + .description("맛있는 짜장면") + .build(); menu.getMenuImages().addAll( List.of( @@ -63,25 +61,21 @@ public MenuFixture( .build() ) ); - menu.getMenuCategoryMaps().add( - MenuCategoryMap.builder() - .menu(menu) - .menuCategory(menuCategory) - .build() - ); - + MenuCategoryMap menuCategoryMap = MenuCategoryMap.builder() + .menu(menu) + .menuCategory(menuCategory) + .build(); + menu.getMenuCategoryMaps().add(menuCategoryMap); + menuCategory.getMenuCategoryMaps().add(menuCategoryMap); return menuRepository.save(menu); } public Menu 짜장면_단일메뉴(Shop shop, MenuCategory menuCategory) { - - Menu menu = menuRepository.save( - Menu.builder() - .shopId(shop.getId()) - .name("짜장면") - .description("맛있는 짜장면") - .build() - ); + Menu menu = Menu.builder() + .shopId(shop.getId()) + .name("짜장면") + .description("맛있는 짜장면") + .build(); menu.getMenuImages().addAll( List.of( @@ -102,13 +96,12 @@ public MenuFixture( .price(7000) .build() ); - menu.getMenuCategoryMaps().add( - MenuCategoryMap.builder() - .menu(menu) - .menuCategory(menuCategory) - .build() - ); - + MenuCategoryMap menuCategoryMap = MenuCategoryMap.builder() + .menu(menu) + .menuCategory(menuCategory) + .build(); + menu.getMenuCategoryMaps().add(menuCategoryMap); + menuCategory.getMenuCategoryMaps().add(menuCategoryMap); return menuRepository.save(menu); } } diff --git a/src/test/java/in/koreatech/koin/fixture/ShopReviewFixture.java b/src/test/java/in/koreatech/koin/fixture/ShopReviewFixture.java index 0e8cb40e4..0c26cd572 100644 --- a/src/test/java/in/koreatech/koin/fixture/ShopReviewFixture.java +++ b/src/test/java/in/koreatech/koin/fixture/ShopReviewFixture.java @@ -9,6 +9,7 @@ import in.koreatech.koin.domain.shop.model.ShopReview; import in.koreatech.koin.domain.shop.model.ShopReviewImage; import in.koreatech.koin.domain.shop.model.ShopReviewMenu; +import in.koreatech.koin.domain.shop.repository.ShopRepository; import in.koreatech.koin.domain.shop.repository.ShopReviewImageRepository; import in.koreatech.koin.domain.shop.repository.ShopReviewMenuRepository; import in.koreatech.koin.domain.shop.repository.ShopReviewRepository; @@ -27,8 +28,8 @@ public ShopReviewFixture( ShopReviewRepository shopReviewRepository, ShopReviewImageRepository shopReviewImageRepository, ShopReviewMenuRepository shopReviewMenuRepository, - Clock clock - ) { + Clock clock, + ShopRepository shopRepository) { this.shopReviewRepository = shopReviewRepository; this.shopReviewImageRepository = shopReviewImageRepository; this.shopReviewMenuRepository = shopReviewMenuRepository; @@ -51,8 +52,9 @@ public ShopReviewFixture( .menuName("피자") .review(shopReview) .build()); - ShopReview savedShopReview = shopReviewRepository.save(shopReview); - return savedShopReview; + shopReviewRepository.save(shopReview); + shop.getReviews().add(shopReview); + return shopReview; } @FixedDate(year = 2024, month = 8, day = 7) diff --git a/src/test/java/in/koreatech/koin/support/DBInitializer.java b/src/test/java/in/koreatech/koin/support/DBInitializer.java index fc115ae07..26a82152d 100644 --- a/src/test/java/in/koreatech/koin/support/DBInitializer.java +++ b/src/test/java/in/koreatech/koin/support/DBInitializer.java @@ -1,7 +1,5 @@ package in.koreatech.koin.support; -import java.sql.ResultSet; -import java.sql.Statement; import java.util.ArrayList; import java.util.List; @@ -24,7 +22,7 @@ public class DBInitializer { private static final int ON = 1; private static final int COLUMN_INDEX = 1; - private final List tableNames = new ArrayList<>(); + private List tableNames = new ArrayList<>(); @Autowired private DataSource dataSource; @@ -39,24 +37,27 @@ public class DBInitializer { private MongoTemplate mongoTemplate; private void findDatabaseTableNames() { - try (final Statement statement = dataSource.getConnection().createStatement()) { - ResultSet resultSet = statement.executeQuery("SHOW TABLES"); - while (resultSet.next()) { - final String tableName = resultSet.getString(COLUMN_INDEX); - tableNames.add(tableName); - } - } catch (Exception ignore) { - } + String sql = "SHOW TABLES"; + tableNames = entityManager.createNativeQuery(sql).getResultList(); } - private void truncate() { + private void truncateAllTable() { setForeignKeyCheck(OFF); - for (String tableName : tableNames) { + for (String tableName: tableNames) { entityManager.createNativeQuery(String.format("TRUNCATE TABLE %s", tableName)).executeUpdate(); } setForeignKeyCheck(ON); } + @Transactional + public void initIncrement() { + String sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'test' AND AUTO_INCREMENT >= 1"; + List dirtyTables = entityManager.createNativeQuery(sql).getResultList(); + for (String tableName: dirtyTables) { + entityManager.createNativeQuery(String.format("ALTER TABLE %s AUTO_INCREMENT = 1", tableName)).executeUpdate(); + } + } + private void setForeignKeyCheck(int mode) { entityManager.createNativeQuery(String.format("SET FOREIGN_KEY_CHECKS = %d", mode)).executeUpdate(); } @@ -67,12 +68,16 @@ public void clear() { findDatabaseTableNames(); } entityManager.clear(); - truncate(); - // Redis 초기화 + truncateAllTable(); + clearRedis(); + clearMongo(); + } + + public void clearRedis() { redisTemplate.getConnectionFactory().getConnection().flushAll(); - // Mongo 초기화 - for (String collectionName : mongoTemplate.getCollectionNames()) { - mongoTemplate.remove(new Query(), collectionName); - } + } + + private void clearMongo() { + mongoTemplate.getCollectionNames().forEach(collectionName -> mongoTemplate.remove(new Query(), collectionName)); } } diff --git a/src/test/java/in/koreatech/koin/support/JsonAssertions.java b/src/test/java/in/koreatech/koin/support/JsonAssertions.java index c7ef8b5d7..9a53b47b6 100644 --- a/src/test/java/in/koreatech/koin/support/JsonAssertions.java +++ b/src/test/java/in/koreatech/koin/support/JsonAssertions.java @@ -1,22 +1,56 @@ package in.koreatech.koin.support; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.util.List; import java.util.Map; import org.assertj.core.api.Assertions; +import org.hibernate.query.sqm.sql.ConversionException; +import org.springframework.test.web.servlet.MvcResult; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import in.koreatech.koin.domain.bus.dto.SingleBusTimeResponse; public class JsonAssertions { private static final ObjectMapper objectMapper = new ObjectMapper(); + static { + objectMapper.registerModule(new JavaTimeModule()); + objectMapper.findAndRegisterModules(); // 다른 모듈들도 자동 등록 + } + public static JsonStringAssert assertThat(String expect) { return new JsonStringAssert(expect); } + public static JsonNode convertJsonNode(MvcResult mvcResult) { + try { + return objectMapper.readTree(mvcResult.getResponse().getContentAsString()); + } catch (JsonProcessingException e) { + throw new ConversionException("JsonString to JsonNode convert exception: " + e.getMessage()); + } catch (UnsupportedEncodingException e) { + throw new ConversionException("Response to String convert exception: " + e.getMessage()); + } + } + + public static List convertToList(JsonNode jsonNode, Class clazz) { + try { + return objectMapper.readValue( + jsonNode.toString(), + objectMapper.getTypeFactory().constructCollectionType(List.class, clazz) + ); + } catch (JsonProcessingException e) { + throw new ConversionException("JsonNode to List conversion exception: " + e.getMessage()); + } + } + public static class JsonStringAssert { private final String expect; diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 0d9e7584f..1c391098f 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -30,11 +30,21 @@ spring: server: tomcat: max-http-form-post-size: 10MB +# MockMvc이용하면 한글 깨지는 문제 해결하는 설정 + servlet: + encoding: + force-response: true logging: level: org: + springframework: + transaction: DEBUG # 스프링 트랜잭션 관련 로그 출력 + orm.jpa.JpaTransactionManager: DEBUG hibernate: + engine: + transaction: + internal: DEBUG # Hibernate 트랜잭션 로그 상세 출력 type: descriptor: sql: trace