diff --git a/backend/src/main/java/com/vaessl/app/connection/Endpoint.java b/backend/src/main/java/com/vaessl/app/connection/Endpoint.java index f52535a..619e914 100644 --- a/backend/src/main/java/com/vaessl/app/connection/Endpoint.java +++ b/backend/src/main/java/com/vaessl/app/connection/Endpoint.java @@ -2,7 +2,7 @@ package com.vaessl.app.connection; public enum Endpoint { HOMEBOX_LOGIN("/api/v1/users/login"), LOGIN("/login"), CONNECTION_STATUS( - "/connections/status"), HOMEBOX_QUERY_ALL_ITEMS("/api/v1/items"); + "/connections/status"), HOMEBOX_QUERY_ALL_ITEMS("/api/v1/items"), SEARCH("/search"); private final String value; diff --git a/backend/src/test/java/com/vaessl/app/search/SearchControllerTest.java b/backend/src/test/java/com/vaessl/app/search/SearchControllerTest.java new file mode 100644 index 0000000..a04f7b5 --- /dev/null +++ b/backend/src/test/java/com/vaessl/app/search/SearchControllerTest.java @@ -0,0 +1,154 @@ +package com.vaessl.app.search; + +import static com.vaessl.app.connection.Endpoint.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; +import jakarta.servlet.http.Cookie; + +@SpringBootTest +@AutoConfigureMockMvc +@WireMockTest +class SearchControllerTest { + + @Autowired + MockMvc mockMvc; + + private static final String TEST_USER = "admin"; + + private static final String TEST_PASS = "pw"; + + private static final String QUERY_ALL_ITEMS = HOMEBOX_QUERY_ALL_ITEMS.getValue(); + + private static final String LOGIN_PATH = LOGIN.getValue(); + + private static final String SEARCH_REQUEST = SEARCH.getValue(); + + private static final String VALID_HOMEBOX_LOGIN_RESPONSE = """ + { + "token": "fake-bearer-token", + "attachmentToken": "fake-attach-token", + "expiresAt": "2099-01-01T00:00:00Z" + } + """; + + private static final String VALID_HOMEBOX_ALL_ITEMS_QUERY_RESPONSE = """ + { + "page": -1, + "pageSize": -1, + "total": 1, + "items": [ + { + "id": "c643e7f9-93d0-4b5f-ae4d-e1c2d90389e0", + "assetId": "000-001", + "name": "MacBook Pro A1398", + "description": "Running Linux (Fedora)", + "quantity": 1, + "insured": false, + "archived": false, + "createdAt": "2026-05-13T19:52:20.016176Z", + "updatedAt": "2026-05-14T12:39:11.836403Z", + "purchasePrice": 0, + "location": { + "id": "b6f60ab8-3a2a-4a8d-a4bf-897d0555f636", + "name": "Server Schrank Ikea weiß", + "description": "Weißer Ikea Schrank, wo sich der Server befindet.", + "createdAt": "2026-05-13T19:55:55.817576Z", + "updatedAt": "2026-05-14T12:37:24.396651Z" + }, + "tags": [], + "imageId": "cb3e44d5-ccd4-421e-9f5a-f52cd5f40ca6", + "thumbnailId": "2bfd53fa-1bf1-483c-8d76-7720464532fa", + "soldTime": "0001-01-01T00:00:00Z" + } + ] + } + """; + + @Test + void shouldReturnListOfQueriedHomeboxItems(WireMockRuntimeInfo wm) throws Exception { + + WireMock.stubFor(WireMock.post(HOMEBOX_LOGIN.getValue()) + .willReturn(WireMock.okJson(VALID_HOMEBOX_LOGIN_RESPONSE))); + + WireMock.stubFor(WireMock.get(WireMock.urlPathEqualTo(QUERY_ALL_ITEMS)) + .willReturn(WireMock.okJson(VALID_HOMEBOX_ALL_ITEMS_QUERY_RESPONSE))); + + MvcResult loginResult = + mockMvc.perform(post(LOGIN_PATH).contentType(MediaType.APPLICATION_JSON) + .content(connectionRequestBody(wm))).andExpect(status().isOk()).andReturn(); + + Cookie sessionCookie = loginResult.getResponse().getCookie("SESSION"); + + mockMvc.perform(post(SEARCH_REQUEST).cookie(sessionCookie) + .contentType(MediaType.APPLICATION_JSON).content(searchRequestBody(wm, "HOMEBOX"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.content[0].title").value("MacBook Pro A1398")) + .andExpect(jsonPath("$.totalElements").value(1)) + .andExpect(jsonPath("$.content[0].extraData.location.name") + .value("Server Schrank Ikea weiß")); + } + + @Test + void shouldReturnUnauthorizedWhenNoSession() throws Exception { + mockMvc.perform(post(SEARCH_REQUEST).contentType(MediaType.APPLICATION_JSON).content(""" + { + "appUrl": "http://irrelevant", + "query": "Item", + "serviceType": "HOMEBOX", + "username": "irrelevant" + } + """)).andExpect(status().isUnauthorized()); + } + + @Test + void shouldReturnUnauthorizedWhenSessionHasNoConnectionForRequestedServiceType( + WireMockRuntimeInfo wm) throws Exception { + + WireMock.stubFor(WireMock.post(HOMEBOX_LOGIN.getValue()) + .willReturn(WireMock.okJson(VALID_HOMEBOX_LOGIN_RESPONSE))); + + MvcResult loginResult = + mockMvc.perform(post(LOGIN_PATH).contentType(MediaType.APPLICATION_JSON) + .content(connectionRequestBody(wm))).andExpect(status().isOk()).andReturn(); + + Cookie sessionCookie = loginResult.getResponse().getCookie("SESSION"); + + mockMvc.perform( + post(SEARCH_REQUEST).cookie(sessionCookie).contentType(MediaType.APPLICATION_JSON) + .content(searchRequestBody(wm, "OTHER_SERVICETYPE"))) + .andExpect(status().isUnauthorized()); + } + + private String searchRequestBody(WireMockRuntimeInfo wm, String serviceType) { + return """ + { + "appUrl": "%s", + "query": "Item", + "serviceType": "%s", + "username": "%s" + } + """.formatted(wm.getHttpBaseUrl(), serviceType, TEST_USER); + } + + private String connectionRequestBody(WireMockRuntimeInfo wm) { + return """ + { + "appUrl": "%s", + "serviceType": "HOMEBOX", + "username": "%s", + "password": "%s" + } + """.formatted(wm.getHttpBaseUrl(), TEST_USER, TEST_PASS); + } +}