mirror of
https://github.com/st-tu-dresden-praktikum/swt23w23
synced 2024-06-05 00:13:42 +02:00
756 lines
28 KiB
Java
756 lines
28 KiB
Java
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||
// SPDX-FileCopyrightText: 2023-2024 swt23w23
|
||
package catering.inventory;
|
||
|
||
import static catering.inventory.InventoryControllerIntegrationTests.PermissionResult.FORBIDDEN;
|
||
import static catering.inventory.InventoryControllerIntegrationTests.PermissionResult.LOGIN;
|
||
import static catering.inventory.InventoryControllerIntegrationTests.PermissionResult.OK;
|
||
import static catering.inventory.InventoryControllerIntegrationTests.PermissionResult.OVERVIEW;
|
||
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.hamcrest.CoreMatchers.not;
|
||
import static org.salespointframework.core.Currencies.EURO;
|
||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous;
|
||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||
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 java.math.BigDecimal;
|
||
import java.util.Optional;
|
||
import java.util.Set;
|
||
|
||
import catering.orderCatalog.CustomCatalogEntryRepository;
|
||
import org.javamoney.moneta.Money;
|
||
import org.junit.jupiter.api.BeforeEach;
|
||
import org.junit.jupiter.api.Test;
|
||
import org.salespointframework.catalog.Product;
|
||
import org.salespointframework.catalog.Product.ProductIdentifier;
|
||
import org.salespointframework.inventory.UniqueInventory;
|
||
import org.salespointframework.inventory.UniqueInventoryItem;
|
||
import org.salespointframework.quantity.Metric;
|
||
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 org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||
|
||
import catering.catalog.CateringCatalog;
|
||
import catering.catalog.Consumable;
|
||
import catering.catalog.Rentable;
|
||
import catering.order.OrderType;
|
||
|
||
@AutoConfigureMockMvc
|
||
@SpringBootTest
|
||
class InventoryControllerIntegrationTests {
|
||
@Autowired
|
||
MockMvc mvc;
|
||
|
||
@Autowired
|
||
UniqueInventory<UniqueInventoryItem> inventory;
|
||
|
||
@Autowired
|
||
CateringCatalog catalog;
|
||
|
||
@Autowired
|
||
private CustomCatalogEntryRepository catalogEntryRepository;
|
||
|
||
UniqueInventoryItem anyConsumableItem;
|
||
UniqueInventoryItem anyRentableItem;
|
||
Consumable anyConsumable;
|
||
Rentable anyRentable;
|
||
ProductIdentifier anyConsumableId;
|
||
ProductIdentifier anyRentableId;
|
||
|
||
UniqueInventoryItem anyItemOf(Class<? extends Product> type) {
|
||
switch (type.getSimpleName()) {
|
||
case "Consumable":
|
||
return anyConsumableItem;
|
||
case "Rentable":
|
||
return anyRentableItem;
|
||
default:
|
||
throw new IllegalArgumentException("Invalid product type!");
|
||
}
|
||
}
|
||
|
||
private long countItems() {
|
||
return inventory.findAll().stream().count();
|
||
}
|
||
|
||
/**
|
||
* Functional interface for comparing two {@link BigDecimal}s.
|
||
*
|
||
* It can be used for jUnit tests with
|
||
* {@code .withEqualsForType(InventoryControllerIntegrationTests::compareBigDecimal, BigDecimal.class)}.
|
||
*
|
||
* This is required when comparing {@link Quantity}s with a {@link Metric} of
|
||
* {@link Metric#LITER}.
|
||
*
|
||
* @param a {@link BigDecimal}
|
||
* @param b {@link BigDecimal}
|
||
* @return {@literal true} if they are equal, {@literal false} if not
|
||
*/
|
||
private static boolean compareBigDecimal(BigDecimal a, BigDecimal b) {
|
||
return a.compareTo(b) == 0;
|
||
}
|
||
|
||
@BeforeEach
|
||
void populateAnyInventoryItem() {
|
||
catalogEntryRepository.deleteAll();
|
||
inventory.deleteAll();
|
||
catalog.deleteAll();
|
||
|
||
anyConsumableItem = inventory.save(new UniqueInventoryItem(
|
||
catalog.save(new Consumable("Any Consumable", Money.of(1, EURO), Money.of(0.5, EURO),
|
||
Optional.of(Money.of(0.75, EURO)), Set.of(OrderType.EVENT_CATERING, OrderType.PARTY_SERVICE),
|
||
Metric.UNIT)),
|
||
Quantity.of(1)));
|
||
anyRentableItem = inventory.save(new UniqueInventoryItem(
|
||
catalog.save(new Rentable("Any Rentable", Money.of(1, EURO), Money.of(0.5, EURO),
|
||
Set.of(OrderType.EVENT_CATERING, OrderType.PARTY_SERVICE), Metric.UNIT)),
|
||
Quantity.of(1)));
|
||
|
||
anyConsumable = (Consumable) anyConsumableItem.getProduct();
|
||
anyRentable = (Rentable) anyRentableItem.getProduct();
|
||
|
||
anyConsumableId = anyConsumable.getId();
|
||
anyRentableId = anyRentable.getId();
|
||
}
|
||
|
||
void updateAnies() {
|
||
anyConsumableItem = inventory.findById(anyConsumableItem.getId()).get();
|
||
anyRentableItem = inventory.findById(anyRentableItem.getId()).get();
|
||
|
||
anyConsumable = (Consumable) anyConsumableItem.getProduct();
|
||
anyRentable = (Rentable) anyRentableItem.getProduct();
|
||
}
|
||
|
||
/*
|
||
* Tests for listing
|
||
*/
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanList() throws Exception {
|
||
mvc.perform(get("/inventory"))
|
||
.andExpect(status().isOk())
|
||
.andExpect(content().string(containsString(anyConsumableItem.getProduct().getName())));
|
||
}
|
||
|
||
/*
|
||
* Tests for adding
|
||
*/
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanReachAddConsumablePage() throws Exception {
|
||
mvc.perform(get("/inventory/add")
|
||
.queryParam("type", Consumable.class.getSimpleName()))
|
||
.andExpect(status().isOk())
|
||
.andExpect(content().string(containsString("Produkt anlegen")));
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanReachAddRentablePage() throws Exception {
|
||
mvc.perform(get("/inventory/add")
|
||
.queryParam("type", Rentable.class.getSimpleName()))
|
||
.andExpect(status().isOk())
|
||
.andExpect(content().string(containsString("Produkt anlegen")));
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCantReachAddInvalidPage() throws Exception {
|
||
mvc.perform(get("/inventory/add")
|
||
.queryParam("type", "INVALID"))
|
||
.andExpect(status().is3xxRedirection()); // not good error handling
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanAddConsumableWithoutPromotion() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Consumable.class.getSimpleName())
|
||
.param("name", "Added Consumable")
|
||
.param("quantity", "100")
|
||
.param("orderTypes", "EVENT_CATERING", "MOBILE_BREAKFAST", "PARTY_SERVICE")
|
||
.param("wholesalePrice", "3.00")
|
||
.param("retailPrice", "7.50")
|
||
.param("metric", "UNIT"))
|
||
.andExpect(redirectedUrl("/inventory"));
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore + 1);
|
||
|
||
assertThat(inventory.findAll().stream()
|
||
.filter(ie -> ie.getProduct().getName().equals("Added Consumable")).findAny())
|
||
.get()
|
||
.usingRecursiveComparison()
|
||
.ignoringFields("inventoryItemIdentifier.inventoryItemId", "isNew", "product.id.productId",
|
||
"product.isNew")
|
||
.isEqualTo(
|
||
new UniqueInventoryItem(new Consumable("Added Consumable",
|
||
Money.of(7.5, EURO),
|
||
Money.of(3, EURO),
|
||
Optional.empty(),
|
||
Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST, OrderType.PARTY_SERVICE),
|
||
Metric.UNIT),
|
||
Quantity.of(100)));
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanAddConsumableWithPromotion() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Consumable.class.getSimpleName())
|
||
.param("name", "Added Consumable")
|
||
.param("quantity", "99")
|
||
.param("orderTypes", "PARTY_SERVICE")
|
||
.param("wholesalePrice", "4.00")
|
||
.param("retailPrice", "7.00")
|
||
.param("promotionPrice", "5.00")
|
||
.param("metric", "LITER"))
|
||
.andExpect(redirectedUrl("/inventory"));
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore + 1);
|
||
|
||
assertThat(inventory.findAll().stream()
|
||
.filter(ie -> ie.getProduct().getName().equals("Added Consumable")).findAny())
|
||
.get()
|
||
.usingRecursiveComparison()
|
||
.withEqualsForType(InventoryControllerIntegrationTests::compareBigDecimal, BigDecimal.class)
|
||
.ignoringFields("inventoryItemIdentifier.inventoryItemId", "isNew", "product.id.productId",
|
||
"product.isNew")
|
||
.isEqualTo(
|
||
new UniqueInventoryItem(new Consumable("Added Consumable",
|
||
Money.of(7, EURO),
|
||
Money.of(4, EURO),
|
||
Optional.of(Money.of(5, EURO)),
|
||
Set.of(OrderType.PARTY_SERVICE),
|
||
Metric.LITER),
|
||
Quantity.of(99, Metric.LITER)));
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanAddRentable() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Rentable.class.getSimpleName())
|
||
.param("name", "Added Rentable")
|
||
.param("quantity", "8")
|
||
.param("orderTypes", "MOBILE_BREAKFAST")
|
||
.param("wholesalePrice", "1000.00")
|
||
.param("retailPrice", "10.25")
|
||
.param("metric", "UNIT"))
|
||
.andExpect(redirectedUrl("/inventory"));
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore + 1);
|
||
|
||
assertThat(inventory.findAll().stream()
|
||
.filter(ie -> ie.getProduct().getName().equals("Added Rentable")).findAny())
|
||
.get()
|
||
.usingRecursiveComparison()
|
||
.ignoringFields("inventoryItemIdentifier.inventoryItemId", "isNew", "product.id.productId",
|
||
"product.isNew")
|
||
.isEqualTo(
|
||
new UniqueInventoryItem(new Rentable("Added Rentable",
|
||
Money.of(10.25, EURO),
|
||
Money.of(1000.00, EURO),
|
||
Set.of(OrderType.MOBILE_BREAKFAST),
|
||
Metric.UNIT),
|
||
Quantity.of(8)));
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCantAddConsumableWithEmptyFields() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Consumable.class.getSimpleName())
|
||
.param("name", "")
|
||
.param("quantity", "")
|
||
.param("orderTypes", "")
|
||
.param("wholesalePrice", "")
|
||
.param("retailPrice", "")
|
||
.param("promotionPrice", "")
|
||
.param("metric", ""))
|
||
.andExpect(content().string(containsString("Ungültiger Name")))
|
||
.andExpect(content().string(containsString("Ungültige Menge")))
|
||
.andExpect(content().string(not(containsString("Ungültiger Buchungstyp")))) // can be empty set
|
||
.andExpect(content().string(containsString("Ungültiger Einkaufspreis")))
|
||
.andExpect(content().string(containsString("Ungültiger Verkaufspreis")))
|
||
.andExpect(content().string(not(containsString("Ungültiger Aktionspreis")))) // optional
|
||
.andExpect(content().string(containsString("Ungültige Einheit")));
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore);
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCantAddRentableWithEmptyFields() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Rentable.class.getSimpleName())
|
||
.param("name", "")
|
||
.param("quantity", "")
|
||
.param("orderTypes", "")
|
||
.param("wholesalePrice", "")
|
||
.param("retailPrice", "")
|
||
.param("metric", ""))
|
||
.andExpect(content().string(containsString("Ungültiger Name")))
|
||
.andExpect(content().string(containsString("Ungültige Menge")))
|
||
.andExpect(content().string(not(containsString("Ungültiger Buchungstyp")))) // can be empty set
|
||
.andExpect(content().string(containsString("Ungültiger Einkaufspreis")))
|
||
.andExpect(content().string(containsString("Ungültiger Verkaufspreis")))
|
||
.andExpect(content().string(containsString("Ungültige Einheit")));
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore);
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCantAddConsumableWithMissingFields() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Consumable.class.getSimpleName()))
|
||
.andExpect(content().string(containsString("Ungültiger Name")))
|
||
.andExpect(content().string(containsString("Ungültige Menge")))
|
||
.andExpect(content().string(containsString("Ungültiger Buchungstyp")))
|
||
.andExpect(content().string(containsString("Ungültiger Einkaufspreis")))
|
||
.andExpect(content().string(containsString("Ungültiger Verkaufspreis")))
|
||
.andExpect(content().string(not(containsString("Ungültiger Aktionspreis")))) // optional
|
||
.andExpect(content().string(not(containsString("Ungültige Einheit")))); // "" can be cast to Metric.UNIT
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore);
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCantAddRentableWithMissingFields() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Rentable.class.getSimpleName()))
|
||
.andExpect(content().string(containsString("Ungültiger Name")))
|
||
.andExpect(content().string(containsString("Ungültige Menge")))
|
||
.andExpect(content().string(containsString("Ungültiger Buchungstyp")))
|
||
.andExpect(content().string(containsString("Ungültiger Einkaufspreis")))
|
||
.andExpect(content().string(containsString("Ungültiger Verkaufspreis")))
|
||
.andExpect(content().string(not(containsString("Ungültige Einheit")))); // "" can be cast to Metric.UNIT
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore);
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCantAddConsumableWithNegativeNumbers() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Consumable.class.getSimpleName())
|
||
.param("name", "Added Consumable")
|
||
.param("quantity", "-12")
|
||
.param("orderTypes", "EVENT_CATERING")
|
||
.param("wholesalePrice", "-4.20")
|
||
.param("retailPrice", "-2.31")
|
||
.param("promotionPrice", "-7.14")
|
||
.param("metric", "LITER"))
|
||
.andExpect(content().string(containsString("Ungültige Menge")))
|
||
.andExpect(content().string(containsString("Ungültiger Einkaufspreis")))
|
||
.andExpect(content().string(containsString("Ungültiger Verkaufspreis")))
|
||
.andExpect(content().string(containsString("Ungültiger Aktionspreis")));
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore);
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCantAddRentableWithNegativeNumbers() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Rentable.class.getSimpleName())
|
||
.param("name", "Added Rentable")
|
||
.param("quantity", "-5")
|
||
.param("orderTypes", "MOBILE_BREAKFAST")
|
||
.param("wholesalePrice", "-589.12")
|
||
.param("retailPrice", "-12.40")
|
||
.param("metric", "UNIT"))
|
||
.andExpect(content().string(containsString("Ungültige Menge")))
|
||
.andExpect(content().string(containsString("Ungültiger Einkaufspreis")))
|
||
.andExpect(content().string(containsString("Ungültiger Verkaufspreis")));
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore);
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanAddConsumableWithZeroes() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Consumable.class.getSimpleName())
|
||
.param("name", "Added Consumable")
|
||
.param("quantity", "0")
|
||
.param("orderTypes", "EVENT_CATERING")
|
||
.param("wholesalePrice", "0")
|
||
.param("retailPrice", "0")
|
||
.param("promotionPrice", "0")
|
||
.param("metric", "UNIT"))
|
||
.andExpect(content().string(not(containsString("Ungültige Menge"))))
|
||
.andExpect(content().string(not(containsString("Ungültiger Einkaufspreis"))))
|
||
.andExpect(content().string(not(containsString("Ungültiger Verkaufspreis"))))
|
||
.andExpect(content().string(not(containsString("Ungültiger Aktionspreis"))));
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore + 1);
|
||
|
||
assertThat(inventory.findAll().stream().filter(i -> i.getProduct() instanceof Consumable))
|
||
.extracting("product.name", "quantity", "product.wholesalePrice", "product.retailPrice",
|
||
"product.promotionPrice")
|
||
.contains(tuple("Added Consumable", Quantity.of(0), Money.of(0, EURO), Money.of(0, EURO),
|
||
Optional.of(Money.of(0, EURO))));
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanAddRentableWithZeroes() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Rentable.class.getSimpleName())
|
||
.param("name", "Added Rentable")
|
||
.param("quantity", "0")
|
||
.param("orderTypes", "MOBILE_BREAKFAST")
|
||
.param("wholesalePrice", "0")
|
||
.param("retailPrice", "0")
|
||
.param("metric", "UNIT"))
|
||
.andExpect(content().string(not(containsString("Ungültige Menge"))))
|
||
.andExpect(content().string(not(containsString("Ungültiger Einkaufspreis"))))
|
||
.andExpect(content().string(not(containsString("Ungültiger Verkaufspreis"))));
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore + 1);
|
||
|
||
assertThat(inventory.findAll().stream())
|
||
.extracting("product.name", "quantity", "product.wholesalePrice", "product.retailPrice")
|
||
.contains(tuple("Added Rentable", Quantity.of(0), Money.of(0, EURO), Money.of(0, EURO)));
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void invalidAddReturnsNiceError() throws Exception {
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Consumable.class.getSimpleName())
|
||
.param("name", "")
|
||
.param("quantity", "10")
|
||
.param("wholesalePrice", "1.00")
|
||
.param("retailPrice", "2.00"))
|
||
.andExpect(content().string(containsString("Ungültiger Name")));
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void missingRetailPriceIsNoError() throws Exception {
|
||
mvc.perform(post("/inventory/add")
|
||
.queryParam("type", Consumable.class.getSimpleName())
|
||
.param("name", "MOCK")
|
||
.param("quantity", "10")
|
||
.param("wholesalePrice", "1.00")
|
||
.param("retailPrice", "2.00"))
|
||
.andExpect(content().string(not(containsString("Ungültiger Angebotspreis"))));
|
||
}
|
||
|
||
/*
|
||
* Tests for editing
|
||
*/
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanReachEditConsumablePage() throws Exception {
|
||
mvc.perform(get("/inventory/edit/" + anyConsumableId)
|
||
.queryParam("type", Consumable.class.getSimpleName()))
|
||
.andExpect(status().isOk())
|
||
.andExpect(content().string(containsString("Produkt bearbeiten")))
|
||
.andExpect(content().string(containsString(anyConsumable.getName())));
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanReachEditRentablePage() throws Exception {
|
||
mvc.perform(get("/inventory/edit/" + anyRentableId)
|
||
.queryParam("type", Rentable.class.getSimpleName()))
|
||
.andExpect(status().isOk())
|
||
.andExpect(content().string(containsString("Produkt bearbeiten")))
|
||
.andExpect(content().string(containsString(anyRentable.getName())));
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanEditConsumable() throws Exception {
|
||
mvc.perform(post("/inventory/edit/" + anyConsumableId)
|
||
.queryParam("type", Consumable.class.getSimpleName())
|
||
.param("name", "Edited Consumable")
|
||
.param("orderTypes", "PARTY_SERVICE", "MOBILE_BREAKFAST")
|
||
.param("quantity", "95")
|
||
.param("wholesalePrice", "10")
|
||
.param("retailPrice", "20")
|
||
.param("promotionPrice", "17"))
|
||
.andExpect(redirectedUrl("/inventory"));
|
||
|
||
updateAnies();
|
||
|
||
assertThat(anyConsumableItem.getQuantity()).isEqualTo(Quantity.of(95));
|
||
assertThat(anyConsumable)
|
||
.extracting("name", "wholesalePrice", "retailPrice", "promotionPrice")
|
||
.containsExactly("Edited Consumable", Money.of(10, EURO), Money.of(20, EURO),
|
||
Optional.of(Money.of(17, EURO)));
|
||
assertThat(anyConsumable.getCategories()).containsExactlyInAnyOrder("PARTY_SERVICE", "MOBILE_BREAKFAST");
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanEditRentable() throws Exception {
|
||
mvc.perform(post("/inventory/edit/" + anyConsumableId)
|
||
.queryParam("type", Consumable.class.getSimpleName())
|
||
.param("name", "Edited Rentable")
|
||
.param("orderTypes", "PARTY_SERVICE", "MOBILE_BREAKFAST")
|
||
.param("quantity", "95")
|
||
.param("wholesalePrice", "10")
|
||
.param("retailPrice", "20"))
|
||
.andExpect(redirectedUrl("/inventory"));
|
||
|
||
updateAnies();
|
||
|
||
assertThat(anyConsumableItem.getQuantity()).isEqualTo(Quantity.of(95));
|
||
assertThat(anyConsumable)
|
||
.extracting("name", "wholesalePrice", "retailPrice")
|
||
.containsExactly("Edited Rentable", Money.of(10, EURO), Money.of(20, EURO));
|
||
assertThat(anyConsumable.getCategories()).containsExactlyInAnyOrder("PARTY_SERVICE", "MOBILE_BREAKFAST");
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanEditConsumablePromo() throws Exception {
|
||
mvc.perform(post("/inventory/edit/" + anyConsumableId)
|
||
.queryParam("type", Consumable.class.getSimpleName())
|
||
.param("name", anyConsumable.getName())
|
||
.param("orderTypes", anyConsumable.getCategories().stream().toList().toArray(new String[0]))
|
||
.param("quantity", anyConsumableItem.getQuantity().getAmount().toString())
|
||
.param("wholesalePrice", anyConsumable.getWholesalePrice().getNumber().toString())
|
||
.param("retailPrice", anyConsumable.getRetailPrice().getNumber().toString())
|
||
.param("promotionPrice", ""))
|
||
.andExpect(redirectedUrl("/inventory"));
|
||
|
||
updateAnies();
|
||
|
||
assertThat(anyConsumable.getPromotionPrice()).isEmpty();
|
||
|
||
mvc.perform(post("/inventory/edit/" + anyConsumableId)
|
||
.queryParam("type", Consumable.class.getSimpleName())
|
||
.param("name", anyConsumable.getName())
|
||
.param("orderTypes", anyConsumable.getCategories().stream().toList().toArray(new String[0]))
|
||
.param("quantity", anyConsumableItem.getQuantity().getAmount().toString())
|
||
.param("wholesalePrice", anyConsumable.getWholesalePrice().getNumber().toString())
|
||
.param("retailPrice", anyConsumable.getRetailPrice().getNumber().toString())
|
||
.param("promotionPrice", "7"))
|
||
.andExpect(redirectedUrl("/inventory"));
|
||
|
||
updateAnies();
|
||
|
||
assertThat(anyConsumable.getPromotionPrice()).get().isEqualTo(Money.of(7, EURO));
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanEditConsumableWithMissingFields() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/edit/" + anyConsumableId)
|
||
.queryParam("type", Consumable.class.getSimpleName()))
|
||
.andExpect(content().string(containsString("Ungültiger Name")))
|
||
.andExpect(content().string(containsString("Ungültige Menge")))
|
||
.andExpect(content().string(containsString("Ungültiger Buchungstyp")))
|
||
.andExpect(content().string(containsString("Ungültiger Einkaufspreis")))
|
||
.andExpect(content().string(containsString("Ungültiger Verkaufspreis")))
|
||
.andExpect(content().string(not(containsString("Ungültiger Aktionspreis")))) // optional
|
||
.andExpect(content().string(not(containsString("Ungültige Einheit")))); // "" can be cast to Metric.UNIT
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore);
|
||
}
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanEditRentableWithMissingFields() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(post("/inventory/edit/" + anyRentableId)
|
||
.queryParam("type", Rentable.class.getSimpleName()))
|
||
.andExpect(content().string(containsString("Ungültiger Name")))
|
||
.andExpect(content().string(containsString("Ungültige Menge")))
|
||
.andExpect(content().string(containsString("Ungültiger Buchungstyp")))
|
||
.andExpect(content().string(containsString("Ungültiger Einkaufspreis")))
|
||
.andExpect(content().string(containsString("Ungültiger Verkaufspreis")))
|
||
.andExpect(content().string(not(containsString("Ungültige Einheit")))); // "" can be cast to Metric.UNIT
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore);
|
||
}
|
||
|
||
// tests for editing
|
||
// with all fields empty,
|
||
// all prices negative
|
||
// and all prices zero
|
||
// are omitted for brevity,
|
||
// as they are essentially the same as with adding
|
||
|
||
/*
|
||
* Tests for deleting
|
||
*/
|
||
|
||
@Test
|
||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||
void adminCanDelete() throws Exception {
|
||
long itemCountBefore = countItems();
|
||
|
||
mvc.perform(get("/inventory/delete/" + anyConsumableId))
|
||
.andExpect(redirectedUrl("/inventory"));
|
||
|
||
assertThat(countItems()).isEqualTo(itemCountBefore - 1);
|
||
|
||
assertThat(inventory.findAll().stream())
|
||
.extracting("product.name", "quantity")
|
||
.doesNotContain(tuple(anyConsumable.getName(), anyConsumableItem.getQuantity()));
|
||
}
|
||
|
||
/*
|
||
* Tests for authorization
|
||
*/
|
||
|
||
static enum PermissionResult {
|
||
/**
|
||
* The result is an HTTP 403 (Forbidden) error code
|
||
*/
|
||
FORBIDDEN,
|
||
/**
|
||
* The result is a redirect to the login page
|
||
*/
|
||
LOGIN,
|
||
/**
|
||
* The result is an HTTP 200 (Ok) status code
|
||
*/
|
||
OK,
|
||
/**
|
||
* The result is a redirect to the inventory overview page
|
||
*/
|
||
OVERVIEW;
|
||
|
||
@FunctionalInterface
|
||
public interface PermissionChecker {
|
||
ResultActions check(ResultActions resultActions) throws Exception;
|
||
}
|
||
|
||
PermissionChecker toChecker() {
|
||
return switch (this) {
|
||
case FORBIDDEN -> ra -> ra.andExpect(status().isForbidden());
|
||
case LOGIN -> InventoryControllerIntegrationTests::assertRedirectsToLogin;
|
||
case OK -> ra -> ra.andExpect(status().isOk());
|
||
case OVERVIEW -> ra -> ra.andExpect(redirectedUrl("/inventory"));
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 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
|
||
*/
|
||
static 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")));
|
||
}
|
||
|
||
ResultActions requestAsAnonymous(MockHttpServletRequestBuilder requestBuilder) throws Exception {
|
||
return mvc.perform(requestBuilder.with(anonymous()));
|
||
}
|
||
|
||
ResultActions requestAsCustomer(MockHttpServletRequestBuilder requestBuilder) throws Exception {
|
||
return mvc.perform(requestBuilder.with(user("unprivileged").roles("CUSTOMER")));
|
||
}
|
||
|
||
ResultActions requestAsAdministrator(MockHttpServletRequestBuilder requestBuilder) throws Exception {
|
||
return mvc.perform(requestBuilder.with(user("admin").roles("ADMIN")));
|
||
}
|
||
|
||
void checkPermissions(MockHttpServletRequestBuilder requestBuilder, PermissionResult anonymous,
|
||
PermissionResult customer, PermissionResult administrator) throws Exception {
|
||
anonymous.toChecker().check(requestAsAnonymous(requestBuilder));
|
||
customer.toChecker().check(requestAsCustomer(requestBuilder));
|
||
administrator.toChecker().check(requestAsAdministrator(requestBuilder));
|
||
}
|
||
|
||
@Test
|
||
void checkPermissionsForList() throws Exception {
|
||
checkPermissions(get("/inventory"), LOGIN, FORBIDDEN, OK);
|
||
}
|
||
|
||
@Test
|
||
void checkPermissionsForEditPage() throws Exception {
|
||
for (Class<? extends Product> type : Set.of(Consumable.class, Rentable.class)) {
|
||
checkPermissions(
|
||
get("/inventory/edit/" + anyItemOf(type).getProduct().getId())
|
||
.queryParam("type", type.getSimpleName()),
|
||
LOGIN, FORBIDDEN, OK);
|
||
}
|
||
}
|
||
|
||
@Test
|
||
void checkPermissionsForEdit() throws Exception {
|
||
for (Class<? extends Product> type : Set.of(Consumable.class, Rentable.class)) {
|
||
checkPermissions(
|
||
post("/inventory/edit/" + anyItemOf(type).getProduct().getId())
|
||
.queryParam("type", type.getSimpleName()),
|
||
LOGIN, FORBIDDEN, OK);
|
||
}
|
||
}
|
||
|
||
@Test
|
||
void checkPermissionsForAddPage() throws Exception {
|
||
checkPermissions(get("/inventory/add").queryParam("type", Consumable.class.getSimpleName()), LOGIN, FORBIDDEN,
|
||
OK);
|
||
}
|
||
|
||
@Test
|
||
void checkPermissionsForAdd() throws Exception {
|
||
checkPermissions(post("/inventory/add").queryParam("type", Consumable.class.getSimpleName()), LOGIN, FORBIDDEN,
|
||
OK);
|
||
}
|
||
|
||
@Test
|
||
void checkPermissionsForDelete() throws Exception {
|
||
checkPermissions(get("/inventory/delete/" + anyConsumableId), LOGIN, FORBIDDEN, OVERVIEW);
|
||
}
|
||
}
|