From f7d859b25df4807934b4cf16a718e5d50f54dea3 Mon Sep 17 00:00:00 2001 From: Simon Bruder Date: Sat, 18 Nov 2023 14:58:17 +0100 Subject: [PATCH] Add integration tests for inventory controller --- .../InventoryControllerIntegrationTests.java | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 src/test/java/catering/inventory/InventoryControllerIntegrationTests.java diff --git a/src/test/java/catering/inventory/InventoryControllerIntegrationTests.java b/src/test/java/catering/inventory/InventoryControllerIntegrationTests.java new file mode 100644 index 0000000..8e702c5 --- /dev/null +++ b/src/test/java/catering/inventory/InventoryControllerIntegrationTests.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2023 Simon Bruder + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package catering.inventory; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.endsWith; +import static org.salespointframework.core.Currencies.EURO; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.javamoney.moneta.Money; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.salespointframework.catalog.Product.ProductIdentifier; +import org.salespointframework.inventory.UniqueInventory; +import org.salespointframework.inventory.UniqueInventoryItem; +import org.salespointframework.quantity.Quantity; +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.http.HttpHeaders; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import catering.catalog.CatalogDummy; +import catering.catalog.CatalogDummyType; + +@AutoConfigureMockMvc +@SpringBootTest +class InventoryControllerIntegrationTests { + @Autowired + MockMvc mvc; + + @Autowired + UniqueInventory inventory; + + UniqueInventoryItem anyInventoryItem; + CatalogDummy anyProduct; + ProductIdentifier anyPid; + + @BeforeEach + void populateAnyInventoryItem() { + anyInventoryItem = inventory.findAll().stream().findAny().get(); + anyProduct = (CatalogDummy) anyInventoryItem.getProduct(); + anyPid = anyProduct.getId(); + } + + @Test + @WithMockUser(username = "admin", roles = "ADMIN") + void adminCanList() throws Exception { + mvc.perform(get("/inventory")) + .andExpect(status().isOk()) + .andExpect(content().string(containsString(anyPid.toString()))); + } + + @Test + @WithMockUser(username = "admin", roles = "ADMIN") + void adminCanAdd() throws Exception { + mvc.perform(get("/inventory/add")) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("Produkt anlegen"))); + + long itemCountBefore = inventory.findAll().stream().count(); + + mvc.perform(post("/inventory/add") + .param("type", "CONSUMABLE") + .param("name", "MOCK Schnitzel Wiener Art (vegan)") + .param("quantity", "100") + .param("wholesalePrice", "3.00") + .param("retailPrice", "7.50") + .param("promotionPrice", "6.66")) + .andExpect(redirectedUrl("/inventory")); + + long itemCountAfter = inventory.findAll().stream().count(); + + assertThat(itemCountAfter).isEqualTo(itemCountBefore + 1); + + // TODO: this must be changed once the catalog split is done + assertThat(inventory.findAll().stream()) + .extracting("product.type", "product.name", "quantity", "product.wholesalePrice", "product.price", + "product.promotionPrice") + .contains(tuple(CatalogDummyType.CONSUMABLE, "MOCK Schnitzel Wiener Art (vegan)", Quantity.of(100), + Money.of(3, EURO), Money.of(7.5, EURO), Money.of(6.66, EURO))); + } + + @Test + @WithMockUser(username = "admin", roles = "ADMIN") + void adminCanDelete() throws Exception { + long itemCountBefore = inventory.findAll().stream().count(); + + mvc.perform(get("/inventory/delete/" + anyPid)) + .andExpect(redirectedUrl("/inventory")); + + long itemCountAfter = inventory.findAll().stream().count(); + + assertThat(itemCountAfter).isEqualTo(itemCountBefore - 1); + + // TODO: this must be changed once the catalog split is done + assertThat(inventory.findAll().stream()) + .extracting("product.type", "product.name", "quantity") + .doesNotContain(tuple(anyProduct.getType(), anyProduct.getName(), anyInventoryItem.getQuantity())); + } + + @Test + @WithMockUser(username = "admin", roles = "ADMIN") + void adminCanEditConsumable() throws Exception { + // TODO: this must be changed once the catalog split is done + CatalogDummy anyConsumable = inventory.findAll().stream() + .map(UniqueInventoryItem::getProduct) + .map(p -> (CatalogDummy) p) + .filter(cd -> cd.getType().equals(CatalogDummyType.CONSUMABLE)) + .findAny() + .get(); + + mvc.perform(get("/inventory/edit/" + anyConsumable.getId())) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("Produkt bearbeiten"))); + + boolean hasPromotionPrice = anyConsumable.getPromotionPrice() != null; + + mvc.perform(post("/inventory/edit/" + anyConsumable.getId()) + .param("type", "CONSUMABLE") + .param("name", "MOCK edited") + .param("quantity", "4711") + .param("wholesalePrice", "0.01") + .param("retailPrice", "0.01") + .param("promotionPrice", hasPromotionPrice ? "" : "5")) + .andExpect(redirectedUrl("/inventory")); + + UniqueInventoryItem editedInventoryItem = inventory.findByProductIdentifier(anyConsumable.getId()).stream() + .findAny().get(); + CatalogDummy editedProduct = (CatalogDummy) editedInventoryItem.getProduct(); + + assertThat(editedInventoryItem.getQuantity()).isEqualTo(Quantity.of(4711)); + assertThat(editedProduct) + .extracting("type", "name", "wholesalePrice", "price", "promotionPrice") + .containsExactly(CatalogDummyType.CONSUMABLE, "MOCK edited", Money.of(0.01, EURO), + Money.of(0.01, EURO), hasPromotionPrice ? null : Money.of(0.01, EURO)); + } + + /** + * Helper function for asserting that protected endpoints are not accessible by + * unauthentificated users. + * + * @param resultActions the result of + * {@link org.springframework.test.web.servlet.MockMvc#perform} + * @return the {@link ResultActions} given as a parameter to perform more + * matching on + */ + ResultActions assertRedirectsToLogin(ResultActions resultActions) throws Exception { + // Spring security uses the full URL to redirect to login, + // so we can’t use redirectedUrl("/login") in this case. + return resultActions + .andExpect(status().is3xxRedirection()) // concrete redirection type is implementation detail + .andExpect(header().string(HttpHeaders.LOCATION, endsWith("/login"))); + } + + @Test + void disallowUnauthorizedList() throws Exception { + assertRedirectsToLogin(mvc.perform(get("/inventory"))); + } + + @Test + void disallowUnauthorizedEditPage() throws Exception { + assertRedirectsToLogin(mvc.perform(get("/inventory/edit/" + anyPid))); + } + + @Test + void disallowUnauthorizedEdit() throws Exception { + assertRedirectsToLogin(mvc.perform(post("/inventory/edit/" + anyPid))); + } + + @Test + void disallowUnauthorizedAddPage() throws Exception { + assertRedirectsToLogin(mvc.perform(get("/inventory/add"))); + } + + @Test + void disallowUnauthorizedAdd() throws Exception { + assertRedirectsToLogin(mvc.perform(post("/inventory/add"))); + } + + @Test + void disallowUnauthorizedDelete() throws Exception { + assertRedirectsToLogin(mvc.perform(get("/inventory/delete/" + anyPid))); + } +}