diff --git a/pom.xml b/pom.xml index cf88828b..e429444b 100644 --- a/pom.xml +++ b/pom.xml @@ -53,11 +53,6 @@ reactor-spring 1.0.1.RELEASE - - org.json - json - 20190722 - org.postgresql postgresql @@ -80,7 +75,7 @@ org.keycloak keycloak-spring-boot-starter - + diff --git a/src/main/java/de/sakpaas/backend/service/UserService.java b/src/main/java/de/sakpaas/backend/service/UserService.java index 1ecbe81c..7c9897a7 100644 --- a/src/main/java/de/sakpaas/backend/service/UserService.java +++ b/src/main/java/de/sakpaas/backend/service/UserService.java @@ -79,7 +79,7 @@ public UserInfoDto getUserInfo(String header) throws InvalidBearerTokenException * @throws VerificationException iff the given JWT is invalid */ @VisibleForTesting - AccessToken verifyToken(String token) throws VerificationException { + public AccessToken verifyToken(String token) throws VerificationException { return AdapterTokenVerifier.verifyToken( token, keycloakConfiguration.getKeycloakDeployment() diff --git a/src/main/java/de/sakpaas/backend/v2/controller/UserController.java b/src/main/java/de/sakpaas/backend/v2/controller/UserController.java index 40a95d88..dfc7c0ba 100644 --- a/src/main/java/de/sakpaas/backend/v2/controller/UserController.java +++ b/src/main/java/de/sakpaas/backend/v2/controller/UserController.java @@ -69,7 +69,7 @@ public ResponseEntity> getFavorites( List favorites = favoriteService.findByUserUuid(userInfo.getId()); List response = favorites.stream() - .map(favorite -> locationMapper.mapLocationToOutputDto(favorite.getLocation())) + .map(favorite -> locationMapper.mapLocationToOutputDto(favorite.getLocation(), userInfo)) .collect(Collectors.toList()); return new ResponseEntity<>(response, OK); @@ -79,7 +79,7 @@ public ResponseEntity> getFavorites( * Post Endpoint that creates a favorite. * * @param locationId the location Id of the new favorite - * @param header The Authorization-Header that has to be provided in the request. + * @param header The Authorization-Header that has to be provided in the request. * @return Returns a ResponseEntity */ @PostMapping("/self/favorites/{id}") @@ -101,7 +101,7 @@ public ResponseEntity postFavorite( * Delete Endpoint that deletes a favorite. * * @param locationId the location Id of the new favorite - * @param header The Authorization-Header that has to be provided in the request. + * @param header The Authorization-Header that has to be provided in the request. * @return Returns a ResponseEntity */ @DeleteMapping("/self/favorites/{id}") diff --git a/src/main/java/de/sakpaas/backend/v2/mapper/LocationMapper.java b/src/main/java/de/sakpaas/backend/v2/mapper/LocationMapper.java index 4244d60d..6475abbd 100644 --- a/src/main/java/de/sakpaas/backend/v2/mapper/LocationMapper.java +++ b/src/main/java/de/sakpaas/backend/v2/mapper/LocationMapper.java @@ -55,7 +55,7 @@ public LocationResultLocationDto mapLocationToOutputDto(Location location, UserI boolean flag = favoriteRepository.findByUserUuid(user.getId()) .stream() - .anyMatch(favorite -> favorite.getLocation() == location); + .anyMatch(favorite -> favorite.getLocation().equals(location)); return new LocationResultLocationDto( location.getId(), location.getName(), flag, diff --git a/src/test/java/de/sakpaas/backend/HappyHamsterTest.java b/src/test/java/de/sakpaas/backend/HappyHamsterTest.java index fb1670fb..b1a2406e 100644 --- a/src/test/java/de/sakpaas/backend/HappyHamsterTest.java +++ b/src/test/java/de/sakpaas/backend/HappyHamsterTest.java @@ -2,12 +2,16 @@ import de.sakpaas.backend.util.KeycloakConfiguration; import org.junit.ClassRule; +import org.keycloak.adapters.springboot.KeycloakSpringBootProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.mock.mockito.MockBean; import org.testcontainers.containers.PostgreSQLContainer; +@EnableConfigurationProperties(KeycloakSpringBootProperties.class) public class HappyHamsterTest { + @MockBean - private KeycloakConfiguration keycloakConfiguration; + protected KeycloakConfiguration keycloakConfiguration; @ClassRule public static PostgreSQLContainer container = PostgresqlContainer.getInstance(); diff --git a/src/test/java/de/sakpaas/backend/IntegrationTest.java b/src/test/java/de/sakpaas/backend/IntegrationTest.java new file mode 100644 index 00000000..c7f5f7e9 --- /dev/null +++ b/src/test/java/de/sakpaas/backend/IntegrationTest.java @@ -0,0 +1,200 @@ +package de.sakpaas.backend; + +import static org.hamcrest.Matchers.anything; +import static org.hamcrest.Matchers.equalTo; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +import de.sakpaas.backend.model.Favorite; +import de.sakpaas.backend.model.Location; +import de.sakpaas.backend.model.Occupancy; +import de.sakpaas.backend.service.AddressRepository; +import de.sakpaas.backend.service.FavoriteRepository; +import de.sakpaas.backend.service.LocationDetailsRepository; +import de.sakpaas.backend.service.LocationRepository; +import de.sakpaas.backend.service.OccupancyRepository; +import de.sakpaas.backend.service.UserService; +import java.util.List; +import java.util.UUID; +import lombok.SneakyThrows; +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.JSONValue; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.keycloak.common.VerificationException; +import org.keycloak.representations.AccessToken; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultMatcher; + +public class IntegrationTest extends HappyHamsterTest { + + public static final UUID USER_UUID = UUID.fromString("550e8400-e29b-11d4-a716-446655440000"); + public static final AccessToken USER_ACCESS_TOKEN = new AccessToken(); + public static final String AUTHENTICATION_VALID = "Bearer token.valid.token"; + public static final String AUTHENTICATION_INVALID = "Bearer token.invalid.token"; + @Autowired + protected MockMvc mockMvc; + @SpyBean + protected UserService userService; + @Autowired + protected OccupancyRepository occupancyRepository; + @Autowired + protected FavoriteRepository favoriteRepository; + @Autowired + protected LocationRepository locationRepository; + @Autowired + protected LocationDetailsRepository locationDetailsRepository; + @Autowired + protected AddressRepository addressRepository; + + @BeforeAll + static void setupAll() { + USER_ACCESS_TOKEN.setSubject(USER_UUID.toString()); + USER_ACCESS_TOKEN.setPreferredUsername("test.user"); + USER_ACCESS_TOKEN.setName("Testonius Tester"); + USER_ACCESS_TOKEN.setGivenName("Testonius"); + USER_ACCESS_TOKEN.setFamilyName("Tester"); + USER_ACCESS_TOKEN.setEmail("testonius.tester@example.com"); + } + + @SneakyThrows + @BeforeEach + void setup() { + Mockito.doAnswer(invocation -> { + if (invocation.getArgument(0).equals("token.valid.token")) { + return USER_ACCESS_TOKEN; + } + throw new VerificationException(); + }) + .when(userService) + .verifyToken(Mockito.any()); + + // Cleanup tables + occupancyRepository.deleteAll(); + favoriteRepository.deleteAll(); + locationRepository.deleteAll(); + addressRepository.deleteAll(); + locationDetailsRepository.deleteAll(); + } + + @AfterEach + void tearDown() { + // Cleanup tables + occupancyRepository.deleteAll(); + favoriteRepository.deleteAll(); + locationRepository.deleteAll(); + addressRepository.deleteAll(); + locationDetailsRepository.deleteAll(); + } + + protected Location insert(Location location) { + locationDetailsRepository.save(location.getDetails()); + addressRepository.save(location.getAddress()); + return locationRepository.save(location); + } + + protected Favorite insert(Favorite favorite) { + return favoriteRepository.save(favorite); + } + + protected Occupancy insert(Occupancy occupancy) { + return occupancyRepository.save(occupancy); + } + + protected ResultMatcher expectErrorObject() { + return result -> { + ResultMatcher[] matcher = new ResultMatcher[] { + jsonPath("$.timestamp").exists(), + jsonPath("$.status").isNumber(), + jsonPath("$.error").isString(), + jsonPath("$.path").isString(), + jsonPath("$.context.textId").isString(), + jsonPath("$.context.parameters").isArray(), + jsonPath("$.context.defaultMessage").isString(), + }; + for (ResultMatcher resultMatcher : matcher) { + resultMatcher.match(result); + } + }; + } + + /** + * Checks if the given {@link org.springframework.test.web.servlet.MvcResult} has the form of a + * Location as defined in the openAPI specification. The fields given in the location parameter + * have to be correct. + * + * @param location the baseline {@link Location} + * @return the {@link ResultMatcher} + */ + protected ResultMatcher expectLocation(Location location) { + return result -> this.match(result, "$", location); + } + + + /** + * Checks if the given {@link org.springframework.test.web.servlet.MvcResult} has the form of a + * List of Locations as defined in the openAPI specification. The fields given in the locations + * parameter have to be correct. + * + * @param locations the baseline {@link List} of {@link Location}s + * @return the {@link ResultMatcher} + */ + protected ResultMatcher expectLocationList(List locations) { + return result -> { + // Check if the result is an array + jsonPath("$").isArray().match(result); + + // Find correct Location for array element + JSONArray array = (JSONArray) JSONValue.parse(result.getResponse().getContentAsString()); + for (int i = 0; i < array.size(); i++) { + long id = ((JSONObject) array.get(i)) + .getAsNumber("id") + .longValue(); + Location location = locations.stream() + .filter(loc -> loc.getId() == id) + .findAny() + .orElseThrow(() -> new AssertionError("Unknown LocationId in result.")); + this.match(result, "$[" + i + "]", location); + } + }; + } + + private void match(MvcResult result, String selector, Location location) throws Exception { + // Define assertions + ResultMatcher[] matcher = new ResultMatcher[] { + jsonPath(selector + ".id").value(equalTo(location.getId()), Long.class), + jsonPath(selector + ".name").value(location.getName()), + // Favorite (unknown contents) + jsonPath(selector + ".favorite", anything()), + // Coordinates + jsonPath(selector + ".coordinates.latitude").value(location.getLatitude()), + jsonPath(selector + ".coordinates.longitude").value(location.getLongitude()), + // Details + jsonPath(selector + ".details.type").value(location.getDetails().getType()), + jsonPath(selector + ".details.brand").value(location.getDetails().getBrand()), + jsonPath(selector + ".details.openingHours") + .value(location.getDetails().getOpeningHours()), + // Occupancy (unknown contents) + jsonPath(selector + ".occupancy.value", anything()), + jsonPath(selector + ".occupancy.count", anything()), + jsonPath(selector + ".occupancy.latestReport", anything()), + // Address + jsonPath(selector + ".address.country").value(location.getAddress().getCountry()), + jsonPath(selector + ".address.city").value(location.getAddress().getCity()), + jsonPath(selector + ".address.postcode") + .value(location.getAddress().getPostcode()), + jsonPath(selector + ".address.street").value(location.getAddress().getStreet()), + jsonPath(selector + ".address.housenumber") + .value(location.getAddress().getHousenumber()) + }; + // Run assertions + for (ResultMatcher resultMatcher : matcher) { + resultMatcher.match(result); + } + } +} diff --git a/src/test/java/de/sakpaas/backend/controller/UserControllerTest.java b/src/test/java/de/sakpaas/backend/controller/UserControllerTest.java deleted file mode 100644 index 4dc4a207..00000000 --- a/src/test/java/de/sakpaas/backend/controller/UserControllerTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package de.sakpaas.backend.controller; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import de.sakpaas.backend.dto.UserInfoDto; -import de.sakpaas.backend.model.Address; -import de.sakpaas.backend.model.Favorite; -import de.sakpaas.backend.model.Location; -import de.sakpaas.backend.model.LocationDetails; -import de.sakpaas.backend.service.AddressRepository; -import de.sakpaas.backend.service.FavoriteRepository; -import de.sakpaas.backend.service.LocationDetailsRepository; -import de.sakpaas.backend.service.LocationRepository; -import de.sakpaas.backend.service.OccupancyRepository; -import de.sakpaas.backend.service.PresenceRepository; -import de.sakpaas.backend.service.UserService; -import de.sakpaas.backend.util.KeycloakConfiguration; -import de.sakpaas.backend.v2.controller.UserController; -import de.sakpaas.backend.v2.dto.LocationResultLocationDto; -import de.sakpaas.backend.v2.mapper.LocationMapper; -import io.micrometer.core.instrument.MeterRegistry; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import org.junit.jupiter.api.Test; -import org.keycloak.adapters.springboot.KeycloakSpringBootProperties; -import org.keycloak.common.util.RandomString; -import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.web.servlet.MockMvc; -import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; - -@WebMvcTest(UserController.class) -@EnableConfigurationProperties(KeycloakSpringBootProperties.class) -public class UserControllerTest { - @Autowired - private MockMvc mockMvc; - - @MockBean - private UserService userService; - @MockBean - private LocationMapper locationMapper; - @MockBean - private FavoriteRepository favoriteRepository; - @MockBean - private AddressRepository addressRepository; - @MockBean - private LocationDetailsRepository locationDetailsRepository; - @MockBean - private LocationRepository locationRepository; - @MockBean - private PresenceRepository presenceRepository; - @MockBean - private OccupancyRepository occupancyRepository; - @MockBean - private MeterRegistry meterRegistry; - @MockBean - private KeycloakConfiguration keycloakConfiguration; - - @Test - public void shouldGetLocationsForCurrentUser() throws Exception { - String header = RandomString.randomCode(32); - UUID user = UUID.randomUUID(); - - List locations = new ArrayList<>(); - List resultDtos = new ArrayList<>(); - List favorites = new ArrayList<>(); - for (int i = 0; i < 3; i++) { - Location location = location((long) i); - locations.add(location); - resultDtos.add(resultDto((long) i)); - favorites.add(new Favorite(user, location)); - } - - Mockito.when(userService.getUserInfo(header)).thenReturn( - new UserInfoDto(user.toString(), "test", "test", "test", "test", - "test@test.de")); - Mockito.when(favoriteRepository.findByUserUuid(user)).thenReturn(favorites); - Mockito.when(locationMapper.mapLocationToOutputDto(locations.get(0))) - .thenReturn(resultDtos.get(0)); - Mockito.when(locationMapper.mapLocationToOutputDto(locations.get(1))) - .thenReturn(resultDtos.get(1)); - Mockito.when(locationMapper.mapLocationToOutputDto(locations.get(2))) - .thenReturn(resultDtos.get(2)); - - String resultJson = - this.mockMvc.perform(get("/v2/users/self/favorites").header("Authorization", header)) - .andDo(print()) - .andExpect(status().isOk()) - .andReturn() - .getResponse() - .getContentAsString(); - - String compareJson = new ObjectMapper().writeValueAsString(resultDtos); - assertThat(compareJson, equalTo(resultJson)); - } - - private Location location(Long id) { - return new Location(id, "Location: " + id, 0.0D, 0.0D, new LocationDetails(), new Address()); - } - - private LocationResultLocationDto resultDto(Long id) { - return new LocationResultLocationDto(id, "Location: " + id, false, null, null, null, null); - } -} diff --git a/src/test/java/de/sakpaas/backend/v2/controller/EndpointAddFavoriteByLocationIdTest.java b/src/test/java/de/sakpaas/backend/v2/controller/EndpointAddFavoriteByLocationIdTest.java new file mode 100644 index 00000000..df78e8ce --- /dev/null +++ b/src/test/java/de/sakpaas/backend/v2/controller/EndpointAddFavoriteByLocationIdTest.java @@ -0,0 +1,180 @@ +package de.sakpaas.backend.v2.controller; + +import static org.assertj.core.api.Assertions.assertThat; +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 de.sakpaas.backend.IntegrationTest; +import de.sakpaas.backend.model.Address; +import de.sakpaas.backend.model.Favorite; +import de.sakpaas.backend.model.Location; +import de.sakpaas.backend.model.LocationDetails; +import de.sakpaas.backend.model.Occupancy; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Tests the endpoint POST /v2/users/self/favorites/{id} if it conforms to + * the openAPI specification. + */ +@SpringBootTest +@RunWith(SpringRunner.class) +@AutoConfigureMockMvc +class EndpointAddFavoriteByLocationIdTest extends IntegrationTest { + + @Test + void testMalformed() throws Exception { + // Test all authentication possibilities + mockMvc.perform(post("/v2/users/self/favorites/xxxx") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(post("/v2/users/self/favorites/xxxx") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(post("/v2/users/self/favorites/xxxx")) + // No Authentication + .andExpect(status().isBadRequest()); + + // Test database change + List testFavorites = favoriteRepository.findByUserUuid(USER_UUID); + assertThat(testFavorites.isEmpty()).isTrue(); + } + + @Test + void testNotFound() throws Exception { + mockMvc.perform(post("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(post("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isNotFound()); + + mockMvc.perform(post("/v2/users/self/favorites/1000")) + // No Authentication + .andExpect(status().is4xxClientError()); + // Authentication should be handled by Keycloak, but only the controller is being tested, + // thus the controller requests the Principal can not be added and a 400 Error is thrown. + //.andExpect(status().isUnauthorized()); + + // Test database change + List testFavorites = favoriteRepository.findByUserUuid(USER_UUID); + assertThat(testFavorites.isEmpty()).isTrue(); + } + + @Test + void testFound() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + super.insert(location); + + // Test all authentication possibilities + mockMvc.perform(post("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(post("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").value(true)); + + mockMvc.perform(post("/v2/users/self/favorites/1000")) + // No Authentication + .andExpect(status().is4xxClientError()); + // Authentication should be handled by Keycloak, but only the controller is being tested, + // thus the controller requests the Principal can not be added and a 400 Error is thrown. + //.andExpect(status().isUnauthorized()); + + // Test database change + List testFavorites = favoriteRepository.findByUserUuid(USER_UUID); + assertThat(testFavorites.size()).isEqualTo(1); + assertThat(testFavorites.get(0).getLocation()).isEqualTo(location); + assertThat(testFavorites.get(0).getUserUuid()).isEqualTo(USER_UUID); + } + + @Test + void testAlreadyFavorite() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + Favorite favorite = new Favorite(USER_UUID, location); + super.insert(location); + super.insert(favorite); + + // Test all authentication possibilities + mockMvc.perform(post("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(post("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").value(true)); + + mockMvc.perform(post("/v2/users/self/favorites/1000")) + // No Authentication + .andExpect(status().is4xxClientError()); + // Authentication should be handled by Keycloak, but only the controller is being tested, + // thus the controller requests the Principal can not be added and a 400 Error is thrown. + //.andExpect(status().isUnauthorized()); + + // Test database change + List testFavorites = favoriteRepository.findByUserUuid(USER_UUID); + assertThat(testFavorites.size()).isEqualTo(1); + assertThat(testFavorites.get(0).getLocation()).isEqualTo(location); + assertThat(testFavorites.get(0).getUserUuid()).isEqualTo(USER_UUID); + } + + @Test + void testOccupancy() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + Occupancy occupancy = new Occupancy(location, 0.5, "TEST"); + super.insert(location); + super.insert(occupancy); + + // Test all authentication possibilities + mockMvc.perform(post("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(post("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").value(true)) + .andExpect(jsonPath("$.occupancy.value").value(occupancy.getOccupancy())) + .andExpect(jsonPath("$.occupancy.count").value(1)) + .andExpect(jsonPath("$.occupancy.latestReport").isString()); + + mockMvc.perform(post("/v2/users/self/favorites/1000")) + // No Authentication + .andExpect(status().is4xxClientError()); + // Authentication should be handled by Keycloak, but only the controller is being tested, + // thus the controller requests the Principal can not be added and a 400 Error is thrown. + //.andExpect(status().isUnauthorized()); + + // Test database change + List testFavorites = favoriteRepository.findByUserUuid(USER_UUID); + assertThat(testFavorites.size()).isEqualTo(1); + assertThat(testFavorites.get(0).getLocation()).isEqualTo(location); + assertThat(testFavorites.get(0).getUserUuid()).isEqualTo(USER_UUID); + } +} \ No newline at end of file diff --git a/src/test/java/de/sakpaas/backend/v2/controller/EndpointGetLocationByIdTest.java b/src/test/java/de/sakpaas/backend/v2/controller/EndpointGetLocationByIdTest.java new file mode 100644 index 00000000..f0be51f7 --- /dev/null +++ b/src/test/java/de/sakpaas/backend/v2/controller/EndpointGetLocationByIdTest.java @@ -0,0 +1,161 @@ +package de.sakpaas.backend.v2.controller; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import de.sakpaas.backend.IntegrationTest; +import de.sakpaas.backend.model.Address; +import de.sakpaas.backend.model.Favorite; +import de.sakpaas.backend.model.Location; +import de.sakpaas.backend.model.LocationDetails; +import de.sakpaas.backend.model.Occupancy; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Tests the endpoint /v2/locations/{id} if it conforms to the openAPI specification. + */ +@SpringBootTest +@RunWith(SpringRunner.class) +@AutoConfigureMockMvc +class EndpointGetLocationByIdTest extends IntegrationTest { + + @Test + void testMalformed() throws Exception { + // Test all authentication possibilities + mockMvc.perform(get("/v2/locations/xxxx") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/v2/locations/xxxx") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/v2/locations/xxxx")) + // No Authentication + .andExpect(status().isBadRequest()); + } + + @Test + void testNotFound() throws Exception { + // Test all authentication possibilities + mockMvc.perform(get("/v2/locations/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(get("/v2/locations/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isNotFound()); + + mockMvc.perform(get("/v2/locations/1000")) + // No Authentication + .andExpect(status().isNotFound()); + } + + @Test + void testFound() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + super.insert(location); + + // Test all authentication possibilities + mockMvc.perform(get("/v2/locations/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(get("/v2/locations/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").value(false)) + .andExpect(jsonPath("$.occupancy.value").doesNotExist()) + .andExpect(jsonPath("$.occupancy.count").value(0)) + .andExpect(jsonPath("$.occupancy.latestReport").doesNotExist()); + + mockMvc.perform(get("/v2/locations/1000")) + // No Authentication + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").doesNotExist()) + .andExpect(jsonPath("$.occupancy.value").doesNotExist()) + .andExpect(jsonPath("$.occupancy.count").value(0)) + .andExpect(jsonPath("$.occupancy.latestReport").doesNotExist()); + } + + @Test + void testFoundFavorite() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + Favorite favorite = new Favorite(USER_UUID, location); + super.insert(location); + super.insert(favorite); + + // Test all authentication possibilities + mockMvc.perform(get("/v2/locations/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(get("/v2/locations/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").value(true)) + .andExpect(jsonPath("$.occupancy.value").doesNotExist()) + .andExpect(jsonPath("$.occupancy.count").value(0)) + .andExpect(jsonPath("$.occupancy.latestReport").doesNotExist()); + + mockMvc.perform(get("/v2/locations/1000")) + // No Authentication + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").doesNotExist()) + .andExpect(jsonPath("$.occupancy.value").doesNotExist()) + .andExpect(jsonPath("$.occupancy.count").value(0)) + .andExpect(jsonPath("$.occupancy.latestReport").doesNotExist()); + } + + @Test + void testFoundOccupancy() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + Occupancy occupancy = new Occupancy(location, 0.5, "TEST"); + super.insert(location); + super.insert(occupancy); + + // Test all authentication possibilities + mockMvc.perform(get("/v2/locations/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(get("/v2/locations/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").value(false)) + .andExpect(jsonPath("$.occupancy.value").value(occupancy.getOccupancy())) + .andExpect(jsonPath("$.occupancy.count").value(1)) + .andExpect(jsonPath("$.occupancy.latestReport").isString()); + + mockMvc.perform(get("/v2/locations/1000")) + // No Authentication + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").doesNotExist()) + .andExpect(jsonPath("$.occupancy.value").value(occupancy.getOccupancy())) + .andExpect(jsonPath("$.occupancy.count").value(1)) + .andExpect(jsonPath("$.occupancy.latestReport").isString()); + } +} \ No newline at end of file diff --git a/src/test/java/de/sakpaas/backend/v2/controller/EndpointGetLocationsByCoordinatesTest.java b/src/test/java/de/sakpaas/backend/v2/controller/EndpointGetLocationsByCoordinatesTest.java new file mode 100644 index 00000000..0eed104e --- /dev/null +++ b/src/test/java/de/sakpaas/backend/v2/controller/EndpointGetLocationsByCoordinatesTest.java @@ -0,0 +1,228 @@ +package de.sakpaas.backend.v2.controller; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.nullValue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import de.sakpaas.backend.IntegrationTest; +import de.sakpaas.backend.model.Address; +import de.sakpaas.backend.model.Favorite; +import de.sakpaas.backend.model.Location; +import de.sakpaas.backend.model.LocationDetails; +import de.sakpaas.backend.model.Occupancy; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Tests the endpoint /v2/locations if it conforms to the openAPI specification. + */ +@SpringBootTest +@RunWith(SpringRunner.class) +@AutoConfigureMockMvc +class EndpointGetLocationsByCoordinatesTest extends IntegrationTest { + + @Test + void testMalformed() throws Exception { + // Test all authentication possibilities + // No coordinates given + mockMvc.perform(get("/v2/locations") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/v2/locations") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/v2/locations")) + // No Authentication + .andExpect(status().isBadRequest()); + + // Incomplete coordinates given + mockMvc.perform(get("/v2/locations?latitude=42.0") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/v2/locations?latitude=42.0") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/v2/locations?latitude=42.0")) + // No Authentication + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/v2/locations?longitude=42.0") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/v2/locations?longitude=42.0") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/v2/locations?longitude=42.0")) + // No Authentication + .andExpect(status().isBadRequest()); + + // Wrong type + mockMvc.perform(get("/v2/locations?latitude=XXX&longitude=42.0") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/v2/locations?latitude=XXX&longitude=42.0") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/v2/locations?latitude=XXX&longitude=42.0")) + // No Authentication + .andExpect(status().isBadRequest()); + } + + @Test + void testEmpty() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + super.insert(location); + + // Test all authentication possibilities + mockMvc.perform(get("/v2/locations?latitude=0.0&longitude=0.0") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(get("/v2/locations?latitude=0.0&longitude=0.0") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(0))); + + mockMvc.perform(get("/v2/locations?latitude=0.0&longitude=0.0")) + // No Authentication + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(0))); + } + + @Test + void testFound() throws Exception { + // Setup test data + Location locationEdeka = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + Location locationLidl = new Location(3000L, "Lidl", 0.0, 0.0, + new LocationDetails("beverages", "Mo-So 01-23", "Lidl"), + new Address("ES", "Madrid", "5432", "Street", "1111") + ); + super.insert(locationEdeka); + super.insert(locationLidl); + List locations = Arrays.asList( + locationEdeka, + locationLidl + ); + + // Test all authentication possibilities + mockMvc.perform(get("/v2/locations?latitude=42.0&longitude=7.0") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(get("/v2/locations?latitude=42.0&longitude=7.0") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocationList(locations)) + .andExpect(jsonPath("$", hasSize(1))); + + mockMvc.perform(get("/v2/locations?latitude=42.0&longitude=7.0")) + // No Authentication + .andExpect(status().isOk()) + .andExpect(super.expectLocationList(locations)) + .andExpect(jsonPath("$", hasSize(1))); + } + + @Test + void testFavorite() throws Exception { + // Setup test data + Location locationEdeka = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + Location locationAldi = new Location(2000L, "Aldi", 42.001, 7.001, + new LocationDetails("kiosk", "Fr-Sa 12-14", "Aldi"), + new Address("FR", "Paris", "101010", "Louvre", "1") + ); + List locations = Arrays.asList( + locationEdeka, + locationAldi + ); + super.insert(locationEdeka); + super.insert(locationAldi); + Favorite favoriteEdeka = new Favorite(USER_UUID, locationEdeka); + super.insert(favoriteEdeka); + + // Test all authentication possibilities + mockMvc.perform(get("/v2/locations?latitude=42.0&longitude=7.0") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(get("/v2/locations?latitude=42.0&longitude=7.0") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocationList(locations)) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$.[?(@.id==1000)].favorite").value(true)) + .andExpect(jsonPath("$.[?(@.id==2000)].favorite").value(false)); + + mockMvc.perform(get("/v2/locations?latitude=42.0&longitude=7.0")) + // No Authentication + .andExpect(status().isOk()) + .andExpect(super.expectLocationList(locations)) + .andExpect(jsonPath("$", hasSize(2))) + // The result is a list and will not automatically unpacked with nullValue() + .andExpect(jsonPath("$.[?(@.id==1000)].favorite").value(contains(nullValue()))) + .andExpect(jsonPath("$.[?(@.id==2000)].favorite").value(contains(nullValue()))); + } + + @Test + void testOccupancy() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + Occupancy occupancy = new Occupancy(location, 0.5, "TEST"); + super.insert(location); + super.insert(occupancy); + + // Test all authentication possibilities + mockMvc.perform( + get("/v2/locations/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform( + get("/v2/locations/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").value(false)) + .andExpect(jsonPath("$.occupancy.value").value(occupancy.getOccupancy())) + .andExpect(jsonPath("$.occupancy.count").value(1)) + .andExpect(jsonPath("$.occupancy.latestReport").isString()); + + mockMvc.perform( + get("/v2/locations/1000")) + // No Authentication + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").doesNotExist()) + .andExpect(jsonPath("$.occupancy.value").value(occupancy.getOccupancy())) + .andExpect(jsonPath("$.occupancy.count").value(1)) + .andExpect(jsonPath("$.occupancy.latestReport").isString()); + } +} \ No newline at end of file diff --git a/src/test/java/de/sakpaas/backend/v2/controller/EndpointListFavoritesTest.java b/src/test/java/de/sakpaas/backend/v2/controller/EndpointListFavoritesTest.java new file mode 100644 index 00000000..38c23e72 --- /dev/null +++ b/src/test/java/de/sakpaas/backend/v2/controller/EndpointListFavoritesTest.java @@ -0,0 +1,98 @@ +package de.sakpaas.backend.v2.controller; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.everyItem; +import static org.hamcrest.Matchers.hasSize; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import de.sakpaas.backend.IntegrationTest; +import de.sakpaas.backend.model.Address; +import de.sakpaas.backend.model.Favorite; +import de.sakpaas.backend.model.Location; +import de.sakpaas.backend.model.LocationDetails; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Tests the endpoint /v2/users/self/favorites if it conforms to the openAPI + * specification. + */ +@SpringBootTest +@RunWith(SpringRunner.class) +@AutoConfigureMockMvc +class EndpointListFavoritesTest extends IntegrationTest { + + @Test + void testEmpty() throws Exception { + // Test all authentication possibilities + mockMvc.perform(get("/v2/users/self/favorites") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(get("/v2/users/self/favorites") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$").isEmpty()); + + mockMvc.perform(get("/v2/users/self/favorites")) + // No Authentication + .andExpect(status().is4xxClientError()); + // Authentication should be handled by Keycloak, but only the controller is being tested, + // thus the controller requests the Principal can not be added and a 400 Error is thrown. + //.andExpect(status().isUnauthorized()); + } + + @Test + void testFound() throws Exception { + // Setup test data + Location locationEdeka = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + Location locationAldi = new Location(2000L, "Aldi", 42.001, 7.001, + new LocationDetails("kiosk", "Fr-Sa 12-14", "Aldi"), + new Address("FR", "Paris", "101010", "Louvre", "1") + ); + Location locationPenny = new Location(3000L, "Penny", 0.0, 0.0, + new LocationDetails("beverages", "Sa-So 02-03", "Penny"), + new Address("CH", "Zurich", "567", "Am Berg", "5") + ); + super.insert(locationEdeka); + super.insert(locationAldi); + super.insert(locationPenny); + List locations = Arrays.asList( + locationEdeka, + locationAldi, + locationPenny + ); + super.insert(new Favorite(USER_UUID, locationEdeka)); + super.insert(new Favorite(USER_UUID, locationPenny)); + + // Test all authentication possibilities + mockMvc.perform(get("/v2/users/self/favorites") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(get("/v2/users/self/favorites") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocationList(locations)) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[*].favorite").value(everyItem(equalTo(true)))); + + mockMvc.perform(get("/v2/users/self/favorites")) + // No Authentication + .andExpect(status().is4xxClientError()); + // Authentication should be handled by Keycloak, but only the controller is being tested, + // thus the controller requests the Principal can not be added and a 400 Error is thrown. + //.andExpect(status().isUnauthorized()); + } +} \ No newline at end of file diff --git a/src/test/java/de/sakpaas/backend/v2/controller/EndpointRemoveFavoriteByLocationIdTest.java b/src/test/java/de/sakpaas/backend/v2/controller/EndpointRemoveFavoriteByLocationIdTest.java new file mode 100644 index 00000000..bb94f43d --- /dev/null +++ b/src/test/java/de/sakpaas/backend/v2/controller/EndpointRemoveFavoriteByLocationIdTest.java @@ -0,0 +1,224 @@ +package de.sakpaas.backend.v2.controller; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import de.sakpaas.backend.IntegrationTest; +import de.sakpaas.backend.model.Address; +import de.sakpaas.backend.model.Favorite; +import de.sakpaas.backend.model.Location; +import de.sakpaas.backend.model.LocationDetails; +import de.sakpaas.backend.model.Occupancy; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Tests the endpoint DELETE /v2/users/self/favorites/{id} if it conforms to the + * openAPI specification. + */ +@SpringBootTest +@RunWith(SpringRunner.class) +@AutoConfigureMockMvc +class EndpointRemoveFavoriteByLocationIdTest extends IntegrationTest { + + @Test + void testMalformed() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + Favorite favorite = new Favorite(USER_UUID, location); + super.insert(location); + super.insert(favorite); + + // Test all authentication possibilities + mockMvc.perform(delete("/v2/users/self/favorites/xxxx") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(delete("/v2/users/self/favorites/xxxx") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isBadRequest()); + + mockMvc.perform(delete("/v2/users/self/favorites/xxxx")) + // No Authentication + .andExpect(status().isBadRequest()); + + // Test database change + List testFavorites = favoriteRepository.findByUserUuid(USER_UUID); + assertThat(testFavorites.size()).isEqualTo(1); + assertThat(testFavorites.get(0).getLocation()).isEqualTo(location); + assertThat(testFavorites.get(0).getUserUuid()).isEqualTo(USER_UUID); + } + + @Test + void testLocationNotFound() throws Exception { + // Test all authentication possibilities + mockMvc.perform(delete("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(delete("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isNotFound()); + + mockMvc.perform(delete("/v2/users/self/favorites/1000")) + // No Authentication + .andExpect(status().is4xxClientError()); + // Authentication should be handled by Keycloak, but only the controller is being tested, + // thus the controller requests the Principal can not be added and a 400 Error is thrown. + //.andExpect(status().isUnauthorized()); + + // Test database change + List testFavorites = favoriteRepository.findByUserUuid(USER_UUID); + assertThat(testFavorites.isEmpty()).isTrue(); + } + + @Test + void testFavoriteNotFound() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + super.insert(location); + + // Test all authentication possibilities + mockMvc.perform(delete("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(delete("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").value(false)); + + mockMvc.perform(delete("/v2/users/self/favorites/1000")) + // No Authentication + .andExpect(status().is4xxClientError()); + // Authentication should be handled by Keycloak, but only the controller is being tested, + // thus the controller requests the Principal can not be added and a 400 Error is thrown. + //.andExpect(status().isUnauthorized()); + + // Test database change + List testFavorites = favoriteRepository.findByUserUuid(USER_UUID); + assertThat(testFavorites.isEmpty()).isTrue(); + } + + @Test + void testFavoriteFound() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + Favorite favorite = new Favorite(USER_UUID, location); + super.insert(location); + super.insert(favorite); + + // Test all authentication possibilities + mockMvc.perform(delete("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(delete("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").value(false)); + + mockMvc.perform(delete("/v2/users/self/favorites/1000")) + // No Authentication + .andExpect(status().is4xxClientError()); + // Authentication should be handled by Keycloak, but only the controller is being tested, + // thus the controller requests the Principal can not be added and a 400 Error is thrown. + //.andExpect(status().isUnauthorized()); + + // Test database change + List testFavorites = favoriteRepository.findByUserUuid(USER_UUID); + assertThat(testFavorites.isEmpty()).isTrue(); + } + + @Test + void testMultipleFavoritesFound() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + Favorite favorite = new Favorite(USER_UUID, location); + Favorite favoriteDuplicate = new Favorite(USER_UUID, location); + super.insert(location); + super.insert(favorite); + super.insert(favoriteDuplicate); + + // Test all authentication possibilities + mockMvc.perform(delete("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(delete("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").value(false)); + + mockMvc.perform(delete("/v2/users/self/favorites/1000")) + // No Authentication + .andExpect(status().is4xxClientError()); + // Authentication should be handled by Keycloak, but only the controller is being tested, + // thus the controller requests the Principal can not be added and a 400 Error is thrown. + //.andExpect(status().isUnauthorized()); + + // Test database change + List testFavorites = favoriteRepository.findByUserUuid(USER_UUID); + assertThat(testFavorites.isEmpty()).isTrue(); + } + + @Test + void testOccupancy() throws Exception { + // Setup test data + Location location = new Location(1000L, "Edeka Eima", 42.0, 7.0, + new LocationDetails("supermarket", "Mo-Fr 10-22", "Edeka"), + new Address("DE", "Mannheim", "25565", "Handelshafen", "12a") + ); + Occupancy occupancy = new Occupancy(location, 0.5, "TEST"); + Favorite favorite = new Favorite(USER_UUID, location); + super.insert(location); + super.insert(occupancy); + super.insert(favorite); + + // Test all authentication possibilities + mockMvc.perform(delete("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_INVALID)) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(delete("/v2/users/self/favorites/1000") + .header("Authorization", AUTHENTICATION_VALID)) + .andExpect(status().isOk()) + .andExpect(super.expectLocation(location)) + .andExpect(jsonPath("$.favorite").value(false)) + .andExpect(jsonPath("$.occupancy.value").value(occupancy.getOccupancy())) + .andExpect(jsonPath("$.occupancy.count").value(1)) + .andExpect(jsonPath("$.occupancy.latestReport").isString()); + + mockMvc.perform(delete("/v2/users/self/favorites/1000")) + // No Authentication + .andExpect(status().is4xxClientError()); + // Authentication should be handled by Keycloak, but only the controller is being tested, + // thus the controller requests the Principal can not be added and a 400 Error is thrown. + //.andExpect(status().isUnauthorized()); + + // Test database change + List testFavorites = favoriteRepository.findByUserUuid(USER_UUID); + assertThat(testFavorites.isEmpty()).isTrue(); + } +} \ No newline at end of file