From ae01c874a55b72b28673267472c43e257765ef83 Mon Sep 17 00:00:00 2001 From: almeidast Date: Mon, 30 Sep 2024 20:04:12 +0100 Subject: [PATCH 01/37] create a method to search routes and listeners by parameter "q" --- .../swisspush/gateleen/hook/HookHandler.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index 66b1a59e..509d8038 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -569,6 +569,14 @@ public boolean handle(final RoutingContext ctx) { } } + HttpServerResponse response = ctx.response(); + String queryParam = request.getParam("q"); + + if ((queryParam != null) && !queryParam.isEmpty()) { + this.handleHookSearch(queryParam,response); + return true; + } + /* * 2) Check if we have to queue a request for listeners */ @@ -592,6 +600,39 @@ public boolean handle(final RoutingContext ctx) { } } + /** + * Handles hook search requests based on the 'destination' property. + * Searches in both routes and listeners. + * + * @param queryParam the RoutingContext of the request + */ + public void handleHookSearch(String queryParam,HttpServerResponse response) { + JsonObject result = new JsonObject(); + JsonArray matchingRoutes = new JsonArray(); + JsonArray matchingListeners = new JsonArray(); + + // Search routes by destination + routeRepository.getRoutes().forEach((routeKey, route) -> { + if (route.getHook().getDestination().contains(queryParam)) { + matchingRoutes.add(routeKey); + } + }); + + // Search listeners by destination + listenerRepository.getListeners().forEach(listener -> { + if (listener.getHook().getDestination().contains(queryParam)) { + matchingListeners.add(listener.getListenerId()); + } + }); + + // Build and send the response + result.put("routes", matchingRoutes); + result.put("listeners", matchingListeners); + + response.putHeader("content-type", "application/json").end(result.encode()); + } + + /** * Create a listing of routes in the given parent. This happens * only if we have a GET request, the routes are listable and From 9e39c832b80c4aad44dc309e7f298ff1b04516b7 Mon Sep 17 00:00:00 2001 From: almeidast Date: Thu, 3 Oct 2024 08:52:33 +0100 Subject: [PATCH 02/37] Fix null pointer in HookHandler Add the tests to cover new implementations lto cover HookHandlerSearch --- .../swisspush/gateleen/hook/HookHandler.java | 10 +- .../gateleen/hook/HookHandlerTest.java | 141 ++++++++++++++++++ 2 files changed, 148 insertions(+), 3 deletions(-) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index 509d8038..3c67e360 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -570,10 +570,14 @@ public boolean handle(final RoutingContext ctx) { } HttpServerResponse response = ctx.response(); - String queryParam = request.getParam("q"); + String queryParam = null; - if ((queryParam != null) && !queryParam.isEmpty()) { - this.handleHookSearch(queryParam,response); + if (request.params() != null) { + queryParam = request.getParam("q"); + } + + if (queryParam != null && !queryParam.isEmpty()) { + this.handleHookSearch(queryParam, response); return true; } diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index 0dbef201..17ac1f21 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -15,6 +15,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,11 +32,13 @@ import org.swisspush.gateleen.queue.queuing.RequestQueue; import org.swisspush.gateleen.routing.Router; +import java.lang.reflect.Field; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; +import java.util.Map; import static io.vertx.core.http.HttpMethod.PUT; import static org.junit.Assert.assertEquals; @@ -641,6 +644,144 @@ public void hookRegistration_RouteWithRouteMultiplier(TestContext testContext) t vertx.eventBus().request("gateleen.hook-route-remove", "pathToRouterResource"); } + @Test + public void testHookHandleSearchWithMatchingRoutesAndListeners(TestContext context) throws Exception { + // Mock the response + HttpServerResponse response = Mockito.mock(HttpServerResponse.class); + Mockito.when(response.putHeader(anyString(), anyString())).thenReturn(response); + + // Mock the request and set the query parameter + HttpServerRequest request = Mockito.mock(HttpServerRequest.class); + Mockito.when(request.response()).thenReturn(response); + Mockito.when(request.getParam("q")).thenReturn("destination"); + + // Mock the route and listener + Route mockRoute = Mockito.mock(Route.class); + HttpHook mockHook = new HttpHook("destination/matching"); + Mockito.when(mockRoute.getHook()).thenReturn(mockHook); + + Listener mockListener = new Listener("listener1", "monitoredUrl", "destination/matching", mockHook); + + // Mock repositories + ListenerRepository listenerRepository = Mockito.mock(ListenerRepository.class); + RouteRepository routeRepository = Mockito.mock(RouteRepository.class); + + // Configure mocked behavior for repositories + Mockito.when(routeRepository.getRoutes()).thenReturn(Map.of("route1", mockRoute)); + Mockito.when(listenerRepository.getListeners()).thenReturn(Collections.singletonList(mockListener)); + + // Create HookHandler instance + HookHandler hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, + logAppenderRepository, monitoringHandler, "userProfilePath", HOOK_ROOT_URI, requestQueue, + false + ); + + // Use reflection to set private fields + setPrivateField(hookHandler, "listenerRepository", listenerRepository); + setPrivateField(hookHandler, "routeRepository", routeRepository); + + // Call the method under test + hookHandler.handleHookSearch("destination", response); + + // Capture the output and verify the response was sent correctly + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + Mockito.verify(response).end(captor.capture()); + + // Check the result + String capturedResult = captor.getValue(); + JsonObject result = new JsonObject(capturedResult); + JsonArray routes = result.getJsonArray("routes"); + JsonArray listeners = result.getJsonArray("listeners"); + + // Assert the expected results + context.assertTrue(routes.contains("route1")); + context.assertTrue(listeners.contains("listener1")); + + // Verify the content-type header was set correctly + Mockito.verify(response).putHeader("content-type", "application/json"); + } + + + private void setPrivateField(Object target, String fieldName, Object value) throws Exception { + Field field = target.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.set(target, value); + } + + @Test + public void testHookHandleSearchWithNoMatchingRoutesOrListeners(TestContext context) throws Exception { + // Mock the response + HttpServerResponse response = Mockito.mock(HttpServerResponse.class); + Mockito.when(response.putHeader(anyString(), anyString())).thenReturn(response); + + // Mock the request and set the query parameter + HttpServerRequest request = Mockito.mock(HttpServerRequest.class); + Mockito.when(request.response()).thenReturn(response); + Mockito.when(request.getParam("q")).thenReturn("destination"); + + // Mock the route and listener + Route mockRoute = Mockito.mock(Route.class); + HttpHook mockHook = new HttpHook("destination/matching"); + Mockito.when(mockRoute.getHook()).thenReturn(mockHook); + + Listener mockListener = new Listener("listener1", "monitoredUrl", "destination/matching", mockHook); + + // Mock repositories + ListenerRepository listenerRepository = Mockito.mock(ListenerRepository.class); + RouteRepository routeRepository = Mockito.mock(RouteRepository.class); + + // Configure mocked behavior for repositories with no matching routes or listeners + Mockito.when(routeRepository.getRoutes()).thenReturn(Map.of()); + Mockito.when(listenerRepository.getListeners()).thenReturn(Collections.emptyList()); + + // Create HookHandler instance + HookHandler hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, + logAppenderRepository, monitoringHandler, "userProfilePath", HOOK_ROOT_URI, requestQueue, + false + ); + + // Use reflection to set private fields + setPrivateField(hookHandler, "listenerRepository", listenerRepository); + setPrivateField(hookHandler, "routeRepository", routeRepository); + + // Call the method under test + hookHandler.handleHookSearch("destination", response); + + // Capture the output and verify the response was sent correctly + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + Mockito.verify(response).end(captor.capture()); + + // Check the result + String capturedResult = captor.getValue(); + JsonObject result = new JsonObject(capturedResult); + JsonArray routes = result.getJsonArray("routes"); + JsonArray listeners = result.getJsonArray("listeners"); + + // Assert that there are no matching routes or listeners + context.assertTrue(routes.isEmpty()); + context.assertTrue(listeners.isEmpty()); + + // Verify the content-type header was set correctly + Mockito.verify(response).putHeader("content-type", "application/json"); + } + + @Test + public void testHookHandleSearchWithInvalidQueryParam(TestContext context) { + // Mocking the HttpServerResponse and request objects + HttpServerResponse response = Mockito.mock(HttpServerResponse.class); + Mockito.when(response.putHeader(anyString(), anyString())).thenReturn(response); + + HttpServerRequest request = Mockito.mock(HttpServerRequest.class); + Mockito.when(request.response()).thenReturn(response); + Mockito.when(request.getParam("q")).thenReturn(null); + + // Call hookHandleSearch + hookHandler.handleHookSearch(null, response); + + // Verify that nothing is returned + Mockito.verify(response, Mockito.never()).end(any(Buffer.class)); + } + /////////////////////////////////////////////////////////////////////////////// // Helpers From d9f1c769bd9559f8d46a29be0a4f9141dbccf4e8 Mon Sep 17 00:00:00 2001 From: almeidast Date: Wed, 9 Oct 2024 21:44:09 +0100 Subject: [PATCH 03/37] Fix tests and implement more validations. Add new tests for HookHandler Create testes with storage --- .../swisspush/gateleen/hook/HookHandler.java | 91 +++--- .../gateleen/hook/HookHandlerTest.java | 268 +++++++++--------- 2 files changed, 184 insertions(+), 175 deletions(-) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index 3c67e360..d54a3431 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -70,6 +70,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -123,6 +124,13 @@ public class HookHandler implements LoggableResource { public static final String LISTABLE = "listable"; public static final String COLLECTION = "collection"; + private static final String CONTENT_TYPE_JSON = "application/json"; + private static final String LISTENERS_KEY = "listeners"; + private static final String ROUTES_KEY = "routes"; + private static final String DESTINATION_KEY = "destination"; + private static final String CONTENT_TYPE_HEADER = "content-type"; + + private final Comparator collectionContentComparator; private static final Logger log = LoggerFactory.getLogger(HookHandler.class); @@ -569,16 +577,24 @@ public boolean handle(final RoutingContext ctx) { } } - HttpServerResponse response = ctx.response(); - String queryParam = null; - - if (request.params() != null) { - queryParam = request.getParam("q"); - } + // 1. Check if the request method is GET + if (request.method() == HttpMethod.GET) { + String uri = request.uri(); + String queryParam = request.getParam("q"); - if (queryParam != null && !queryParam.isEmpty()) { - this.handleHookSearch(queryParam, response); - return true; + // 2. Check if the URI is for listeners or routes and has a query parameter + if (queryParam != null && !queryParam.isEmpty()) { + if (uri.contains(HOOK_LISTENER_STORAGE_PATH)) { + handleListenerSearch(queryParam, request.response()); + return true; + } else if (uri.contains(HOOK_ROUTE_STORAGE_PATH)) { + handleRouteSearch(queryParam, request.response()); + return true; + } + } + else { + return false; + } } /* @@ -604,38 +620,45 @@ public boolean handle(final RoutingContext ctx) { } } - /** - * Handles hook search requests based on the 'destination' property. - * Searches in both routes and listeners. - * - * @param queryParam the RoutingContext of the request - */ - public void handleHookSearch(String queryParam,HttpServerResponse response) { - JsonObject result = new JsonObject(); - JsonArray matchingRoutes = new JsonArray(); - JsonArray matchingListeners = new JsonArray(); + private void handleListenerSearch(String queryParam, HttpServerResponse response) { + handleSearch( + listenerRepository.getListeners().stream().collect(Collectors.toMap(Listener::getListenerId, listener -> listener)), + listener -> listener.getHook().getDestination(), + queryParam, + LISTENERS_KEY, + response + ); + } - // Search routes by destination - routeRepository.getRoutes().forEach((routeKey, route) -> { - if (route.getHook().getDestination().contains(queryParam)) { - matchingRoutes.add(routeKey); - } - }); + private void handleRouteSearch(String queryParam, HttpServerResponse response) { + handleSearch( + routeRepository.getRoutes(), + route -> route.getHook().getDestination(), + queryParam, + ROUTES_KEY, + response + ); + } + + private void handleSearch(Map repository, Function getDestination, String queryParam, String resultKey, HttpServerResponse response) { + JsonArray matchingResults = new JsonArray(); - // Search listeners by destination - listenerRepository.getListeners().forEach(listener -> { - if (listener.getHook().getDestination().contains(queryParam)) { - matchingListeners.add(listener.getListenerId()); + repository.forEach((key, value) -> { + String destination = getDestination.apply(value); + if (destination != null && destination.contains(queryParam)) { + matchingResults.add(key); } }); - // Build and send the response - result.put("routes", matchingRoutes); - result.put("listeners", matchingListeners); + JsonObject result = new JsonObject(); + result.put(resultKey, matchingResults); - response.putHeader("content-type", "application/json").end(result.encode()); - } + // Set headers safely before writing the response + response.putHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE_JSON); + response.write(result.encode()); + response.end(); + } /** * Create a listing of routes in the given parent. This happens diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index 17ac1f21..a346457f 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -8,6 +8,7 @@ import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.core.net.HostAndPort; +import io.vertx.ext.unit.Async; import io.vertx.ext.unit.TestContext; import io.vertx.ext.unit.junit.VertxUnitRunner; import io.vertx.ext.web.RoutingContext; @@ -19,10 +20,7 @@ import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.swisspush.gateleen.core.http.DummyHttpServerRequest; -import org.swisspush.gateleen.core.http.DummyHttpServerResponse; -import org.swisspush.gateleen.core.http.FastFailHttpServerRequest; -import org.swisspush.gateleen.core.http.FastFailHttpServerResponse; +import org.swisspush.gateleen.core.http.*; import org.swisspush.gateleen.core.storage.MockResourceStorage; import org.swisspush.gateleen.hook.reducedpropagation.ReducedPropagationManager; import org.swisspush.gateleen.logging.LogAppenderRepository; @@ -41,8 +39,9 @@ import java.util.Map; import static io.vertx.core.http.HttpMethod.PUT; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import static org.swisspush.gateleen.core.util.HttpRequestHeader.*; /** @@ -72,15 +71,15 @@ public class HookHandlerTest { @Before public void setUp() { vertx = Vertx.vertx(); - routingContext = Mockito.mock(RoutingContext.class); - httpClient = Mockito.mock(HttpClient.class); - Mockito.when(httpClient.request(any(HttpMethod.class), anyString())).thenReturn(Mockito.mock(Future.class)); + routingContext = mock(RoutingContext.class); + httpClient = mock(HttpClient.class); + when(httpClient.request(any(HttpMethod.class), anyString())).thenReturn(mock(Future.class)); storage = new MockResourceStorage(); - loggingResourceManager = Mockito.mock(LoggingResourceManager.class); - logAppenderRepository = Mockito.mock(LogAppenderRepository.class); - monitoringHandler = Mockito.mock(MonitoringHandler.class); - requestQueue = Mockito.mock(RequestQueue.class); - reducedPropagationManager = Mockito.mock(ReducedPropagationManager.class); + loggingResourceManager = mock(LoggingResourceManager.class); + logAppenderRepository = mock(LogAppenderRepository.class); + monitoringHandler = mock(MonitoringHandler.class); + requestQueue = mock(RequestQueue.class); + reducedPropagationManager = mock(ReducedPropagationManager.class); hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, logAppenderRepository, monitoringHandler, @@ -136,7 +135,7 @@ public void testListenerEnqueueWithDefaultQueueingStrategy(TestContext context) PUTRequest putRequest = new PUTRequest(uri, originalPayload); putRequest.addHeader(CONTENT_LENGTH.getName(), "99"); - Mockito.when(routingContext.request()).thenReturn(putRequest); + when(routingContext.request()).thenReturn(putRequest); hookHandler.handle(routingContext); @@ -163,7 +162,7 @@ public void testListenerEnqueueWithDefaultQueueingStrategyBecauseOfInvalidConfig PUTRequest putRequest = new PUTRequest(uri, originalPayload); putRequest.addHeader(CONTENT_LENGTH.getName(), "99"); - Mockito.when(routingContext.request()).thenReturn(putRequest); + when(routingContext.request()).thenReturn(putRequest); hookHandler.handle(routingContext); // verify that enqueue has been called WITH the payload @@ -188,7 +187,7 @@ public void testListenerEnqueueWithDiscardPayloadQueueingStrategy(TestContext co String originalPayload = "{\"key\":123}"; PUTRequest putRequest = new PUTRequest(uri, originalPayload); putRequest.addHeader(CONTENT_LENGTH.getName(), "99"); - Mockito.when(routingContext.request()).thenReturn(putRequest); + when(routingContext.request()).thenReturn(putRequest); hookHandler.handle(routingContext); // verify that enqueue has been called WITHOUT the payload but with 'Content-Length : 0' header @@ -200,7 +199,7 @@ public void testListenerEnqueueWithDiscardPayloadQueueingStrategy(TestContext co }), anyString(), any(Handler.class)); PUTRequest putRequestWithoutContentLengthHeader = new PUTRequest(uri, originalPayload); - Mockito.when(routingContext.request()).thenReturn(putRequestWithoutContentLengthHeader); + when(routingContext.request()).thenReturn(putRequestWithoutContentLengthHeader); hookHandler.handle(routingContext); // verify that enqueue has been called WITHOUT the payload and WITHOUT 'Content-Length' header @@ -229,7 +228,7 @@ public void testListenerEnqueueWithReducedPropagationQueueingStrategyButNoManage String originalPayload = "{\"key\":123}"; PUTRequest putRequest = new PUTRequest(uri, originalPayload); putRequest.addHeader(CONTENT_LENGTH.getName(), "99"); - Mockito.when(routingContext.request()).thenReturn(putRequest); + when(routingContext.request()).thenReturn(putRequest); hookHandler.handle(routingContext); // verify that no enqueue (or lockedEnqueue) has been called because no ReducedPropagationManager was configured @@ -255,7 +254,7 @@ public void testListenerEnqueueWithReducedPropagationQueueingStrategy(TestContex PUTRequest putRequest = new PUTRequest(uri, originalPayload); putRequest.addHeader(CONTENT_LENGTH.getName(), "99"); - Mockito.when(routingContext.request()).thenReturn(putRequest); + when(routingContext.request()).thenReturn(putRequest); hookHandler.handle(routingContext); String targetUri = "/playground/server/push/v1/devices/" + deviceId + "/playground/server/tests/hooktest/abc123"; @@ -277,7 +276,7 @@ public void testListenerEnqueueWithInvalidReducedPropagationQueueingStrategy(Tes PUTRequest putRequest = new PUTRequest(uri, originalPayload); putRequest.addHeader(CONTENT_LENGTH.getName(), "99"); - Mockito.when(routingContext.request()).thenReturn(putRequest); + when(routingContext.request()).thenReturn(putRequest); hookHandler.handle(routingContext); // verify that enqueue has been called WITH the payload @@ -304,7 +303,7 @@ public void testListenerEnqueueWithMatchingRequestsHeaderFilter(TestContext cont putRequest.addHeader(CONTENT_LENGTH.getName(), "99"); putRequest.addHeader("x-foo", "A"); - Mockito.when(routingContext.request()).thenReturn(putRequest); + when(routingContext.request()).thenReturn(putRequest); hookHandler.handle(routingContext); // verify that enqueue has been called WITH the payload @@ -329,7 +328,7 @@ public void testListenerNoEnqueueWithoutMatchingRequestsHeaderFilter(TestContext String originalPayload = "{\"key\":123}"; PUTRequest putRequest = new PUTRequest(uri, originalPayload); putRequest.addHeader(CONTENT_LENGTH.getName(), "99"); - Mockito.when(routingContext.request()).thenReturn(putRequest); + when(routingContext.request()).thenReturn(putRequest); hookHandler.handle(routingContext); // verify that no enqueue has been called since the header did not match @@ -372,7 +371,7 @@ public void hookRegistration_usesDefaultExpiryIfExpireAfterHeaderIsNegativeNumbe } // Trigger work - Mockito.when(routingContext.request()).thenReturn(request); + when(routingContext.request()).thenReturn(request); hookHandler.handle(routingContext); // Assert request was ok @@ -408,7 +407,7 @@ public void hookRegistration_RouteWithTimeout(TestContext testContext) { } // Trigger work - Mockito.when(routingContext.request()).thenReturn(request); + when(routingContext.request()).thenReturn(request); hookHandler.handle(routingContext); // Assert request was ok @@ -443,7 +442,7 @@ public void hookRegistration_usesDefaultExpiryWhenHeaderContainsCorruptValue(Tes } // Trigger work - Mockito.when(routingContext.request()).thenReturn(request); + when(routingContext.request()).thenReturn(request); hookHandler.handle(routingContext); // Assert request was ok @@ -474,7 +473,7 @@ public void hookRegistration_usesDefaultExpiryIfHeaderIsMissing(TestContext test } // Trigger work - Mockito.when(routingContext.request()).thenReturn(request); + when(routingContext.request()).thenReturn(request); hookHandler.handle(routingContext); // Assert request was ok @@ -505,7 +504,7 @@ public void hookRegistration_usesMinusOneIfExpireAfterIsSetToMinusOne(TestContex } // Trigger work - Mockito.when(routingContext.request()).thenReturn(request); + when(routingContext.request()).thenReturn(request); hookHandler.handle(routingContext); // Assert request was ok @@ -539,7 +538,7 @@ public void listenerRegistration_acceptOnlyWhitelistedHttpMethods(TestContext te } // Trigger - Mockito.when(routingContext.request()).thenReturn(request); + when(routingContext.request()).thenReturn(request); hookHandler.handle(routingContext); { // Assert request got accepted. @@ -575,7 +574,7 @@ public void listenerRegistration_rejectNotWhitelistedHttpMethods(TestContext tes } // Trigger - Mockito.when(routingContext.request()).thenReturn(request); + when(routingContext.request()).thenReturn(request); hookHandler.handle(routingContext); { // Assert request got rejected. @@ -645,141 +644,128 @@ public void hookRegistration_RouteWithRouteMultiplier(TestContext testContext) t } @Test - public void testHookHandleSearchWithMatchingRoutesAndListeners(TestContext context) throws Exception { - // Mock the response - HttpServerResponse response = Mockito.mock(HttpServerResponse.class); - Mockito.when(response.putHeader(anyString(), anyString())).thenReturn(response); - - // Mock the request and set the query parameter - HttpServerRequest request = Mockito.mock(HttpServerRequest.class); - Mockito.when(request.response()).thenReturn(response); - Mockito.when(request.getParam("q")).thenReturn("destination"); - - // Mock the route and listener - Route mockRoute = Mockito.mock(Route.class); - HttpHook mockHook = new HttpHook("destination/matching"); - Mockito.when(mockRoute.getHook()).thenReturn(mockHook); - - Listener mockListener = new Listener("listener1", "monitoredUrl", "destination/matching", mockHook); - - // Mock repositories - ListenerRepository listenerRepository = Mockito.mock(ListenerRepository.class); - RouteRepository routeRepository = Mockito.mock(RouteRepository.class); - - // Configure mocked behavior for repositories - Mockito.when(routeRepository.getRoutes()).thenReturn(Map.of("route1", mockRoute)); - Mockito.when(listenerRepository.getListeners()).thenReturn(Collections.singletonList(mockListener)); - - // Create HookHandler instance - HookHandler hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, - logAppenderRepository, monitoringHandler, "userProfilePath", HOOK_ROOT_URI, requestQueue, - false - ); - - // Use reflection to set private fields - setPrivateField(hookHandler, "listenerRepository", listenerRepository); - setPrivateField(hookHandler, "routeRepository", routeRepository); - - // Call the method under test - hookHandler.handleHookSearch("destination", response); - - // Capture the output and verify the response was sent correctly - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); - Mockito.verify(response).end(captor.capture()); + public void testHandleListenerSearch_Success() { + // Arrange + String queryParam = "validQueryParam"; + String uri = "registrations/listeners/?q=" + queryParam; + + HttpServerRequest request = mock(HttpServerRequest.class); + HttpServerResponse response = mock(HttpServerResponse.class); + + // Mock request and response behavior + when(request.uri()).thenReturn(uri); + when(request.method()).thenReturn(HttpMethod.GET); + when(request.getParam("q")).thenReturn(queryParam); + when(request.response()).thenReturn(response); + + // Mock RoutingContext + RoutingContext routingContext = mock(RoutingContext.class); + when(routingContext.request()).thenReturn(request); + + // Act + boolean result = hookHandler.handle(routingContext); // Calls the public `handle` method + + // Assert + assertTrue(result); // Ensure the handler returns true for a valid listener search + verify(response, times(1)).end(); // Ensure `response.end()` was called + } + @Test + public void testHandleListenerSearch_MissingQueryParam() { + String uri = "registrations/listeners/?q="; - // Check the result - String capturedResult = captor.getValue(); - JsonObject result = new JsonObject(capturedResult); - JsonArray routes = result.getJsonArray("routes"); - JsonArray listeners = result.getJsonArray("listeners"); + HttpServerRequest request = mock(HttpServerRequest.class); + HttpServerResponse response = mock(HttpServerResponse.class); - // Assert the expected results - context.assertTrue(routes.contains("route1")); - context.assertTrue(listeners.contains("listener1")); + when(request.uri()).thenReturn(uri); + when(request.method()).thenReturn(HttpMethod.GET); + when(request.getParam("q")).thenReturn(""); + when(request.response()).thenReturn(response); - // Verify the content-type header was set correctly - Mockito.verify(response).putHeader("content-type", "application/json"); - } + RoutingContext routingContext = mock(RoutingContext.class); + when(routingContext.request()).thenReturn(request); + boolean result = hookHandler.handle(routingContext); - private void setPrivateField(Object target, String fieldName, Object value) throws Exception { - Field field = target.getClass().getDeclaredField(fieldName); - field.setAccessible(true); - field.set(target, value); + assertFalse(result); + verify(response, never()).end(); } @Test - public void testHookHandleSearchWithNoMatchingRoutesOrListeners(TestContext context) throws Exception { - // Mock the response + public void testHandleListenerWithStorageAndSearchSuccess(TestContext context) { + vertx = Vertx.vertx(); + storage = new MockResourceStorage(); + LoggingResourceManager loggingResourceManager = Mockito.mock(LoggingResourceManager.class); + LogAppenderRepository logAppenderRepository = Mockito.mock(LogAppenderRepository.class); + MonitoringHandler monitoringHandler = Mockito.mock(MonitoringHandler.class); + RequestQueue requestQueue = Mockito.mock(RequestQueue.class); + + hookHandler = new HookHandler(vertx, Mockito.mock(HttpClient.class), storage, loggingResourceManager, logAppenderRepository, monitoringHandler, + "userProfilePath", "hookRootURI/", requestQueue, false, null); + // Prepopulate storage with a listener resource + storage.putMockData("hookRootURI/registrations/listeners/listener1", "{ \"hook\": { \"destination\": \"/test/endpoint\" } }"); + + // Mock RoutingContext and its behavior + RoutingContext routingContext = Mockito.mock(RoutingContext.class); + HttpServerRequest request = Mockito.mock(HttpServerRequest.class); HttpServerResponse response = Mockito.mock(HttpServerResponse.class); - Mockito.when(response.putHeader(anyString(), anyString())).thenReturn(response); - // Mock the request and set the query parameter - HttpServerRequest request = Mockito.mock(HttpServerRequest.class); + // Simulate a GET request with a query parameter + Mockito.when(request.method()).thenReturn(HttpMethod.GET); + Mockito.when(request.uri()).thenReturn("hookRootURI/registrations/listeners/?q=test"); + Mockito.when(request.getParam("q")).thenReturn("test"); Mockito.when(request.response()).thenReturn(response); - Mockito.when(request.getParam("q")).thenReturn("destination"); - - // Mock the route and listener - Route mockRoute = Mockito.mock(Route.class); - HttpHook mockHook = new HttpHook("destination/matching"); - Mockito.when(mockRoute.getHook()).thenReturn(mockHook); - - Listener mockListener = new Listener("listener1", "monitoredUrl", "destination/matching", mockHook); - - // Mock repositories - ListenerRepository listenerRepository = Mockito.mock(ListenerRepository.class); - RouteRepository routeRepository = Mockito.mock(RouteRepository.class); - - // Configure mocked behavior for repositories with no matching routes or listeners - Mockito.when(routeRepository.getRoutes()).thenReturn(Map.of()); - Mockito.when(listenerRepository.getListeners()).thenReturn(Collections.emptyList()); - - // Create HookHandler instance - HookHandler hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, - logAppenderRepository, monitoringHandler, "userProfilePath", HOOK_ROOT_URI, requestQueue, - false - ); - // Use reflection to set private fields - setPrivateField(hookHandler, "listenerRepository", listenerRepository); - setPrivateField(hookHandler, "routeRepository", routeRepository); - - // Call the method under test - hookHandler.handleHookSearch("destination", response); + Mockito.when(routingContext.request()).thenReturn(request); - // Capture the output and verify the response was sent correctly - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); - Mockito.verify(response).end(captor.capture()); + // Async handler to check the result + Async async = context.async(); - // Check the result - String capturedResult = captor.getValue(); - JsonObject result = new JsonObject(capturedResult); - JsonArray routes = result.getJsonArray("routes"); - JsonArray listeners = result.getJsonArray("listeners"); + // Act: Call the hookHandler.handle method + boolean handled = hookHandler.handle(routingContext); - // Assert that there are no matching routes or listeners - context.assertTrue(routes.isEmpty()); - context.assertTrue(listeners.isEmpty()); + // Assert that it was handled + context.assertTrue(handled); - // Verify the content-type header was set correctly - Mockito.verify(response).putHeader("content-type", "application/json"); + // Verify that the response ended correctly (simulating a successful response) + Mockito.verify(response, Mockito.times(1)).end(); + async.complete(); } @Test - public void testHookHandleSearchWithInvalidQueryParam(TestContext context) { - // Mocking the HttpServerResponse and request objects + public void testHandleListenerWithStorageAndSearchFailure(TestContext context) { + vertx = Vertx.vertx(); + storage = new MockResourceStorage(); + LoggingResourceManager loggingResourceManager = Mockito.mock(LoggingResourceManager.class); + LogAppenderRepository logAppenderRepository = Mockito.mock(LogAppenderRepository.class); + MonitoringHandler monitoringHandler = Mockito.mock(MonitoringHandler.class); + RequestQueue requestQueue = Mockito.mock(RequestQueue.class); + + hookHandler = new HookHandler(vertx, Mockito.mock(HttpClient.class), storage, loggingResourceManager, logAppenderRepository, monitoringHandler, + "userProfilePath", "hookRootURI/", requestQueue, false, null); + RoutingContext routingContext = Mockito.mock(RoutingContext.class); + HttpServerRequest request = Mockito.mock(HttpServerRequest.class); HttpServerResponse response = Mockito.mock(HttpServerResponse.class); - Mockito.when(response.putHeader(anyString(), anyString())).thenReturn(response); - HttpServerRequest request = Mockito.mock(HttpServerRequest.class); + // Simulate a GET request without a query parameter + Mockito.when(request.method()).thenReturn(HttpMethod.GET); + Mockito.when(request.uri()).thenReturn("hookRootURI/registrations/listeners/"); + Mockito.when(request.getParam("q")).thenReturn(null); // No query parameter Mockito.when(request.response()).thenReturn(response); - Mockito.when(request.getParam("q")).thenReturn(null); - // Call hookHandleSearch - hookHandler.handleHookSearch(null, response); + Mockito.when(routingContext.request()).thenReturn(request); + + // Async handler to check the result + Async async = context.async(); + + // Act: Call the hookHandler.handle method + boolean handled = hookHandler.handle(routingContext); + + // Assert that it was NOT handled (as the query param is missing) + context.assertFalse(handled); - // Verify that nothing is returned - Mockito.verify(response, Mockito.never()).end(any(Buffer.class)); + // Verify that the response was NOT ended (because it shouldn't be processed) + Mockito.verify(response, Mockito.never()).end(); + async.complete(); } @@ -805,7 +791,7 @@ public HttpServerResponse response() { }; putRequest.addHeader(CONTENT_LENGTH.getName(), "99"); - Mockito.when(routingContext.request()).thenReturn(putRequest); + when(routingContext.request()).thenReturn(putRequest); hookHandler.handle(routingContext); latch.await(); From 4a0b791f28025860dd0433edf1926c018cb4c4c1 Mon Sep 17 00:00:00 2001 From: almeidast Date: Thu, 10 Oct 2024 15:36:18 +0100 Subject: [PATCH 04/37] Add integration tests for handleListenerSearch - Verifies successful listener search when multiple listeners are present in storage. - Ensures correct retrieval of a single listener from storage. - Tests failure case when searching for a non-existent listener among multiple listeners. - Ensures proper handling of search when no listeners are registered in storage. --- .../gateleen/hook/HookHandlerTest.java | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index a346457f..474d7774 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -768,6 +768,207 @@ public void testHandleListenerWithStorageAndSearchFailure(TestContext context) { async.complete(); } + @Test + public void testSearchMultipleListeners_Success(TestContext context) { + Vertx vertx = Vertx.vertx(); + MockResourceStorage storage = new MockResourceStorage(); + HttpClient httpClient = vertx.createHttpClient(); + + // Create mock implementation for RequestQueue + RequestQueue requestQueue = Mockito.mock(RequestQueue.class); + + // Initialize HookHandler + String HOOK_LISTENER_STORAGE_PATH = "/_hooks/listeners/"; + HookHandler hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, + logAppenderRepository, monitoringHandler, + "userProfilePath", "/hookRootUri", + requestQueue, false, reducedPropagationManager); + + // Add multiple listeners to the storage + JsonObject listener1 = new JsonObject() + .put("requesturl", "/playground/server/tests/hooktest/_hooks/listeners/http/push/x99") + .put("expirationTime", "2025-01-03T14:15:53.277") + .put("hook", new JsonObject().put("destination", "/playground/server/push/v1/devices/x99")); + JsonObject listener2 = new JsonObject() + .put("requesturl", "/playground/server/tests/hooktest/_hooks/listeners/http/push/x100") + .put("expirationTime", "2025-01-03T14:15:53.277") + .put("hook", new JsonObject().put("destination", "/playground/server/push/v1/devices/x100")); + JsonObject listener3 = new JsonObject() + .put("requesturl", "/playground/server/tests/hooktest/_hooks/listeners/http/push/x101") + .put("expirationTime", "2025-01-03T14:15:53.277") + .put("hook", new JsonObject().put("destination", "/playground/server/push/v1/devices/x101")); + + storage.putMockData(HOOK_LISTENER_STORAGE_PATH + "x99", listener1.encode()); + storage.putMockData(HOOK_LISTENER_STORAGE_PATH + "x100", listener2.encode()); + storage.putMockData(HOOK_LISTENER_STORAGE_PATH + "x101", listener3.encode()); + + // Configure HttpServer for integration test + io.vertx.ext.web.Router router = io.vertx.ext.web.Router.router(vertx); + router.route().handler(hookHandler::handle); + + HttpServer server = vertx.createHttpServer(); + server.requestHandler(router).listen(8080, ar -> { + if (ar.succeeded()) { + // Make a real HTTP request to the HookHandler + httpClient.request(HttpMethod.GET, 8080, "localhost", "/_hooks/listeners?q=x99") + .compose(HttpClientRequest::send) + .compose(response -> { + context.assertEquals(200, response.statusCode()); + return response.body(); + }) + .onSuccess(body -> { + JsonObject jsonResponse = new JsonObject(body.toString()); + context.assertTrue(jsonResponse.getJsonArray("listeners").contains("x99")); + context.assertFalse(jsonResponse.getJsonArray("listeners").contains("x100")); + context.async().complete(); + }) + .onFailure(context::fail); + } else { + context.fail(ar.cause()); + } + }); + } + @Test + public void testSearchSingleListener_Success(TestContext context) { + Vertx vertx = Vertx.vertx(); + MockResourceStorage storage = new MockResourceStorage(); + HttpClient httpClient = vertx.createHttpClient(); + + // Create mock implementation for RequestQueue + RequestQueue requestQueue = Mockito.mock(RequestQueue.class); + + // Initialize HookHandler + HookHandler hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, + logAppenderRepository, monitoringHandler, + "userProfilePath", "/hookRootUri/", + requestQueue, false, reducedPropagationManager); + + // Insert a single listener to the storage + JsonObject listener1 = new JsonObject() + .put("requesturl", "/playground/server/tests/hooktest/_hooks/listeners/http/push/listener1") + .put("expirationTime", "2025-01-03T14:15:53.277") + .put("hook", new JsonObject().put("destination", "/playground/server/push/v1/devices/listener1")); + storage.putMockData("/_hooks/listeners/listener1", listener1.encode()); + + // Configure HttpServer for integration test + io.vertx.ext.web.Router router = io.vertx.ext.web.Router.router(vertx); + router.route().handler(hookHandler::handle); + + HttpServer server = vertx.createHttpServer(); + server.requestHandler(router).listen(8080, ar -> { + if (ar.succeeded()) { + // Make a real HTTP request to the HookHandler + httpClient.request(HttpMethod.GET, 8080, "localhost", "/_hooks/listeners?q=listener1") + .compose(HttpClientRequest::send) + .compose(response -> { + context.assertEquals(200, response.statusCode()); + return response.body(); + }) + .onSuccess(body -> { + JsonObject jsonResponse = new JsonObject(body.toString()); + context.assertTrue(jsonResponse.getJsonArray("listeners").contains("listener1")); + context.async().complete(); + }) + .onFailure(context::fail); + } else { + context.fail(ar.cause()); + } + }); + } + @Test + public void testSearchListenerNotFound_MultipleListeners_Failure(TestContext context) { + Vertx vertx = Vertx.vertx(); + MockResourceStorage storage = new MockResourceStorage(); + HttpClient httpClient = vertx.createHttpClient(); + + // Create mock implementation for RequestQueue + RequestQueue requestQueue = Mockito.mock(RequestQueue.class); + + // Initialize HookHandler + String HOOK_LISTENER_STORAGE_PATH = "/_hooks/listeners/"; + HookHandler hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, + logAppenderRepository, monitoringHandler, + "userProfilePath", "/hookRootUri", + requestQueue, false, reducedPropagationManager); + + // Add multiple listeners to the storage + JsonObject listener2 = new JsonObject() + .put("requesturl", "/playground/server/tests/hooktest/_hooks/listeners/http/push/x100") + .put("expirationTime", "2025-01-03T14:15:53.277") + .put("hook", new JsonObject().put("destination", "/playground/server/push/v1/devices/x100")); + JsonObject listener3 = new JsonObject() + .put("requesturl", "/playground/server/tests/hooktest/_hooks/listeners/http/push/x101") + .put("expirationTime", "2025-01-03T14:15:53.277") + .put("hook", new JsonObject().put("destination", "/playground/server/push/v1/devices/x101")); + + storage.putMockData(HOOK_LISTENER_STORAGE_PATH + "x100", listener2.encode()); + storage.putMockData(HOOK_LISTENER_STORAGE_PATH + "x101", listener3.encode()); + + // Configure HttpServer for integration test + io.vertx.ext.web.Router router = io.vertx.ext.web.Router.router(vertx); + router.route().handler(hookHandler::handle); + + HttpServer server = vertx.createHttpServer(); + server.requestHandler(router).listen(8080, ar -> { + if (ar.succeeded()) { + // Make a real HTTP request to the HookHandler + httpClient.request(HttpMethod.GET, 8080, "localhost", "/_hooks/listeners?q=x99") + .compose(HttpClientRequest::send) + .compose(response -> { + context.assertEquals(200, response.statusCode()); + return response.body(); + }) + .onSuccess(body -> { + JsonObject jsonResponse = new JsonObject(body.toString()); + context.assertFalse(jsonResponse.getJsonArray("listeners").contains("x99")); + context.async().complete(); + }) + .onFailure(context::fail); + } else { + context.fail(ar.cause()); + } + }); + } + @Test + public void testSearchListenerNotFound_NoListeners_Failure(TestContext context) { + Vertx vertx = Vertx.vertx(); + MockResourceStorage storage = new MockResourceStorage(); + HttpClient httpClient = vertx.createHttpClient(); + + // Create mock implementation for RequestQueue + RequestQueue requestQueue = Mockito.mock(RequestQueue.class); + + // Initialize HookHandler + HookHandler hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, + logAppenderRepository, monitoringHandler, + "userProfilePath", "/hookRootUri/", + requestQueue, false, reducedPropagationManager); + + // Configure HttpServer for integration test + io.vertx.ext.web.Router router = io.vertx.ext.web.Router.router(vertx); + router.route().handler(hookHandler::handle); + + HttpServer server = vertx.createHttpServer(); + server.requestHandler(router).listen(8080, ar -> { + if (ar.succeeded()) { + // Make a real HTTP request to the HookHandler + httpClient.request(HttpMethod.GET, 8080, "localhost", "/_hooks/listeners?q=invalid") + .compose(HttpClientRequest::send) + .compose(response -> { + context.assertEquals(200, response.statusCode()); + return response.body(); + }) + .onSuccess(body -> { + JsonObject jsonResponse = new JsonObject(body.toString()); + context.assertFalse(jsonResponse.getJsonArray("listeners").contains("invalid")); + context.async().complete(); + }) + .onFailure(context::fail); + } else { + context.fail(ar.cause()); + } + }); + } /////////////////////////////////////////////////////////////////////////////// // Helpers From 922708c2b877b661ebe70df60b9267e7d4e6462b Mon Sep 17 00:00:00 2001 From: almeidast Date: Fri, 11 Oct 2024 12:12:14 +0100 Subject: [PATCH 05/37] Add an integration test at ListenerTest --- .../swisspush/gateleen/hook/ListenerTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java index 4282fe13..c51fc33b 100755 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java @@ -942,4 +942,35 @@ private void checkGETStatusCodeWithAwait(final String request, final Integer sta private void checkGETBodyWithAwait(final String requestUrl, final String body) { await().atMost(TEN_SECONDS).until(() -> when().get(requestUrl).then().extract().body().asString(), equalTo(body)); } + /** + * Test for hookHandleSearch with listener storage path and valid query param.
+ * eg. register / unregister: http://localhost:7012/gateleen/server/listenertest/_hooks/listeners/listener/1
+ * requestUrl: http://localhost:7012/gateleen/server/listenertest/listener/test?q=testQuery + */ + @Test + public void testHookHandleSearch_ListenerPathWithValidQueryParam(TestContext context) { + Async async = context.async(); + delete(); + initRoutingRules(); + + String queryParam = "testQuery"; + String listenerPath = "/_hooks/listeners"; + String requestUrl = requestUrlBase + listenerPath + "?q=" + queryParam; + + // Register a listener + TestUtils.registerListener(requestUrlBase + listenerPath, targetUrlBase, new String[]{"GET", "POST"}, null); + + // Send GET request + given().queryParam("q", queryParam) + .when().get(requestUrl) + .then().assertThat().statusCode(200); + + // Validate the response + checkGETStatusCodeWithAwait(requestUrl, 200); + + TestUtils.unregisterListener(requestUrlBase + listenerPath); + + async.complete(); + } + } From 2f712c04257c27618b5fcc08311fd7b1b1bb1e55 Mon Sep 17 00:00:00 2001 From: almeidast Date: Fri, 11 Oct 2024 12:47:42 +0100 Subject: [PATCH 06/37] Fix return method --- .../main/java/org/swisspush/gateleen/hook/HookHandler.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index d54a3431..1957e2b9 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -581,7 +581,6 @@ public boolean handle(final RoutingContext ctx) { if (request.method() == HttpMethod.GET) { String uri = request.uri(); String queryParam = request.getParam("q"); - // 2. Check if the URI is for listeners or routes and has a query parameter if (queryParam != null && !queryParam.isEmpty()) { if (uri.contains(HOOK_LISTENER_STORAGE_PATH)) { @@ -592,9 +591,6 @@ public boolean handle(final RoutingContext ctx) { return true; } } - else { - return false; - } } /* From a0161a25aa5d45dc634ed511cc9656b0fa1c67d7 Mon Sep 17 00:00:00 2001 From: almeidast Date: Fri, 11 Oct 2024 15:25:44 +0100 Subject: [PATCH 07/37] Added test for hookHandleSearch to verify behavior when no matching listener is found for a given query parameter. --- .../swisspush/gateleen/hook/ListenerTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java index c51fc33b..06dc038d 100755 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java @@ -973,4 +973,37 @@ public void testHookHandleSearch_ListenerPathWithValidQueryParam(TestContext con async.complete(); } + /** + * Test for hookHandleSearch with listener storage path and valid query param but no match found.
+ * eg. register / unregister: http://localhost:7012/gateleen/server/listenertest/_hooks/listeners/listener/1
+ * requestUrl: http://localhost:7012/gateleen/server/listenertest/listener/test?q=nonMatchingQuery + */ + @Test + public void testHookHandleSearch_ListenerPathWithNonMatchingQueryParam(TestContext context) { + Async async = context.async(); + delete(); + initRoutingRules(); + + String nonMatchingQueryParam = "nonMatchingQuery"; + String listenerPath = "/_hooks/listeners"; + String requestUrl = requestUrlBase + listenerPath + "?q=" + nonMatchingQueryParam; + + // Register a listener with a different query param + String differentQueryParam = "differentQuery"; + TestUtils.registerListener(requestUrlBase + listenerPath + "?q=" + differentQueryParam, targetUrlBase, new String[]{"GET", "POST"}, null); + + // Send GET request with non-matching query param + given().queryParam("q", nonMatchingQueryParam) + .when().get(requestUrl) + .then().assertThat().statusCode(404); // Expecting 404 as no listener matches the query + + // Validate that the response is not found + checkGETStatusCodeWithAwait(requestUrl, 404); + + // Unregister the listener + TestUtils.unregisterListener(requestUrlBase + listenerPath); + + async.complete(); + } + } From 322508fd88d102abe3eb9eb19c09cfa4a1fe9be6 Mon Sep 17 00:00:00 2001 From: almeidast Date: Fri, 11 Oct 2024 16:59:46 +0100 Subject: [PATCH 08/37] Add tests for hookHandleSearch with no matching listeners and no listeners registered. - Test for handling searches with no matching listeners, returning an empty list. - Test for handling searches when no listeners are registered, ensuring an empty list is returned. - Fix hookHandler Search --- .../swisspush/gateleen/hook/HookHandler.java | 14 ++++--- .../swisspush/gateleen/hook/ListenerTest.java | 38 +++++++++++++++++-- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index 1957e2b9..49e30042 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -583,10 +583,10 @@ public boolean handle(final RoutingContext ctx) { String queryParam = request.getParam("q"); // 2. Check if the URI is for listeners or routes and has a query parameter if (queryParam != null && !queryParam.isEmpty()) { - if (uri.contains(HOOK_LISTENER_STORAGE_PATH)) { + if (uri.contains(LISTENERS_KEY)) { handleListenerSearch(queryParam, request.response()); return true; - } else if (uri.contains(HOOK_ROUTE_STORAGE_PATH)) { + } else if (uri.contains(ROUTES_KEY)) { handleRouteSearch(queryParam, request.response()); return true; } @@ -649,11 +649,15 @@ private void handleSearch(Map repository, Function get JsonObject result = new JsonObject(); result.put(resultKey, matchingResults); - // Set headers safely before writing the response + String encodedResult = result.encode(); // Convert the result to a string + + // Set Content-Length header before sending the response response.putHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE_JSON); - response.write(result.encode()); - response.end(); + response.putHeader("Content-Length", String.valueOf(encodedResult.length())); // Set content length + // Write and end the response + response.write(encodedResult); + response.end(); } /** diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java index 06dc038d..7cdba97f 100755 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java @@ -995,10 +995,12 @@ public void testHookHandleSearch_ListenerPathWithNonMatchingQueryParam(TestConte // Send GET request with non-matching query param given().queryParam("q", nonMatchingQueryParam) .when().get(requestUrl) - .then().assertThat().statusCode(404); // Expecting 404 as no listener matches the query + .then().assertThat() + .statusCode(200) // Expecting 200 as the request is valid but no match found + .body("listeners", org.hamcrest.Matchers.empty()); // Expecting an empty list of listeners - // Validate that the response is not found - checkGETStatusCodeWithAwait(requestUrl, 404); + // Validate that the response is 200 and the result is an empty array + checkGETStatusCodeWithAwait(requestUrl, 200); // Unregister the listener TestUtils.unregisterListener(requestUrlBase + listenerPath); @@ -1006,4 +1008,34 @@ public void testHookHandleSearch_ListenerPathWithNonMatchingQueryParam(TestConte async.complete(); } + /** + * Test for hookHandleSearch with listener storage path and valid query param but no listeners registered.
+ * eg. register / unregister: http://localhost:7012/gateleen/server/listenertest/_hooks/listeners/listener/1
+ * requestUrl: http://localhost:7012/gateleen/server/listenertest/listener/test?q=someQuery + */ + @Test + public void testHookHandleSearch_NoListenersRegistered(TestContext context) { + Async async = context.async(); + delete(); + initRoutingRules(); + + String queryParam = "someQuery"; + String listenerPath = "/_hooks/listeners"; + String requestUrl = requestUrlBase + listenerPath + "?q=" + queryParam; + + // No listeners registered + + // Send GET request with a query param + given().queryParam("q", queryParam) + .when().get(requestUrl) + .then().assertThat() + .statusCode(200) // Expecting 200 as the request is valid but no listeners are registered + .body("listeners", org.hamcrest.Matchers.empty()); // Expecting an empty list of listeners + + // Validate that the response is 200 and the result is an empty array + checkGETStatusCodeWithAwait(requestUrl, 200); + + async.complete(); + } + } From 55ab41bb39425b8c34707edc1bf4327eb75f71c2 Mon Sep 17 00:00:00 2001 From: almeidast Date: Fri, 11 Oct 2024 18:53:50 +0100 Subject: [PATCH 09/37] Implemented a search mechanism that iterates over the provided repository. It checks if the destination of each item contains the query string (queryParam), adding matching items to the result. --- .../swisspush/gateleen/hook/HookHandler.java | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index 49e30042..40812349 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -636,12 +636,29 @@ private void handleRouteSearch(String queryParam, HttpServerResponse response) { ); } + /** + * Search the repository for items matching the query parameter. + * Returns a JSON response with the matched results. + * If any essential parameter (repository, response, getDestination) is null, + * a 400 Bad Request is returned. + * + * @param repository The items to search. + * @param getDestination Function to extract destinations. + * @param queryParam The query string to match. + * @param resultKey The key for the result in the response. + * @param response The HTTP response to return the results. Must not be null. + */ private void handleSearch(Map repository, Function getDestination, String queryParam, String resultKey, HttpServerResponse response) { + if (repository == null || getDestination == null) { + response.setStatusCode(400).end(); // Bad request for missing parameters + return; + } + JsonArray matchingResults = new JsonArray(); repository.forEach((key, value) -> { String destination = getDestination.apply(value); - if (destination != null && destination.contains(queryParam)) { + if (destination != null && destination.contains(queryParam != null ? queryParam : "")) { matchingResults.add(key); } }); @@ -649,17 +666,15 @@ private void handleSearch(Map repository, Function get JsonObject result = new JsonObject(); result.put(resultKey, matchingResults); - String encodedResult = result.encode(); // Convert the result to a string + String encodedResult = result.encode(); - // Set Content-Length header before sending the response response.putHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE_JSON); - response.putHeader("Content-Length", String.valueOf(encodedResult.length())); // Set content length - - // Write and end the response + response.putHeader("Content-Length", String.valueOf(encodedResult.length())); response.write(encodedResult); response.end(); } + /** * Create a listing of routes in the given parent. This happens * only if we have a GET request, the routes are listable and From 8feb3865add8bd00618166971a8f6aa5cfc73724 Mon Sep 17 00:00:00 2001 From: almeidast Date: Wed, 16 Oct 2024 12:05:10 +0100 Subject: [PATCH 10/37] Fix error in mock tests --- .../gateleen/hook/HookHandlerTest.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index 474d7774..c5e073cc 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -680,6 +680,7 @@ public void testHandleListenerSearch_MissingQueryParam() { when(request.method()).thenReturn(HttpMethod.GET); when(request.getParam("q")).thenReturn(""); when(request.response()).thenReturn(response); + when(request.headers()).thenReturn(MultiMap.caseInsensitiveMultiMap()); RoutingContext routingContext = mock(RoutingContext.class); when(routingContext.request()).thenReturn(request); @@ -746,28 +747,28 @@ public void testHandleListenerWithStorageAndSearchFailure(TestContext context) { HttpServerRequest request = Mockito.mock(HttpServerRequest.class); HttpServerResponse response = Mockito.mock(HttpServerResponse.class); - // Simulate a GET request without a query parameter + // Mock necessary methods Mockito.when(request.method()).thenReturn(HttpMethod.GET); Mockito.when(request.uri()).thenReturn("hookRootURI/registrations/listeners/"); - Mockito.when(request.getParam("q")).thenReturn(null); // No query parameter + Mockito.when(request.getParam("q")).thenReturn(null); // No query param Mockito.when(request.response()).thenReturn(response); + Mockito.when(request.headers()).thenReturn(MultiMap.caseInsensitiveMultiMap()); - Mockito.when(routingContext.request()).thenReturn(request); - // Async handler to check the result - Async async = context.async(); + HttpClient selfClient = Mockito.mock(HttpClient.class); + Mockito.when(selfClient.request(Mockito.any(), Mockito.anyString())).thenReturn(Future.failedFuture(new Exception("Mocked failure"))); - // Act: Call the hookHandler.handle method + Mockito.when(routingContext.request()).thenReturn(request); + + // Act boolean handled = hookHandler.handle(routingContext); - // Assert that it was NOT handled (as the query param is missing) + // Assert context.assertFalse(handled); - - // Verify that the response was NOT ended (because it shouldn't be processed) Mockito.verify(response, Mockito.never()).end(); - async.complete(); } + @Test public void testSearchMultipleListeners_Success(TestContext context) { Vertx vertx = Vertx.vertx(); From 4004646251bac88a50c53e1f4c0a3ec66da1c78d Mon Sep 17 00:00:00 2001 From: almeidast Date: Wed, 16 Oct 2024 14:01:22 +0100 Subject: [PATCH 11/37] - add tests for Route - improve listeners tests --- .../swisspush/gateleen/hook/ListenerTest.java | 1 + .../gateleen/hook/RouteListingTest.java | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java index 7cdba97f..b22a283d 100755 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java @@ -942,6 +942,7 @@ private void checkGETStatusCodeWithAwait(final String request, final Integer sta private void checkGETBodyWithAwait(final String requestUrl, final String body) { await().atMost(TEN_SECONDS).until(() -> when().get(requestUrl).then().extract().body().asString(), equalTo(body)); } + /** * Test for hookHandleSearch with listener storage path and valid query param.
* eg. register / unregister: http://localhost:7012/gateleen/server/listenertest/_hooks/listeners/listener/1
diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java index 4a76ac64..ed7ae6bf 100644 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java @@ -15,6 +15,7 @@ import org.swisspush.gateleen.TestUtils; import static io.restassured.RestAssured.*; +import static org.swisspush.gateleen.TestUtils.checkGETStatusCodeWithAwait; /** * Test class for the hook route feature. @@ -223,5 +224,91 @@ private void assertResponse(final Response response, final String[] expectedArra Assert.assertEquals(expectedArray.length, array.size()); Assert.assertThat(array, Matchers.contains(expectedArray)); } + /** + * Test for route listing with a valid query parameter. + */ + @Test + public void testRouteListing_ValidQueryParam(TestContext context) { + Async async = context.async(); + delete(); + initSettings(); + + String queryParam = "testQuery"; + String routePath = "/routes"; + String requestUrl = requestUrlBase + routePath + "?q=" + queryParam; + + // Register a route + TestUtils.registerRoute(requestUrlBase + routePath, targetUrlBase, new String[]{"GET", "POST"}, null, true, true); + + // Send GET request with a valid query param + given().queryParam("q", queryParam) + .when().get(requestUrl) + .then().assertThat().statusCode(200); + + // Validate response + checkGETStatusCodeWithAwait(requestUrl, 200); + + TestUtils.unregisterRoute(requestUrlBase + routePath); + + async.complete(); + } + + /** + * Test for route listing with a non-matching query parameter. + */ + @Test + public void testRouteListing_NonMatchingQueryParam(TestContext context) { + Async async = context.async(); + delete(); + initSettings(); + + String nonMatchingQueryParam = "nonMatchingQuery"; + String routePath = "/routes"; + String requestUrl = requestUrlBase + routePath + "?q=" + nonMatchingQueryParam; + + // Register a route with a different query param + String differentQueryParam = "differentQuery"; + TestUtils.registerRoute(requestUrlBase + routePath + "?q=" + differentQueryParam, targetUrlBase, new String[]{"GET", "POST"}, null, true, true); + + // Send GET request with non-matching query param + given().queryParam("q", nonMatchingQueryParam) + .when().get(requestUrl) + .then().assertThat().statusCode(200) + .body("routes", Matchers.empty()); + + // Validate response + checkGETStatusCodeWithAwait(requestUrl, 200); + + TestUtils.unregisterRoute(requestUrlBase + routePath); + + async.complete(); + } + + /** + * Test for route listing when no routes are registered. + */ + @Test + public void testRouteListing_NoRoutesRegistered(TestContext context) { + Async async = context.async(); + delete(); + initSettings(); + + String queryParam = "someQuery"; + String routePath = "/routes"; + String requestUrl = requestUrlBase + routePath + "?q=" + queryParam; + + // No routes registered + + // Send GET request with a query param + given().queryParam("q", queryParam) + .when().get(requestUrl) + .then().assertThat().statusCode(200) + .body("routes", Matchers.empty()); + + // Validate response + checkGETStatusCodeWithAwait(requestUrl, 200); + + async.complete(); + } } From 64e69057992f09c6de5c0ab2a6b16e6c103561a6 Mon Sep 17 00:00:00 2001 From: steniobhz Date: Thu, 17 Oct 2024 13:53:16 +0100 Subject: [PATCH 12/37] Update README_hook.md --- gateleen-hook/README_hook.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/gateleen-hook/README_hook.md b/gateleen-hook/README_hook.md index 556c2a84..5a1cc9e5 100644 --- a/gateleen-hook/README_hook.md +++ b/gateleen-hook/README_hook.md @@ -240,10 +240,43 @@ hookHandler.enableResourceLogging(true); ``` +## Query-Based Listener and Route Search +Gateleen allows searching for listeners and routes using the query parameter `q`. This simplifies filtering the registered hooks based on query parameters. +### Listener Search with `q` +Search for listeners based on a query parameter like this: +``` +GET http://myserver:7012/gateleen/server/listenertest/_hooks/listeners/listener/1?q=testQuery +``` + +The response will contain the matching listeners. If no match is found, an empty list is returned: + +**Example response with matches:** +```json +{ + "listeners": [ + { + "destination": "/path/to/destination", + "methods": ["GET", "POST"] + } + ] +} +``` +**Example response with no matches:** +```json +{ + "listeners": [] +} +``` +### Route Search with `q` +Similarly, you can search for routes using a query parameter: +``` +GET http://myserver:7012/gateleen/server/listenertest/_hooks/routes?q=testRoute +``` +The response contains the matching routes, or an empty list if no match is found. From 6aeec5cb744bc73e976090146417001539267b97 Mon Sep 17 00:00:00 2001 From: almeidast Date: Thu, 17 Oct 2024 18:10:07 +0100 Subject: [PATCH 13/37] add validation to return 400 instead 404 search parameter != "q" or null --- .../swisspush/gateleen/hook/HookHandler.java | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index 40812349..4fd633a4 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -90,7 +90,6 @@ public class HookHandler implements LoggableResource { public static final String HOOKS_LISTENERS_URI_PART = "/_hooks/listeners/"; public static final String LISTENER_QUEUE_PREFIX = "listener-hook"; private static final String X_QUEUE = "x-queue"; - private static final String X_EXPIRE_AFTER = "X-Expire-After"; private static final String LISTENER_HOOK_TARGET_PATH = "listeners/"; public static final String HOOKS_ROUTE_URI_PART = "/_hooks/route"; @@ -127,7 +126,6 @@ public class HookHandler implements LoggableResource { private static final String CONTENT_TYPE_JSON = "application/json"; private static final String LISTENERS_KEY = "listeners"; private static final String ROUTES_KEY = "routes"; - private static final String DESTINATION_KEY = "destination"; private static final String CONTENT_TYPE_HEADER = "content-type"; @@ -549,13 +547,12 @@ public void registerListenerRegistrationHandler(Handler readyHandler) { public boolean handle(final RoutingContext ctx) { HttpServerRequest request = ctx.request(); boolean consumed = false; - + var requestUri = request.uri(); /* * 1) Un- / Register Listener / Routes */ var requestMethod = request.method(); if (requestMethod == PUT) { - var requestUri = request.uri(); if (requestUri.contains(HOOKS_LISTENERS_URI_PART)) { handleListenerRegistration(request); return true; @@ -566,7 +563,6 @@ public boolean handle(final RoutingContext ctx) { } } if (requestMethod == DELETE) { - var requestUri = request.uri(); if (requestUri.contains(HOOKS_LISTENERS_URI_PART)) { handleListenerUnregistration(request); return true; @@ -579,17 +575,22 @@ public boolean handle(final RoutingContext ctx) { // 1. Check if the request method is GET if (request.method() == HttpMethod.GET) { - String uri = request.uri(); String queryParam = request.getParam("q"); - // 2. Check if the URI is for listeners or routes and has a query parameter - if (queryParam != null && !queryParam.isEmpty()) { - if (uri.contains(LISTENERS_KEY)) { + // If the 'q' parameter exists, proceed with search handling + if (queryParam != null) { + // Check if the URI corresponds to listeners or routes + if (requestUri.contains(LISTENERS_KEY)) { handleListenerSearch(queryParam, request.response()); return true; - } else if (uri.contains(ROUTES_KEY)) { + } else if (requestUri.contains(ROUTES_KEY)) { handleRouteSearch(queryParam, request.response()); return true; } + }else{ + if (!request.params().isEmpty()) { + request.response().setStatusCode(400).end("Bad Request: Only the 'q' parameter is allowed"); + return true; + } } } @@ -649,8 +650,9 @@ private void handleRouteSearch(String queryParam, HttpServerResponse response) { * @param response The HTTP response to return the results. Must not be null. */ private void handleSearch(Map repository, Function getDestination, String queryParam, String resultKey, HttpServerResponse response) { - if (repository == null || getDestination == null) { - response.setStatusCode(400).end(); // Bad request for missing parameters + + if (repository == null || getDestination == null || resultKey == null || queryParam.isEmpty()) { + response.setStatusCode(400).end("Bad Request: One or more required parameters are missing or null"); return; } @@ -658,7 +660,7 @@ private void handleSearch(Map repository, Function get repository.forEach((key, value) -> { String destination = getDestination.apply(value); - if (destination != null && destination.contains(queryParam != null ? queryParam : "")) { + if (destination != null && destination.contains(queryParam)) { matchingResults.add(key); } }); From 4f7f4ff2df18691fd15fc31d8303eb8d59698b2a Mon Sep 17 00:00:00 2001 From: almeidast Date: Thu, 17 Oct 2024 18:11:17 +0100 Subject: [PATCH 14/37] add testes to check if parameter is valid --- .../swisspush/gateleen/hook/ListenerTest.java | 66 +++++++++++++++++-- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java index b22a283d..9f1edd05 100755 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java @@ -945,8 +945,7 @@ private void checkGETBodyWithAwait(final String requestUrl, final String body) { /** * Test for hookHandleSearch with listener storage path and valid query param.
- * eg. register / unregister: http://localhost:7012/gateleen/server/listenertest/_hooks/listeners/listener/1
- * requestUrl: http://localhost:7012/gateleen/server/listenertest/listener/test?q=testQuery + * requestUrl: http://localhost:7012/playground/server/hooks/v1/registrations/listeners/?q=testQuery */ @Test public void testHookHandleSearch_ListenerPathWithValidQueryParam(TestContext context) { @@ -976,8 +975,7 @@ public void testHookHandleSearch_ListenerPathWithValidQueryParam(TestContext con /** * Test for hookHandleSearch with listener storage path and valid query param but no match found.
- * eg. register / unregister: http://localhost:7012/gateleen/server/listenertest/_hooks/listeners/listener/1
- * requestUrl: http://localhost:7012/gateleen/server/listenertest/listener/test?q=nonMatchingQuery + * requestUrl: http://localhost:7012/playground/server/hooks/v1/registrations/listeners/?q=nonMatchingQuery */ @Test public void testHookHandleSearch_ListenerPathWithNonMatchingQueryParam(TestContext context) { @@ -1011,8 +1009,7 @@ public void testHookHandleSearch_ListenerPathWithNonMatchingQueryParam(TestConte /** * Test for hookHandleSearch with listener storage path and valid query param but no listeners registered.
- * eg. register / unregister: http://localhost:7012/gateleen/server/listenertest/_hooks/listeners/listener/1
- * requestUrl: http://localhost:7012/gateleen/server/listenertest/listener/test?q=someQuery + * requestUrl: http://localhost:7012/playground/server/hooks/v1/registrations/listeners/?q=someQuery */ @Test public void testHookHandleSearch_NoListenersRegistered(TestContext context) { @@ -1039,4 +1036,61 @@ public void testHookHandleSearch_NoListenersRegistered(TestContext context) { async.complete(); } + + @Test + public void testHookHandleSearch_ListenerPathInvalidParam(TestContext context) { + Async async = context.async(); + delete(); + initRoutingRules(); + + String queryParam = "testQuery"; + String listenerPath = "/_hooks/listeners"; + String requestUrl = requestUrlBase + listenerPath + "?www=" + queryParam; + + // Register a listener + TestUtils.registerListener(requestUrlBase + listenerPath, targetUrlBase, new String[]{"GET", "POST"}, null); + + // Send GET request + given().queryParam("www", queryParam) + .when().get(requestUrl) + .then().assertThat().statusCode(400); + + // Validate the response + checkGETStatusCodeWithAwait(requestUrl, 400); + + TestUtils.unregisterListener(requestUrlBase + listenerPath); + + async.complete(); + } + + /** + * Test for hookHandleSearch with listener storage path and no query parameter.
+ * requestUrl: http://localhost:7012/playground/server/hooks/v1/registrations/listeners/?q= + */ + @Test + public void testHookHandleSearch_NoQueryParameter(TestContext context) { + Async async = context.async(); + delete(); + initRoutingRules(); + + String queryParam = ""; + String listenerPath = "/_hooks/listeners"; + String requestUrl = requestUrlBase + listenerPath + "?q=" + queryParam; + + // Register a listener + TestUtils.registerListener(requestUrlBase + listenerPath, targetUrlBase, new String[]{"GET", "POST"}, null); + + // Send GET request + given().queryParam("q", queryParam) + .when().get(requestUrl) + .then().assertThat().statusCode(400); + + // Validate the response + checkGETStatusCodeWithAwait(requestUrl, 400); + + TestUtils.unregisterListener(requestUrlBase + listenerPath); + + async.complete(); + } + } From 777b906d4381aeeb3383dd46b4e0a8a0c16b5f27 Mon Sep 17 00:00:00 2001 From: almeidast Date: Mon, 21 Oct 2024 19:26:15 +0100 Subject: [PATCH 15/37] Fix RouteListingTest and added more validations. --- .../gateleen/hook/RouteListingTest.java | 75 +++++++++++-------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java index ed7ae6bf..06f27d52 100644 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java @@ -224,31 +224,35 @@ private void assertResponse(final Response response, final String[] expectedArra Assert.assertEquals(expectedArray.length, array.size()); Assert.assertThat(array, Matchers.contains(expectedArray)); } + /** * Test for route listing with a valid query parameter. */ @Test public void testRouteListing_ValidQueryParam(TestContext context) { Async async = context.async(); - delete(); - initSettings(); + delete(); // Remove any pre-existing data + initSettings(); // Initialize routing rules - String queryParam = "testQuery"; + String queryParam = "routeTests"; String routePath = "/routes"; - String requestUrl = requestUrlBase + routePath + "?q=" + queryParam; + String requestUrl = requestUrlBase + routePath; - // Register a route - TestUtils.registerRoute(requestUrlBase + routePath, targetUrlBase, new String[]{"GET", "POST"}, null, true, true); + addRoute(queryParam, true, true); - // Send GET request with a valid query param - given().queryParam("q", queryParam) - .when().get(requestUrl) - .then().assertThat().statusCode(200); + // Verify that the route was correctly registered + Response response = given() + .queryParam("q", queryParam) + .when().get(requestUrl + "?q=" + queryParam) + .then().assertThat().statusCode(200) + .extract().response(); - // Validate response - checkGETStatusCodeWithAwait(requestUrl, 200); + // Assert that the response contains the expected query param + String responseBody = response.getBody().asString(); + Assert.assertTrue(responseBody.contains(queryParam)); // Fails if not found - TestUtils.unregisterRoute(requestUrlBase + routePath); + // Unregister the route + removeRoute(queryParam); async.complete(); } @@ -259,27 +263,29 @@ public void testRouteListing_ValidQueryParam(TestContext context) { @Test public void testRouteListing_NonMatchingQueryParam(TestContext context) { Async async = context.async(); - delete(); - initSettings(); + delete(); // Clean up before the test + initSettings(); // Initialize routing rules String nonMatchingQueryParam = "nonMatchingQuery"; + String queryParam = "other"; String routePath = "/routes"; - String requestUrl = requestUrlBase + routePath + "?q=" + nonMatchingQueryParam; - - // Register a route with a different query param - String differentQueryParam = "differentQuery"; - TestUtils.registerRoute(requestUrlBase + routePath + "?q=" + differentQueryParam, targetUrlBase, new String[]{"GET", "POST"}, null, true, true); + String requestUrl = requestUrlBase + routePath; - // Send GET request with non-matching query param - given().queryParam("q", nonMatchingQueryParam) + // Register a route using the addRoute method + addRoute(queryParam, true, true); + assertResponse(get(requestUrlBase), new String[]{queryParam+"/"}); + // Send GET request with a non-matching query param + Response response = given().queryParam("q", nonMatchingQueryParam) .when().get(requestUrl) .then().assertThat().statusCode(200) - .body("routes", Matchers.empty()); + .extract().response(); - // Validate response - checkGETStatusCodeWithAwait(requestUrl, 200); + // Assert the response does not contain the non-matching query param + Assert.assertFalse("Non-matching query param should not be found in response", + response.getBody().asString().contains(nonMatchingQueryParam)); - TestUtils.unregisterRoute(requestUrlBase + routePath); + // Unregister the route + removeRoute(queryParam); async.complete(); } @@ -290,8 +296,8 @@ public void testRouteListing_NonMatchingQueryParam(TestContext context) { @Test public void testRouteListing_NoRoutesRegistered(TestContext context) { Async async = context.async(); - delete(); - initSettings(); + delete(); // Ensure there's no previous data + initSettings(); // Initialize routing rules String queryParam = "someQuery"; String routePath = "/routes"; @@ -300,15 +306,20 @@ public void testRouteListing_NoRoutesRegistered(TestContext context) { // No routes registered // Send GET request with a query param - given().queryParam("q", queryParam) + Response response = given().queryParam("q", queryParam) .when().get(requestUrl) .then().assertThat().statusCode(200) - .body("routes", Matchers.empty()); + .extract().response(); + + // Print the body of the response for debugging + System.out.println("Response body: " + response.getBody().asString()); - // Validate response - checkGETStatusCodeWithAwait(requestUrl, 200); + // Assert that the response body is empty or does not contain routes + Assert.assertTrue("No routes should be registered", + response.getBody().asString().contains("routes")); async.complete(); } + } From c392df5f3c7b32b28ca272686784f5101944f08a Mon Sep 17 00:00:00 2001 From: almeidast Date: Mon, 21 Oct 2024 19:31:38 +0100 Subject: [PATCH 16/37] Fix README_hook.md --- gateleen-hook/README_hook.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/gateleen-hook/README_hook.md b/gateleen-hook/README_hook.md index 5a1cc9e5..d2964a9f 100644 --- a/gateleen-hook/README_hook.md +++ b/gateleen-hook/README_hook.md @@ -257,10 +257,7 @@ The response will contain the matching listeners. If no match is found, an empty ```json { "listeners": [ - { - "destination": "/path/to/destination", - "methods": ["GET", "POST"] - } + "first+playground+server+test+nemo+origin+b" ] } ``` From 19bc63dbdd823c690acdc3278b2cb48bb81c2970 Mon Sep 17 00:00:00 2001 From: almeidast Date: Tue, 22 Oct 2024 14:56:11 +0100 Subject: [PATCH 17/37] Recreate and fix tests --- .../swisspush/gateleen/hook/ListenerTest.java | 154 +++++++++++------- .../gateleen/hook/RouteListingTest.java | 12 +- 2 files changed, 97 insertions(+), 69 deletions(-) diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java index 9f1edd05..4298ad3a 100755 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java @@ -6,11 +6,13 @@ import io.restassured.RestAssured; import io.restassured.http.Header; import io.restassured.http.Headers; +import io.restassured.response.Response; import io.vertx.core.json.JsonObject; import io.vertx.ext.unit.Async; import io.vertx.ext.unit.TestContext; import io.vertx.ext.unit.junit.VertxUnitRunner; import org.awaitility.Awaitility; +import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -953,22 +955,34 @@ public void testHookHandleSearch_ListenerPathWithValidQueryParam(TestContext con delete(); initRoutingRules(); - String queryParam = "testQuery"; - String listenerPath = "/_hooks/listeners"; - String requestUrl = requestUrlBase + listenerPath + "?q=" + queryParam; + // Settings + String subresource = "validQueryParameter"; + String listenerName = "myListener"; - // Register a listener - TestUtils.registerListener(requestUrlBase + listenerPath, targetUrlBase, new String[]{"GET", "POST"}, null); + String registerUrlListener1 = requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix() + listenerName; + String targetListener1 = targetUrlBase + "/" + listenerName; + String[] methodsListener1 = new String[]{"PUT", "DELETE", "POST"}; + final String targetUrlListener1 = targetUrlBase + "/" + listenerName; - // Send GET request - given().queryParam("q", queryParam) - .when().get(requestUrl) - .then().assertThat().statusCode(200); + final String requestUrl = requestUrlBase + "/" + subresource; - // Validate the response - checkGETStatusCodeWithAwait(requestUrl, 200); + delete(requestUrl); + delete(targetUrlListener1); + + //Sending request, one listener hooked + TestUtils.registerListener(registerUrlListener1, targetListener1, methodsListener1, null, null, + null, null, "x-foo: (A|B)"); - TestUtils.unregisterListener(requestUrlBase + listenerPath); + // Verify that the listener was correctly registered + Response response = given() + .queryParam("q", listenerName) + .when().get(registerUrlListener1) + .then().assertThat().statusCode(200) + .extract().response(); + + // Assert that the response contains the expected query param + Assert.assertTrue(response.getBody().asString().contains(listenerName)); // Fails if not found + TestUtils.unregisterListener(registerUrlListener1); async.complete(); } @@ -983,26 +997,44 @@ public void testHookHandleSearch_ListenerPathWithNonMatchingQueryParam(TestConte delete(); initRoutingRules(); - String nonMatchingQueryParam = "nonMatchingQuery"; - String listenerPath = "/_hooks/listeners"; - String requestUrl = requestUrlBase + listenerPath + "?q=" + nonMatchingQueryParam; + // Settings + String subresource = "matchingQueryParam"; + String listenerName = "myListener"; - // Register a listener with a different query param - String differentQueryParam = "differentQuery"; - TestUtils.registerListener(requestUrlBase + listenerPath + "?q=" + differentQueryParam, targetUrlBase, new String[]{"GET", "POST"}, null); + String registerUrlListener1 = requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix() + listenerName; + String targetListener1 = targetUrlBase + "/" + listenerName; + String[] methodsListener1 = new String[]{"PUT", "DELETE", "POST"}; + final String targetUrlListener1 = targetUrlBase + "/" + listenerName + "/" + "test"; - // Send GET request with non-matching query param - given().queryParam("q", nonMatchingQueryParam) - .when().get(requestUrl) - .then().assertThat() - .statusCode(200) // Expecting 200 as the request is valid but no match found - .body("listeners", org.hamcrest.Matchers.empty()); // Expecting an empty list of listeners + final String requestUrl = requestUrlBase + "/" + subresource + "/" + "test"; - // Validate that the response is 200 and the result is an empty array - checkGETStatusCodeWithAwait(requestUrl, 200); + delete(requestUrl); + delete(targetUrlListener1); - // Unregister the listener - TestUtils.unregisterListener(requestUrlBase + listenerPath); + TestUtils.registerListener(registerUrlListener1, targetListener1, methodsListener1, null, null, + null, null, "x-foo: (A|B)"); + + // Verify that the listener was correctly registered + Response response = given() + .queryParam("q", listenerName) + .when().get(registerUrlListener1) + .then().assertThat().statusCode(200) + .extract().response(); + + // Assert that the response contains the expected query param + Assert.assertTrue(response.getBody().asString().contains(listenerName)); // Fails if not found + + listenerName="nonMatchingQueryParam"; + // Verify that the listener search with a no registered listener + response = given() + .queryParam("q", listenerName) + .when().get(registerUrlListener1) + .then().assertThat().statusCode(200) + .extract().response(); + + // Assert that the response contains the expected query param + Assert.assertFalse(response.getBody().asString().contains(listenerName)); // Fails if not found + TestUtils.unregisterListener(registerUrlListener1); async.complete(); } @@ -1019,20 +1051,17 @@ public void testHookHandleSearch_NoListenersRegistered(TestContext context) { String queryParam = "someQuery"; String listenerPath = "/_hooks/listeners"; - String requestUrl = requestUrlBase + listenerPath + "?q=" + queryParam; - - // No listeners registered + String requestUrl = requestUrlBase + listenerPath; - // Send GET request with a query param - given().queryParam("q", queryParam) + // Verify that the listener search with a no registered listener + Response response = given() + .queryParam("q", queryParam) .when().get(requestUrl) - .then().assertThat() - .statusCode(200) // Expecting 200 as the request is valid but no listeners are registered - .body("listeners", org.hamcrest.Matchers.empty()); // Expecting an empty list of listeners - - // Validate that the response is 200 and the result is an empty array - checkGETStatusCodeWithAwait(requestUrl, 200); + .then().assertThat().statusCode(200) + .extract().response(); + // Assert that the response contains the expected query param + Assert.assertFalse(response.getBody().asString().contains(queryParam)); // Fails if not found async.complete(); } @@ -1047,19 +1076,8 @@ public void testHookHandleSearch_ListenerPathInvalidParam(TestContext context) { String listenerPath = "/_hooks/listeners"; String requestUrl = requestUrlBase + listenerPath + "?www=" + queryParam; - // Register a listener - TestUtils.registerListener(requestUrlBase + listenerPath, targetUrlBase, new String[]{"GET", "POST"}, null); - - // Send GET request - given().queryParam("www", queryParam) - .when().get(requestUrl) - .then().assertThat().statusCode(400); - // Validate the response checkGETStatusCodeWithAwait(requestUrl, 400); - - TestUtils.unregisterListener(requestUrlBase + listenerPath); - async.complete(); } @@ -1073,22 +1091,34 @@ public void testHookHandleSearch_NoQueryParameter(TestContext context) { delete(); initRoutingRules(); - String queryParam = ""; - String listenerPath = "/_hooks/listeners"; - String requestUrl = requestUrlBase + listenerPath + "?q=" + queryParam; + // Settings + String subresource = "validQueryParameter"; + String listenerName = ""; - // Register a listener - TestUtils.registerListener(requestUrlBase + listenerPath, targetUrlBase, new String[]{"GET", "POST"}, null); + String registerUrlListener1 = requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix() + listenerName; + String targetListener1 = targetUrlBase + "/" + listenerName; + String[] methodsListener1 = new String[]{"PUT", "DELETE", "POST"}; + final String targetUrlListener1 = targetUrlBase + "/" + listenerName; - // Send GET request - given().queryParam("q", queryParam) - .when().get(requestUrl) - .then().assertThat().statusCode(400); + final String requestUrl = requestUrlBase + "/" + subresource; - // Validate the response - checkGETStatusCodeWithAwait(requestUrl, 400); + delete(requestUrl); + delete(targetUrlListener1); + + //Sending request, one listener hooked + TestUtils.registerListener(registerUrlListener1, targetListener1, methodsListener1, null, null, + null, null, "x-foo: (A|B)"); + + // Verify that the listener was correctly registered + Response response = given() + .queryParam("q", listenerName) + .when().get(registerUrlListener1) + .then().assertThat().statusCode(400) + .extract().response(); - TestUtils.unregisterListener(requestUrlBase + listenerPath); + // Assert that the response contains the expected query param + Assert.assertTrue(response.getBody().asString().contains("Bad Request")); // Fails if not found + TestUtils.unregisterListener(registerUrlListener1); async.complete(); } diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java index 06f27d52..1cd93712 100644 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java @@ -243,7 +243,7 @@ public void testRouteListing_ValidQueryParam(TestContext context) { // Verify that the route was correctly registered Response response = given() .queryParam("q", queryParam) - .when().get(requestUrl + "?q=" + queryParam) + .when().get(requestUrl ) .then().assertThat().statusCode(200) .extract().response(); @@ -274,6 +274,7 @@ public void testRouteListing_NonMatchingQueryParam(TestContext context) { // Register a route using the addRoute method addRoute(queryParam, true, true); assertResponse(get(requestUrlBase), new String[]{queryParam+"/"}); + // Send GET request with a non-matching query param Response response = given().queryParam("q", nonMatchingQueryParam) .when().get(requestUrl) @@ -301,7 +302,7 @@ public void testRouteListing_NoRoutesRegistered(TestContext context) { String queryParam = "someQuery"; String routePath = "/routes"; - String requestUrl = requestUrlBase + routePath + "?q=" + queryParam; + String requestUrl = requestUrlBase + routePath; // No routes registered @@ -311,12 +312,9 @@ public void testRouteListing_NoRoutesRegistered(TestContext context) { .then().assertThat().statusCode(200) .extract().response(); - // Print the body of the response for debugging - System.out.println("Response body: " + response.getBody().asString()); - // Assert that the response body is empty or does not contain routes - Assert.assertTrue("No routes should be registered", - response.getBody().asString().contains("routes")); + Assert.assertFalse("No routes should be registered", + response.getBody().asString().contains(queryParam)); async.complete(); } From 13b99c208630512d3a2347d7ad21a73d8eed76ca Mon Sep 17 00:00:00 2001 From: almeidast Date: Tue, 22 Oct 2024 15:01:30 +0100 Subject: [PATCH 18/37] Fix Url for listeners search --- gateleen-hook/README_hook.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gateleen-hook/README_hook.md b/gateleen-hook/README_hook.md index d2964a9f..fc3b70fe 100644 --- a/gateleen-hook/README_hook.md +++ b/gateleen-hook/README_hook.md @@ -248,7 +248,7 @@ Gateleen allows searching for listeners and routes using the query parameter `q` Search for listeners based on a query parameter like this: ``` -GET http://myserver:7012/gateleen/server/listenertest/_hooks/listeners/listener/1?q=testQuery +GET http://myserver:7012/gateleen/server/listenertest/_hooks/listeners?q=testQuery ``` The response will contain the matching listeners. If no match is found, an empty list is returned: From ad49bda4604ec8c763e3dd2275c08f015e58bc9b Mon Sep 17 00:00:00 2001 From: almeidast Date: Tue, 22 Oct 2024 15:20:00 +0100 Subject: [PATCH 19/37] Add test with more listeners to check if search works as expected --- .../swisspush/gateleen/hook/ListenerTest.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java index 4298ad3a..6baac56a 100755 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java @@ -1123,4 +1123,58 @@ public void testHookHandleSearch_NoQueryParameter(TestContext context) { async.complete(); } + @Test + public void testHookHandleSearch_ReturnsTwoOutOfThreeListeners(TestContext context) { + Async async = context.async(); + delete(); + initRoutingRules(); + + // Settings for the three listeners + String subresource = "multiListenerTest"; + String listenerName1 = "listenerOne"; + String listenerName2 = "listenerTwo"; + String listenerName3 = "NoMatchThree"; + + String registerUrlListener1 = requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix() + listenerName1; + String registerUrlListener2 = requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix() + listenerName2; + String registerUrlListener3 = requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix() + listenerName3; + + String targetListener1 = targetUrlBase + "/" + listenerName1; + String targetListener2 = targetUrlBase + "/" + listenerName2; + String targetListener3 = targetUrlBase + "/" + listenerName3; + + String[] methodsListener = new String[]{"PUT", "DELETE", "POST"}; + + delete(registerUrlListener1); + delete(registerUrlListener2); + delete(registerUrlListener3); + + // Adding the three listeners + TestUtils.registerListener(registerUrlListener1, targetListener1, methodsListener, null, null, + null, null, "x-foo: (A|B)"); + TestUtils.registerListener(registerUrlListener2, targetListener2, methodsListener, null, null, + null, null, "x-foo: (A|B)"); + TestUtils.registerListener(registerUrlListener3, targetListener3, methodsListener, null, null, + null, null, "x-foo: (A|B)"); + // Perform a search for the listeners that should return only listenerOne and listenerTwo + Response response = given() + .queryParam("q", "listener") + .when().get(requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix()) + .then().assertThat().statusCode(200) + .extract().response(); + + // Assert that the search contains listenerOne and listenerTwo but not listenerThree + Assert.assertTrue(response.getBody().asString().contains(listenerName1)); + Assert.assertTrue(response.getBody().asString().contains(listenerName2)); + Assert.assertFalse(response.getBody().asString().contains(listenerName3)); + + // Unregister the listeners + TestUtils.unregisterListener(registerUrlListener1); + TestUtils.unregisterListener(registerUrlListener2); + TestUtils.unregisterListener(registerUrlListener3); + + async.complete(); + } + + } From 8a2a7cf05989d845506a47921bb708a42af96b57 Mon Sep 17 00:00:00 2001 From: almeidast Date: Tue, 22 Oct 2024 16:28:18 +0100 Subject: [PATCH 20/37] Improve tests --- gateleen-hook/README_hook.md | 4 ++-- .../org/swisspush/gateleen/hook/RouteListingTest.java | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/gateleen-hook/README_hook.md b/gateleen-hook/README_hook.md index fc3b70fe..87721086 100644 --- a/gateleen-hook/README_hook.md +++ b/gateleen-hook/README_hook.md @@ -248,7 +248,7 @@ Gateleen allows searching for listeners and routes using the query parameter `q` Search for listeners based on a query parameter like this: ``` -GET http://myserver:7012/gateleen/server/listenertest/_hooks/listeners?q=testQuery +GET http://myserver:7012/gateleen/server/listenertest/_hooks/listeners?q=test ``` The response will contain the matching listeners. If no match is found, an empty list is returned: @@ -273,7 +273,7 @@ The response will contain the matching listeners. If no match is found, an empty Similarly, you can search for routes using a query parameter: ``` -GET http://myserver:7012/gateleen/server/listenertest/_hooks/routes?q=testRoute +GET http://myserver:7012/gateleen/server/routetest/_hooks/routes?q=test ``` The response contains the matching routes, or an empty list if no match is found. diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java index 1cd93712..4bf69279 100644 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java @@ -285,6 +285,16 @@ public void testRouteListing_NonMatchingQueryParam(TestContext context) { Assert.assertFalse("Non-matching query param should not be found in response", response.getBody().asString().contains(nonMatchingQueryParam)); + // Send GET request with a matching query param + response = given().queryParam("q", queryParam) + .when().get(requestUrl) + .then().assertThat().statusCode(200) + .extract().response(); + + // Assert the response contain the matching query param + Assert.assertTrue("matching query param should be found in response", + response.getBody().asString().contains(queryParam)); + // Unregister the route removeRoute(queryParam); From ac9c7207d862618a5fbba69ae783cb01276a052b Mon Sep 17 00:00:00 2001 From: almeidast Date: Tue, 22 Oct 2024 16:46:33 +0100 Subject: [PATCH 21/37] Improve unit tests --- .../gateleen/hook/HookHandlerTest.java | 58 ------------------- 1 file changed, 58 deletions(-) diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index c5e073cc..0fc792bf 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -669,27 +669,6 @@ public void testHandleListenerSearch_Success() { assertTrue(result); // Ensure the handler returns true for a valid listener search verify(response, times(1)).end(); // Ensure `response.end()` was called } - @Test - public void testHandleListenerSearch_MissingQueryParam() { - String uri = "registrations/listeners/?q="; - - HttpServerRequest request = mock(HttpServerRequest.class); - HttpServerResponse response = mock(HttpServerResponse.class); - - when(request.uri()).thenReturn(uri); - when(request.method()).thenReturn(HttpMethod.GET); - when(request.getParam("q")).thenReturn(""); - when(request.response()).thenReturn(response); - when(request.headers()).thenReturn(MultiMap.caseInsensitiveMultiMap()); - - RoutingContext routingContext = mock(RoutingContext.class); - when(routingContext.request()).thenReturn(request); - - boolean result = hookHandler.handle(routingContext); - - assertFalse(result); - verify(response, never()).end(); - } @Test public void testHandleListenerWithStorageAndSearchSuccess(TestContext context) { @@ -732,43 +711,6 @@ public void testHandleListenerWithStorageAndSearchSuccess(TestContext context) { async.complete(); } - @Test - public void testHandleListenerWithStorageAndSearchFailure(TestContext context) { - vertx = Vertx.vertx(); - storage = new MockResourceStorage(); - LoggingResourceManager loggingResourceManager = Mockito.mock(LoggingResourceManager.class); - LogAppenderRepository logAppenderRepository = Mockito.mock(LogAppenderRepository.class); - MonitoringHandler monitoringHandler = Mockito.mock(MonitoringHandler.class); - RequestQueue requestQueue = Mockito.mock(RequestQueue.class); - - hookHandler = new HookHandler(vertx, Mockito.mock(HttpClient.class), storage, loggingResourceManager, logAppenderRepository, monitoringHandler, - "userProfilePath", "hookRootURI/", requestQueue, false, null); - RoutingContext routingContext = Mockito.mock(RoutingContext.class); - HttpServerRequest request = Mockito.mock(HttpServerRequest.class); - HttpServerResponse response = Mockito.mock(HttpServerResponse.class); - - // Mock necessary methods - Mockito.when(request.method()).thenReturn(HttpMethod.GET); - Mockito.when(request.uri()).thenReturn("hookRootURI/registrations/listeners/"); - Mockito.when(request.getParam("q")).thenReturn(null); // No query param - Mockito.when(request.response()).thenReturn(response); - Mockito.when(request.headers()).thenReturn(MultiMap.caseInsensitiveMultiMap()); - - - HttpClient selfClient = Mockito.mock(HttpClient.class); - Mockito.when(selfClient.request(Mockito.any(), Mockito.anyString())).thenReturn(Future.failedFuture(new Exception("Mocked failure"))); - - Mockito.when(routingContext.request()).thenReturn(request); - - // Act - boolean handled = hookHandler.handle(routingContext); - - // Assert - context.assertFalse(handled); - Mockito.verify(response, Mockito.never()).end(); - } - - @Test public void testSearchMultipleListeners_Success(TestContext context) { Vertx vertx = Vertx.vertx(); From 3609204291082e9bc1d456f321701f67bba3d0eb Mon Sep 17 00:00:00 2001 From: almeidast Date: Tue, 22 Oct 2024 17:59:42 +0100 Subject: [PATCH 22/37] improve test url as suggested --- gateleen-hook/README_hook.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gateleen-hook/README_hook.md b/gateleen-hook/README_hook.md index 87721086..c754d133 100644 --- a/gateleen-hook/README_hook.md +++ b/gateleen-hook/README_hook.md @@ -248,7 +248,7 @@ Gateleen allows searching for listeners and routes using the query parameter `q` Search for listeners based on a query parameter like this: ``` -GET http://myserver:7012/gateleen/server/listenertest/_hooks/listeners?q=test +GET http://myserver:7012/playground/server/hooks/v1/registrations/listeners?q=test ``` The response will contain the matching listeners. If no match is found, an empty list is returned: @@ -273,7 +273,7 @@ The response will contain the matching listeners. If no match is found, an empty Similarly, you can search for routes using a query parameter: ``` -GET http://myserver:7012/gateleen/server/routetest/_hooks/routes?q=test +GET http://myserver:7012/playground/server/hooks/v1/registrations/routes/?q=test ``` The response contains the matching routes, or an empty list if no match is found. From 76adc3408fdb2ccd63930eed5c20c6b8e0c501f8 Mon Sep 17 00:00:00 2001 From: almeidast Date: Thu, 24 Oct 2024 14:14:14 +0100 Subject: [PATCH 23/37] move integration testes to the right project improve validation url to preserve the previous functionality --- .../swisspush/gateleen/hook/HookHandler.java | 34 +-- .../gateleen/hook/HookHandlerTest.java | 262 ++---------------- 2 files changed, 41 insertions(+), 255 deletions(-) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index 4fd633a4..867a5cd5 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -158,7 +158,8 @@ public class HookHandler implements LoggableResource { private int routeMultiplier; private final QueueSplitter queueSplitter; - + private final String routeBase; + private final String listenerBase; /** * Creates a new HookHandler. @@ -291,6 +292,8 @@ public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage use this.queueSplitter = queueSplitter; String hookSchema = ResourcesUtils.loadResource("gateleen_hooking_schema_hook", true); jsonSchemaHook = JsonSchemaFactory.getInstance().getSchema(hookSchema); + this.listenerBase = hookRootUri + HOOK_LISTENER_STORAGE_PATH; + this.routeBase = hookRootUri + HOOK_ROUTE_STORAGE_PATH; } public void init() { @@ -385,9 +388,6 @@ private void registerCleanupHandler(Handler readyHandler) { private void loadStoredRoutes(Handler readyHandler) { log.debug("loadStoredRoutes"); - // load the names of the routes from the hookStorage - final String routeBase = hookRootUri + HOOK_ROUTE_STORAGE_PATH; - hookStorage.get(routeBase, buffer -> { if (buffer != null) { JsonObject listOfRoutes = new JsonObject(buffer.toString()); @@ -432,8 +432,6 @@ private void loadStoredRoutes(Handler readyHandler) { private void loadStoredListeners(final Handler readyHandler) { log.debug("loadStoredListeners"); - // load the names of the listener from the hookStorage - final String listenerBase = hookRootUri + HOOK_LISTENER_STORAGE_PATH; hookStorage.get(listenerBase, buffer -> { if (buffer != null) { JsonObject listOfListeners = new JsonObject(buffer.toString()); @@ -575,22 +573,16 @@ public boolean handle(final RoutingContext ctx) { // 1. Check if the request method is GET if (request.method() == HttpMethod.GET) { - String queryParam = request.getParam("q"); - // If the 'q' parameter exists, proceed with search handling - if (queryParam != null) { - // Check if the URI corresponds to listeners or routes - if (requestUri.contains(LISTENERS_KEY)) { + if (!request.params().isEmpty()) { + String queryParam = request.getParam("q"); + String normalizedRequestUri = requestUri.replaceAll("/$", ""); + if (normalizedRequestUri.contains(listenerBase.replaceAll("/$", ""))) { handleListenerSearch(queryParam, request.response()); return true; - } else if (requestUri.contains(ROUTES_KEY)) { + } else if (normalizedRequestUri.contains(routeBase.replaceAll("/$", ""))) { handleRouteSearch(queryParam, request.response()); return true; } - }else{ - if (!request.params().isEmpty()) { - request.response().setStatusCode(400).end("Bad Request: Only the 'q' parameter is allowed"); - return true; - } } } @@ -651,8 +643,8 @@ private void handleRouteSearch(String queryParam, HttpServerResponse response) { */ private void handleSearch(Map repository, Function getDestination, String queryParam, String resultKey, HttpServerResponse response) { - if (repository == null || getDestination == null || resultKey == null || queryParam.isEmpty()) { - response.setStatusCode(400).end("Bad Request: One or more required parameters are missing or null"); + if (repository == null || getDestination == null || resultKey == null || queryParam == null || queryParam.isEmpty()) { + response.setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()).end("Bad Request: One or more required parameters are missing or null"); return; } @@ -671,9 +663,7 @@ private void handleSearch(Map repository, Function get String encodedResult = result.encode(); response.putHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE_JSON); - response.putHeader("Content-Length", String.valueOf(encodedResult.length())); - response.write(encodedResult); - response.end(); + response.end(encodedResult); } diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index 0fc792bf..cb0e1d25 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -647,7 +647,7 @@ public void hookRegistration_RouteWithRouteMultiplier(TestContext testContext) t public void testHandleListenerSearch_Success() { // Arrange String queryParam = "validQueryParam"; - String uri = "registrations/listeners/?q=" + queryParam; + String uri = "hookRootURI/registrations/listeners/?q=" + queryParam; HttpServerRequest request = mock(HttpServerRequest.class); HttpServerResponse response = mock(HttpServerResponse.class); @@ -666,252 +666,48 @@ public void testHandleListenerSearch_Success() { boolean result = hookHandler.handle(routingContext); // Calls the public `handle` method // Assert - assertTrue(result); // Ensure the handler returns true for a valid listener search + assertTrue(result); verify(response, times(1)).end(); // Ensure `response.end()` was called } @Test - public void testHandleListenerWithStorageAndSearchSuccess(TestContext context) { - vertx = Vertx.vertx(); - storage = new MockResourceStorage(); - LoggingResourceManager loggingResourceManager = Mockito.mock(LoggingResourceManager.class); - LogAppenderRepository logAppenderRepository = Mockito.mock(LogAppenderRepository.class); - MonitoringHandler monitoringHandler = Mockito.mock(MonitoringHandler.class); - RequestQueue requestQueue = Mockito.mock(RequestQueue.class); + public void testHandleListenerWithStorageAndSearchSuccess() { + // Arrange + String queryParam = "validQueryParam"; + String uri = "hookRootURI/registrations/listeners/?q=" + queryParam; - hookHandler = new HookHandler(vertx, Mockito.mock(HttpClient.class), storage, loggingResourceManager, logAppenderRepository, monitoringHandler, - "userProfilePath", "hookRootURI/", requestQueue, false, null); - // Prepopulate storage with a listener resource - storage.putMockData("hookRootURI/registrations/listeners/listener1", "{ \"hook\": { \"destination\": \"/test/endpoint\" } }"); + HttpServerRequest request = mock(HttpServerRequest.class); + HttpServerResponse response = mock(HttpServerResponse.class); - // Mock RoutingContext and its behavior - RoutingContext routingContext = Mockito.mock(RoutingContext.class); - HttpServerRequest request = Mockito.mock(HttpServerRequest.class); - HttpServerResponse response = Mockito.mock(HttpServerResponse.class); + // Mock request and response behavior + when(request.uri()).thenReturn(uri); + when(request.method()).thenReturn(HttpMethod.GET); + when(request.getParam("q")).thenReturn(queryParam); + when(request.response()).thenReturn(response); - // Simulate a GET request with a query parameter - Mockito.when(request.method()).thenReturn(HttpMethod.GET); - Mockito.when(request.uri()).thenReturn("hookRootURI/registrations/listeners/?q=test"); - Mockito.when(request.getParam("q")).thenReturn("test"); - Mockito.when(request.response()).thenReturn(response); + // Use ArgumentCaptor para capturar o conteúdo da resposta + ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); - Mockito.when(routingContext.request()).thenReturn(request); + // Mock RoutingContext + RoutingContext routingContext = mock(RoutingContext.class); + when(routingContext.request()).thenReturn(request); - // Async handler to check the result - Async async = context.async(); + // Act + boolean result = hookHandler.handle(routingContext); // Chama o método público `handle` - // Act: Call the hookHandler.handle method - boolean handled = hookHandler.handle(routingContext); + // Assert + assertTrue(result); // Certifique-se de que o handler retornou true - // Assert that it was handled - context.assertTrue(handled); + // Capture o valor passado para end() + verify(response).end(responseCaptor.capture()); - // Verify that the response ended correctly (simulating a successful response) - Mockito.verify(response, Mockito.times(1)).end(); - async.complete(); + // Verifique o conteúdo da resposta + String actualResponse = responseCaptor.getValue(); + assertNotNull(actualResponse); + assertEquals("{\"listeners\":[]}", actualResponse); // Ajuste conforme o comportamento esperado } - @Test - public void testSearchMultipleListeners_Success(TestContext context) { - Vertx vertx = Vertx.vertx(); - MockResourceStorage storage = new MockResourceStorage(); - HttpClient httpClient = vertx.createHttpClient(); - - // Create mock implementation for RequestQueue - RequestQueue requestQueue = Mockito.mock(RequestQueue.class); - - // Initialize HookHandler - String HOOK_LISTENER_STORAGE_PATH = "/_hooks/listeners/"; - HookHandler hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, - logAppenderRepository, monitoringHandler, - "userProfilePath", "/hookRootUri", - requestQueue, false, reducedPropagationManager); - - // Add multiple listeners to the storage - JsonObject listener1 = new JsonObject() - .put("requesturl", "/playground/server/tests/hooktest/_hooks/listeners/http/push/x99") - .put("expirationTime", "2025-01-03T14:15:53.277") - .put("hook", new JsonObject().put("destination", "/playground/server/push/v1/devices/x99")); - JsonObject listener2 = new JsonObject() - .put("requesturl", "/playground/server/tests/hooktest/_hooks/listeners/http/push/x100") - .put("expirationTime", "2025-01-03T14:15:53.277") - .put("hook", new JsonObject().put("destination", "/playground/server/push/v1/devices/x100")); - JsonObject listener3 = new JsonObject() - .put("requesturl", "/playground/server/tests/hooktest/_hooks/listeners/http/push/x101") - .put("expirationTime", "2025-01-03T14:15:53.277") - .put("hook", new JsonObject().put("destination", "/playground/server/push/v1/devices/x101")); - - storage.putMockData(HOOK_LISTENER_STORAGE_PATH + "x99", listener1.encode()); - storage.putMockData(HOOK_LISTENER_STORAGE_PATH + "x100", listener2.encode()); - storage.putMockData(HOOK_LISTENER_STORAGE_PATH + "x101", listener3.encode()); - - // Configure HttpServer for integration test - io.vertx.ext.web.Router router = io.vertx.ext.web.Router.router(vertx); - router.route().handler(hookHandler::handle); - - HttpServer server = vertx.createHttpServer(); - server.requestHandler(router).listen(8080, ar -> { - if (ar.succeeded()) { - // Make a real HTTP request to the HookHandler - httpClient.request(HttpMethod.GET, 8080, "localhost", "/_hooks/listeners?q=x99") - .compose(HttpClientRequest::send) - .compose(response -> { - context.assertEquals(200, response.statusCode()); - return response.body(); - }) - .onSuccess(body -> { - JsonObject jsonResponse = new JsonObject(body.toString()); - context.assertTrue(jsonResponse.getJsonArray("listeners").contains("x99")); - context.assertFalse(jsonResponse.getJsonArray("listeners").contains("x100")); - context.async().complete(); - }) - .onFailure(context::fail); - } else { - context.fail(ar.cause()); - } - }); - } - @Test - public void testSearchSingleListener_Success(TestContext context) { - Vertx vertx = Vertx.vertx(); - MockResourceStorage storage = new MockResourceStorage(); - HttpClient httpClient = vertx.createHttpClient(); - - // Create mock implementation for RequestQueue - RequestQueue requestQueue = Mockito.mock(RequestQueue.class); - - // Initialize HookHandler - HookHandler hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, - logAppenderRepository, monitoringHandler, - "userProfilePath", "/hookRootUri/", - requestQueue, false, reducedPropagationManager); - - // Insert a single listener to the storage - JsonObject listener1 = new JsonObject() - .put("requesturl", "/playground/server/tests/hooktest/_hooks/listeners/http/push/listener1") - .put("expirationTime", "2025-01-03T14:15:53.277") - .put("hook", new JsonObject().put("destination", "/playground/server/push/v1/devices/listener1")); - storage.putMockData("/_hooks/listeners/listener1", listener1.encode()); - - // Configure HttpServer for integration test - io.vertx.ext.web.Router router = io.vertx.ext.web.Router.router(vertx); - router.route().handler(hookHandler::handle); - - HttpServer server = vertx.createHttpServer(); - server.requestHandler(router).listen(8080, ar -> { - if (ar.succeeded()) { - // Make a real HTTP request to the HookHandler - httpClient.request(HttpMethod.GET, 8080, "localhost", "/_hooks/listeners?q=listener1") - .compose(HttpClientRequest::send) - .compose(response -> { - context.assertEquals(200, response.statusCode()); - return response.body(); - }) - .onSuccess(body -> { - JsonObject jsonResponse = new JsonObject(body.toString()); - context.assertTrue(jsonResponse.getJsonArray("listeners").contains("listener1")); - context.async().complete(); - }) - .onFailure(context::fail); - } else { - context.fail(ar.cause()); - } - }); - } - @Test - public void testSearchListenerNotFound_MultipleListeners_Failure(TestContext context) { - Vertx vertx = Vertx.vertx(); - MockResourceStorage storage = new MockResourceStorage(); - HttpClient httpClient = vertx.createHttpClient(); - - // Create mock implementation for RequestQueue - RequestQueue requestQueue = Mockito.mock(RequestQueue.class); - - // Initialize HookHandler - String HOOK_LISTENER_STORAGE_PATH = "/_hooks/listeners/"; - HookHandler hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, - logAppenderRepository, monitoringHandler, - "userProfilePath", "/hookRootUri", - requestQueue, false, reducedPropagationManager); - - // Add multiple listeners to the storage - JsonObject listener2 = new JsonObject() - .put("requesturl", "/playground/server/tests/hooktest/_hooks/listeners/http/push/x100") - .put("expirationTime", "2025-01-03T14:15:53.277") - .put("hook", new JsonObject().put("destination", "/playground/server/push/v1/devices/x100")); - JsonObject listener3 = new JsonObject() - .put("requesturl", "/playground/server/tests/hooktest/_hooks/listeners/http/push/x101") - .put("expirationTime", "2025-01-03T14:15:53.277") - .put("hook", new JsonObject().put("destination", "/playground/server/push/v1/devices/x101")); - - storage.putMockData(HOOK_LISTENER_STORAGE_PATH + "x100", listener2.encode()); - storage.putMockData(HOOK_LISTENER_STORAGE_PATH + "x101", listener3.encode()); - - // Configure HttpServer for integration test - io.vertx.ext.web.Router router = io.vertx.ext.web.Router.router(vertx); - router.route().handler(hookHandler::handle); - - HttpServer server = vertx.createHttpServer(); - server.requestHandler(router).listen(8080, ar -> { - if (ar.succeeded()) { - // Make a real HTTP request to the HookHandler - httpClient.request(HttpMethod.GET, 8080, "localhost", "/_hooks/listeners?q=x99") - .compose(HttpClientRequest::send) - .compose(response -> { - context.assertEquals(200, response.statusCode()); - return response.body(); - }) - .onSuccess(body -> { - JsonObject jsonResponse = new JsonObject(body.toString()); - context.assertFalse(jsonResponse.getJsonArray("listeners").contains("x99")); - context.async().complete(); - }) - .onFailure(context::fail); - } else { - context.fail(ar.cause()); - } - }); - } - @Test - public void testSearchListenerNotFound_NoListeners_Failure(TestContext context) { - Vertx vertx = Vertx.vertx(); - MockResourceStorage storage = new MockResourceStorage(); - HttpClient httpClient = vertx.createHttpClient(); - - // Create mock implementation for RequestQueue - RequestQueue requestQueue = Mockito.mock(RequestQueue.class); - - // Initialize HookHandler - HookHandler hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, - logAppenderRepository, monitoringHandler, - "userProfilePath", "/hookRootUri/", - requestQueue, false, reducedPropagationManager); - - // Configure HttpServer for integration test - io.vertx.ext.web.Router router = io.vertx.ext.web.Router.router(vertx); - router.route().handler(hookHandler::handle); - - HttpServer server = vertx.createHttpServer(); - server.requestHandler(router).listen(8080, ar -> { - if (ar.succeeded()) { - // Make a real HTTP request to the HookHandler - httpClient.request(HttpMethod.GET, 8080, "localhost", "/_hooks/listeners?q=invalid") - .compose(HttpClientRequest::send) - .compose(response -> { - context.assertEquals(200, response.statusCode()); - return response.body(); - }) - .onSuccess(body -> { - JsonObject jsonResponse = new JsonObject(body.toString()); - context.assertFalse(jsonResponse.getJsonArray("listeners").contains("invalid")); - context.async().complete(); - }) - .onFailure(context::fail); - } else { - context.fail(ar.cause()); - } - }); - } + /////////////////////////////////////////////////////////////////////////////// // Helpers From 8096031af5e9bb4cca53fe234ec4006edcdd7835 Mon Sep 17 00:00:00 2001 From: almeidast Date: Mon, 28 Oct 2024 11:42:04 +0000 Subject: [PATCH 24/37] improve integration tests --- .../swisspush/gateleen/hook/ListenerTest.java | 35 +++++++++---------- .../gateleen/hook/RouteListingTest.java | 30 +++++++++------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java index 6baac56a..8565141e 100755 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java @@ -43,6 +43,7 @@ public class ListenerTest extends AbstractTest { private final static int WIREMOCK_PORT = 8881; private String requestUrlBase; private String targetUrlBase; + private String searchUrlBase; @Rule public WireMockRule wireMockRule = new WireMockRule(WIREMOCK_PORT); @@ -56,6 +57,8 @@ public void initRestAssured() { requestUrlBase = "/tests/gateleen/monitoredresource"; targetUrlBase = "http://localhost:" + MAIN_PORT + SERVER_ROOT + "/tests/gateleen/targetresource"; + searchUrlBase = "http://localhost:" + MAIN_PORT + SERVER_ROOT + "/hooks/v1/registrations/listeners"; + } /** @@ -956,17 +959,16 @@ public void testHookHandleSearch_ListenerPathWithValidQueryParam(TestContext con initRoutingRules(); // Settings - String subresource = "validQueryParameter"; + String subresource = "matchingQueryParam"; String listenerName = "myListener"; - String registerUrlListener1 = requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix() + listenerName; + String registerUrlListener1 = requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix() + listenerName; String targetListener1 = targetUrlBase + "/" + listenerName; String[] methodsListener1 = new String[]{"PUT", "DELETE", "POST"}; final String targetUrlListener1 = targetUrlBase + "/" + listenerName; - final String requestUrl = requestUrlBase + "/" + subresource; - delete(requestUrl); + delete(searchUrlBase); delete(targetUrlListener1); //Sending request, one listener hooked @@ -976,11 +978,12 @@ public void testHookHandleSearch_ListenerPathWithValidQueryParam(TestContext con // Verify that the listener was correctly registered Response response = given() .queryParam("q", listenerName) - .when().get(registerUrlListener1) + .when().get(searchUrlBase) .then().assertThat().statusCode(200) .extract().response(); // Assert that the response contains the expected query param + System.out.println(response.getBody().asString()); Assert.assertTrue(response.getBody().asString().contains(listenerName)); // Fails if not found TestUtils.unregisterListener(registerUrlListener1); @@ -1006,9 +1009,8 @@ public void testHookHandleSearch_ListenerPathWithNonMatchingQueryParam(TestConte String[] methodsListener1 = new String[]{"PUT", "DELETE", "POST"}; final String targetUrlListener1 = targetUrlBase + "/" + listenerName + "/" + "test"; - final String requestUrl = requestUrlBase + "/" + subresource + "/" + "test"; - delete(requestUrl); + delete(searchUrlBase); delete(targetUrlListener1); TestUtils.registerListener(registerUrlListener1, targetListener1, methodsListener1, null, null, @@ -1017,7 +1019,7 @@ public void testHookHandleSearch_ListenerPathWithNonMatchingQueryParam(TestConte // Verify that the listener was correctly registered Response response = given() .queryParam("q", listenerName) - .when().get(registerUrlListener1) + .when().get(searchUrlBase) .then().assertThat().statusCode(200) .extract().response(); @@ -1028,7 +1030,7 @@ public void testHookHandleSearch_ListenerPathWithNonMatchingQueryParam(TestConte // Verify that the listener search with a no registered listener response = given() .queryParam("q", listenerName) - .when().get(registerUrlListener1) + .when().get(searchUrlBase) .then().assertThat().statusCode(200) .extract().response(); @@ -1050,13 +1052,12 @@ public void testHookHandleSearch_NoListenersRegistered(TestContext context) { initRoutingRules(); String queryParam = "someQuery"; - String listenerPath = "/_hooks/listeners"; - String requestUrl = requestUrlBase + listenerPath; + String listenerPath = "/hooks/listeners"; // Verify that the listener search with a no registered listener Response response = given() .queryParam("q", queryParam) - .when().get(requestUrl) + .when().get(searchUrlBase) .then().assertThat().statusCode(200) .extract().response(); @@ -1074,7 +1075,7 @@ public void testHookHandleSearch_ListenerPathInvalidParam(TestContext context) { String queryParam = "testQuery"; String listenerPath = "/_hooks/listeners"; - String requestUrl = requestUrlBase + listenerPath + "?www=" + queryParam; + String requestUrl = searchUrlBase+ "?www=" + queryParam; // Validate the response checkGETStatusCodeWithAwait(requestUrl, 400); @@ -1100,9 +1101,7 @@ public void testHookHandleSearch_NoQueryParameter(TestContext context) { String[] methodsListener1 = new String[]{"PUT", "DELETE", "POST"}; final String targetUrlListener1 = targetUrlBase + "/" + listenerName; - final String requestUrl = requestUrlBase + "/" + subresource; - - delete(requestUrl); + delete(searchUrlBase); delete(targetUrlListener1); //Sending request, one listener hooked @@ -1112,7 +1111,7 @@ public void testHookHandleSearch_NoQueryParameter(TestContext context) { // Verify that the listener was correctly registered Response response = given() .queryParam("q", listenerName) - .when().get(registerUrlListener1) + .when().get(searchUrlBase) .then().assertThat().statusCode(400) .extract().response(); @@ -1159,7 +1158,7 @@ public void testHookHandleSearch_ReturnsTwoOutOfThreeListeners(TestContext conte // Perform a search for the listeners that should return only listenerOne and listenerTwo Response response = given() .queryParam("q", "listener") - .when().get(requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix()) + .when().get(searchUrlBase) .then().assertThat().statusCode(200) .extract().response(); diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java index 4bf69279..6fe83dee 100644 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java @@ -27,7 +27,7 @@ public class RouteListingTest extends AbstractTest { private String requestUrlBase; private String targetUrlBase; private String parentKey; - + private String searchUrlBase; /** * Overwrite RestAssured configuration @@ -39,6 +39,7 @@ public void initRestAssured() { parentKey = "routesource"; requestUrlBase = "/tests/gateleen/" + parentKey; targetUrlBase = "http://localhost:" + MAIN_PORT + SERVER_ROOT + "/tests/gateleen/routetarget"; + searchUrlBase = "http://localhost:" + MAIN_PORT + SERVER_ROOT + "/hooks/v1/registrations/routes"; } @@ -236,14 +237,14 @@ public void testRouteListing_ValidQueryParam(TestContext context) { String queryParam = "routeTests"; String routePath = "/routes"; - String requestUrl = requestUrlBase + routePath; + addRoute(queryParam, true, true); // Verify that the route was correctly registered Response response = given() .queryParam("q", queryParam) - .when().get(requestUrl ) + .when().get(searchUrlBase ) .then().assertThat().statusCode(200) .extract().response(); @@ -268,16 +269,23 @@ public void testRouteListing_NonMatchingQueryParam(TestContext context) { String nonMatchingQueryParam = "nonMatchingQuery"; String queryParam = "other"; - String routePath = "/routes"; - String requestUrl = requestUrlBase + routePath; // Register a route using the addRoute method addRoute(queryParam, true, true); assertResponse(get(requestUrlBase), new String[]{queryParam+"/"}); // Send GET request with a non-matching query param - Response response = given().queryParam("q", nonMatchingQueryParam) - .when().get(requestUrl) + Response response = given().queryParam("q", queryParam) + .when().get(searchUrlBase) + .then().assertThat().statusCode(200) + .extract().response(); + + Assert.assertTrue("Query param should be found in response", + response.getBody().asString().contains(queryParam)); + + // Send GET request with a non-matching query param + response = given().queryParam("q", nonMatchingQueryParam) + .when().get(searchUrlBase) .then().assertThat().statusCode(200) .extract().response(); @@ -287,7 +295,7 @@ public void testRouteListing_NonMatchingQueryParam(TestContext context) { // Send GET request with a matching query param response = given().queryParam("q", queryParam) - .when().get(requestUrl) + .when().get(searchUrlBase) .then().assertThat().statusCode(200) .extract().response(); @@ -311,14 +319,10 @@ public void testRouteListing_NoRoutesRegistered(TestContext context) { initSettings(); // Initialize routing rules String queryParam = "someQuery"; - String routePath = "/routes"; - String requestUrl = requestUrlBase + routePath; - // No routes registered - // Send GET request with a query param Response response = given().queryParam("q", queryParam) - .when().get(requestUrl) + .when().get(searchUrlBase) .then().assertThat().statusCode(200) .extract().response(); From 775704d2770229721b2a975efbdf486b9002ec24 Mon Sep 17 00:00:00 2001 From: almeidast Date: Mon, 28 Oct 2024 14:40:56 +0000 Subject: [PATCH 25/37] Improve and optimize the code --- .../swisspush/gateleen/hook/HookHandler.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index 867a5cd5..a313279d 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -74,8 +74,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import static io.vertx.core.http.HttpMethod.DELETE; -import static io.vertx.core.http.HttpMethod.PUT; +import static io.vertx.core.http.HttpMethod.*; import static org.swisspush.gateleen.core.util.HttpRequestHeader.CONTENT_LENGTH; /** @@ -572,17 +571,15 @@ public boolean handle(final RoutingContext ctx) { } // 1. Check if the request method is GET - if (request.method() == HttpMethod.GET) { - if (!request.params().isEmpty()) { - String queryParam = request.getParam("q"); - String normalizedRequestUri = requestUri.replaceAll("/$", ""); - if (normalizedRequestUri.contains(listenerBase.replaceAll("/$", ""))) { - handleListenerSearch(queryParam, request.response()); - return true; - } else if (normalizedRequestUri.contains(routeBase.replaceAll("/$", ""))) { - handleRouteSearch(queryParam, request.response()); - return true; - } + if (requestMethod == GET && !request.params().isEmpty()) { + String queryParam = request.getParam("q"); + String normalizedRequestUri = requestUri.replaceAll("/$", ""); + if (normalizedRequestUri.contains(listenerBase.replaceAll("/$", ""))) { + handleListenerSearch(queryParam, request.response()); + return true; + } else if (normalizedRequestUri.contains(routeBase.replaceAll("/$", ""))) { + handleRouteSearch(queryParam, request.response()); + return true; } } From 97cbb6e3d0e1cd2dd846bf260357fc938ffb294b Mon Sep 17 00:00:00 2001 From: almeidast Date: Mon, 28 Oct 2024 23:16:30 +0000 Subject: [PATCH 26/37] Create new unit tests --- .../gateleen/hook/HookHandlerTest.java | 239 ++++++++++++++---- 1 file changed, 193 insertions(+), 46 deletions(-) diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index cb0e1d25..149ddf57 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -1,10 +1,12 @@ package org.swisspush.gateleen.hook; +import io.netty.handler.codec.http.QueryStringDecoder; import io.vertx.codegen.annotations.Nullable; import io.vertx.core.*; import io.vertx.core.buffer.Buffer; import io.vertx.core.buffer.impl.BufferImpl; import io.vertx.core.http.*; +import io.vertx.core.http.impl.headers.HeadersMultiMap; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.core.net.HostAndPort; @@ -17,11 +19,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.swisspush.gateleen.core.http.*; import org.swisspush.gateleen.core.storage.MockResourceStorage; +import org.swisspush.gateleen.core.util.StatusCode; import org.swisspush.gateleen.hook.reducedpropagation.ReducedPropagationManager; import org.swisspush.gateleen.logging.LogAppenderRepository; import org.swisspush.gateleen.logging.LoggingResourceManager; @@ -52,6 +56,8 @@ @RunWith(VertxUnitRunner.class) public class HookHandlerTest { + public static final String CONTENT_TYPE_HEADER = "Content-Type"; + public static final String CONTENT_TYPE_JSON = "application/json"; private static final String HOOK_ROOT_URI = "hookRootURI/"; private static final Logger logger = LoggerFactory.getLogger(HookHandlerTest.class); private Vertx vertx; @@ -80,10 +86,8 @@ public void setUp() { monitoringHandler = mock(MonitoringHandler.class); requestQueue = mock(RequestQueue.class); reducedPropagationManager = mock(ReducedPropagationManager.class); - - - hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, logAppenderRepository, monitoringHandler, - "userProfilePath", HOOK_ROOT_URI, requestQueue, false, reducedPropagationManager); + hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, logAppenderRepository, + monitoringHandler, "userProfilePath", "hookRootURI/", requestQueue, false, reducedPropagationManager); hookHandler.init(); } @@ -644,69 +648,181 @@ public void hookRegistration_RouteWithRouteMultiplier(TestContext testContext) t } @Test - public void testHandleListenerSearch_Success() { - // Arrange - String queryParam = "validQueryParam"; - String uri = "hookRootURI/registrations/listeners/?q=" + queryParam; + public void testHandleGETRequestWithRouteSearch() { + // Mocking necessary components + HttpServerRequest mockRequest = mock(HttpServerRequest.class); + HttpServerResponse mockResponse = mock(HttpServerResponse.class); + RoutingContext routingContext = mock(RoutingContext.class); - HttpServerRequest request = mock(HttpServerRequest.class); - HttpServerResponse response = mock(HttpServerResponse.class); + // Setting up a real MultiMap to simulate parameters + MultiMap params = MultiMap.caseInsensitiveMultiMap(); + params.add("q", "routeId"); - // Mock request and response behavior - when(request.uri()).thenReturn(uri); - when(request.method()).thenReturn(HttpMethod.GET); - when(request.getParam("q")).thenReturn(queryParam); - when(request.response()).thenReturn(response); + // Configuring mocks + when(routingContext.request()).thenReturn(mockRequest); + when(mockRequest.method()).thenReturn(HttpMethod.GET); + when(mockRequest.uri()).thenReturn("/hookRootURI/registrations/routes"); + when(mockRequest.response()).thenReturn(mockResponse); + when(mockRequest.params()).thenReturn(params); - // Mock RoutingContext - RoutingContext routingContext = mock(RoutingContext.class); - when(routingContext.request()).thenReturn(request); + // Capture status code and response from mockResponse + ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); + when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); + when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); - // Act - boolean result = hookHandler.handle(routingContext); // Calls the public `handle` method + // Execute the method + boolean result = hookHandler.handle(routingContext); - // Assert + // Verifications + verify(mockRequest, times(1)).method(); + verify(mockRequest, times(1)).uri(); assertTrue(result); - verify(response, times(1)).end(); // Ensure `response.end()` was called + + // Check the 'q' parameter value and expected status code + assertEquals("routeId", params.get("q")); + verify(mockResponse).setStatusCode(200); + + // Verify the captured JSON response content + String jsonResponse = responseCaptor.getValue(); + assertNotNull(jsonResponse); + assertTrue(jsonResponse.contains("\"routes\":")); + + // Additional debug output + System.out.println("Parameter 'q' in params: " + params.get("q")); + System.out.println("Response body: " + jsonResponse); } @Test - public void testHandleListenerWithStorageAndSearchSuccess() { - // Arrange - String queryParam = "validQueryParam"; - String uri = "hookRootURI/registrations/listeners/?q=" + queryParam; + public void testHandleGETRequestWithoutSearchParams() { + // Mocking necessary components + HttpServerRequest mockRequest = mock(HttpServerRequest.class); + HttpServerResponse mockResponse = mock(HttpServerResponse.class); + RoutingContext routingContext = mock(RoutingContext.class); - HttpServerRequest request = mock(HttpServerRequest.class); - HttpServerResponse response = mock(HttpServerResponse.class); + // Setting up an empty MultiMap to simulate no parameters + MultiMap params = MultiMap.caseInsensitiveMultiMap(); - // Mock request and response behavior - when(request.uri()).thenReturn(uri); - when(request.method()).thenReturn(HttpMethod.GET); - when(request.getParam("q")).thenReturn(queryParam); - when(request.response()).thenReturn(response); + // Configuring mocks + when(routingContext.request()).thenReturn(mockRequest); + when(mockRequest.method()).thenReturn(HttpMethod.GET); + when(mockRequest.uri()).thenReturn("/hookRootURI/registrations/listeners"); + when(mockRequest.response()).thenReturn(mockResponse); + when(mockRequest.params()).thenReturn(params); - // Use ArgumentCaptor para capturar o conteúdo da resposta + // Capture status code and response from mockResponse ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); + when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); + when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); + + // Execute the method + boolean result = hookHandler.handle(routingContext); + + // Verifications + verify(mockRequest, times(1)).method(); + verify(mockRequest, times(1)).uri(); + assertTrue(result); + + // Check that the parameters MultiMap is empty + assertTrue(params.isEmpty()); + verify(mockResponse).setStatusCode(400); - // Mock RoutingContext + // Verify captured response content + String jsonResponse = responseCaptor.getValue(); + assertNotNull(jsonResponse); + assertTrue(jsonResponse.contains("Bad Request")); + + // Additional debug output + System.out.println("Params is empty: " + params.isEmpty()); + System.out.println("Response body: " + jsonResponse); + } + + @Test + public void testHandleGETRequestWithInvalidParam() { + // Mocking necessary components + HttpServerRequest mockRequest = mock(HttpServerRequest.class); + HttpServerResponse mockResponse = mock(HttpServerResponse.class); RoutingContext routingContext = mock(RoutingContext.class); - when(routingContext.request()).thenReturn(request); - // Act - boolean result = hookHandler.handle(routingContext); // Chama o método público `handle` + // Setting up a real MultiMap to simulate an invalid parameter + MultiMap params = MultiMap.caseInsensitiveMultiMap(); + params.add("invalid", "value"); + + // Configuring mocks + when(routingContext.request()).thenReturn(mockRequest); + when(mockRequest.method()).thenReturn(HttpMethod.GET); + when(mockRequest.uri()).thenReturn("/hookRootURI/registrations/listeners"); + when(mockRequest.response()).thenReturn(mockResponse); + when(mockRequest.params()).thenReturn(params); + + // Capture status code and response from mockResponse + ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); + when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); + when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); + + // Execute the method + boolean result = hookHandler.handle(routingContext); + + // Verifications + verify(mockRequest, times(1)).method(); + verify(mockRequest, times(1)).uri(); + assertTrue(result); - // Assert - assertTrue(result); // Certifique-se de que o handler retornou true + // Expecting status code 400 due to invalid parameter + verify(mockResponse).setStatusCode(400); - // Capture o valor passado para end() - verify(response).end(responseCaptor.capture()); + // Verify captured response content + String jsonResponse = responseCaptor.getValue(); + assertNotNull(jsonResponse); + assertTrue(jsonResponse.contains("Bad Request")); - // Verifique o conteúdo da resposta - String actualResponse = responseCaptor.getValue(); - assertNotNull(actualResponse); - assertEquals("{\"listeners\":[]}", actualResponse); // Ajuste conforme o comportamento esperado + // Additional debug output + System.out.println("Invalid params: " + params); + System.out.println("Response body: " + jsonResponse); } + @Test + public void testHandleGETRequestWithEmptyParam() { + // Mocking necessary components + HttpServerRequest mockRequest = mock(HttpServerRequest.class); + HttpServerResponse mockResponse = mock(HttpServerResponse.class); + RoutingContext routingContext = mock(RoutingContext.class); + + // Setting up a real MultiMap to simulate an empty 'q' parameter + MultiMap params = MultiMap.caseInsensitiveMultiMap(); + params.add("q", ""); + + // Configuring mocks + when(routingContext.request()).thenReturn(mockRequest); + when(mockRequest.method()).thenReturn(HttpMethod.GET); + when(mockRequest.uri()).thenReturn("/hookRootURI/registrations/listeners"); + when(mockRequest.response()).thenReturn(mockResponse); + when(mockRequest.params()).thenReturn(params); + + // Capture status code and response from mockResponse + ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); + when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); + when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); + + // Execute the method + boolean result = hookHandler.handle(routingContext); + + // Verifications + verify(mockRequest, times(1)).method(); + verify(mockRequest, times(1)).uri(); + assertTrue(result); + + // Expecting status code 400 due to empty 'q' parameter + verify(mockResponse).setStatusCode(400); + + // Verify captured response content + String jsonResponse = responseCaptor.getValue(); + assertNotNull(jsonResponse); + assertTrue(jsonResponse.contains("Bad Request")); + + // Additional debug output + System.out.println("Empty 'q' param: " + params.get("q")); + System.out.println("Response body: " + jsonResponse); + } /////////////////////////////////////////////////////////////////////////////// @@ -981,4 +1097,35 @@ public void addHeader(String headerName, String headerValue) { headers.add(headerName, headerValue); } } + private static class Request extends DummyHttpServerRequest { + private MultiMap headers; + private HttpMethod httpMethod; + private String uri; + private HttpServerResponse response; + + public Request(HttpMethod httpMethod, String uri, MultiMap headers, HttpServerResponse response) { + this.httpMethod = httpMethod; + this.uri = uri; + this.headers = headers; + this.response = response; + } + + @Override public HttpMethod method() { + return httpMethod; + } + @Override public String uri() { + return uri; + } + @Override public HttpServerResponse response() { return response; } + @Override public MultiMap headers() { return headers; } + @Override public HttpServerRequest pause() { return this; } + @Override public HttpServerRequest resume() { return this; } + } + + private static class Response extends DummyHttpServerResponse { + private MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + + @Override + public MultiMap headers() { return headers; } + } } From db98244884b5edfec45ac758d4b5667c285b636e Mon Sep 17 00:00:00 2001 From: almeidast Date: Tue, 29 Oct 2024 16:05:38 +0000 Subject: [PATCH 27/37] improve and implement new unit testes to cover new hookHandlerSearch create a buildRouteConfig create GETRequest --- .../gateleen/hook/HookHandlerTest.java | 358 +++++++++++------- 1 file changed, 227 insertions(+), 131 deletions(-) diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index 149ddf57..ce47578c 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -1,12 +1,10 @@ package org.swisspush.gateleen.hook; -import io.netty.handler.codec.http.QueryStringDecoder; import io.vertx.codegen.annotations.Nullable; import io.vertx.core.*; import io.vertx.core.buffer.Buffer; import io.vertx.core.buffer.impl.BufferImpl; import io.vertx.core.http.*; -import io.vertx.core.http.impl.headers.HeadersMultiMap; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.core.net.HostAndPort; @@ -19,7 +17,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,13 +31,11 @@ import org.swisspush.gateleen.queue.queuing.RequestQueue; import org.swisspush.gateleen.routing.Router; -import java.lang.reflect.Field; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; -import java.util.Map; import static io.vertx.core.http.HttpMethod.PUT; import static org.junit.Assert.*; @@ -55,9 +50,6 @@ */ @RunWith(VertxUnitRunner.class) public class HookHandlerTest { - - public static final String CONTENT_TYPE_HEADER = "Content-Type"; - public static final String CONTENT_TYPE_JSON = "application/json"; private static final String HOOK_ROOT_URI = "hookRootURI/"; private static final Logger logger = LoggerFactory.getLogger(HookHandlerTest.class); private Vertx vertx; @@ -118,7 +110,28 @@ private JsonObject buildListenerConfig(JsonObject queueingStrategy, String devic return config; } - private JsonObject buildListenerConfigWithHeadersFilter(JsonObject queueingStrategy, String deviceId, String headersFilter){ + private JsonObject buildRouteConfig(String routeId) { + JsonObject config = new JsonObject(); + config.put("requesturl", "/playground/server/tests/"+ routeId+"/_hooks/routes/http/push/" ); + config.put("expirationTime", "2017-01-03T14:15:53.277"); + + JsonObject hook = new JsonObject(); + hook.put("destination", "/playground/server/push/v1/routes/" + routeId); + hook.put("methods", new JsonArray(Collections.singletonList("PUT"))); + hook.put("timeout", 42); + hook.put("connectionPoolSize", 10); + JsonObject staticHeaders = new JsonObject(); + staticHeaders.put("x-custom-header", "route-header-value"); + hook.put("staticHeaders", staticHeaders); + config.put("hook", hook); + return config; + } + private void setRouteStorageEntryAndTriggerUpdate(JsonObject routeConfig) { + storage.putMockData("pathToRouteResource", routeConfig.encode()); + vertx.eventBus().request("gateleen.hook-route-insert", "pathToRouteResource"); + } + + private JsonObject buildListenerConfigWithHeadersFilter(JsonObject queueingStrategy, String deviceId, String headersFilter) { JsonObject config = buildListenerConfig(queueingStrategy, deviceId); config.getJsonObject("hook").put("headersFilter", headersFilter); return config; @@ -400,13 +413,13 @@ public void hookRegistration_RouteWithTimeout(TestContext testContext) { final MultiMap requestHeaders = MultiMap.caseInsensitiveMultiMap(); final Buffer requestBody = new BufferImpl(); requestBody.setBytes(0, ("{" + - " \"methods\": [ \"PUT\" , \"DELETE\" ]," + - " \"destination\": \"/an/example/destination/\"," + - " \"timeout\": 42" + - "}").getBytes()); + " \"methods\": [ \"PUT\" , \"DELETE\" ]," + + " \"destination\": \"/an/example/destination/\"," + + " \"timeout\": 42" + + "}").getBytes()); request = createSimpleRequest(HttpMethod.PUT, "/gateleen/example/_hooks/route/http/my-service/my-hook", - requestHeaders, requestBody, statusCodePtr, statusMessagePtr + requestHeaders, requestBody, statusCodePtr, statusMessagePtr ); } @@ -647,25 +660,20 @@ public void hookRegistration_RouteWithRouteMultiplier(TestContext testContext) t vertx.eventBus().request("gateleen.hook-route-remove", "pathToRouterResource"); } + @Test - public void testHandleGETRequestWithRouteSearch() { - // Mocking necessary components - HttpServerRequest mockRequest = mock(HttpServerRequest.class); + public void testHandleGETRequestWithEmptyParam() { + // Define URI and configures the request with an empty 'q' parameter + String uri = "/hookRootURI/registrations/listeners"; HttpServerResponse mockResponse = mock(HttpServerResponse.class); - RoutingContext routingContext = mock(RoutingContext.class); - - // Setting up a real MultiMap to simulate parameters - MultiMap params = MultiMap.caseInsensitiveMultiMap(); - params.add("q", "routeId"); + GETRequest request = new GETRequest(uri, mockResponse); + request.addParameter("q", ""); // Empty parameter to simulate bad request - // Configuring mocks - when(routingContext.request()).thenReturn(mockRequest); - when(mockRequest.method()).thenReturn(HttpMethod.GET); - when(mockRequest.uri()).thenReturn("/hookRootURI/registrations/routes"); - when(mockRequest.response()).thenReturn(mockResponse); - when(mockRequest.params()).thenReturn(params); + // Mock RoutingContext + RoutingContext routingContext = mock(RoutingContext.class); + when(routingContext.request()).thenReturn(request); - // Capture status code and response from mockResponse + // Capture response content ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); @@ -674,157 +682,173 @@ public void testHandleGETRequestWithRouteSearch() { boolean result = hookHandler.handle(routingContext); // Verifications - verify(mockRequest, times(1)).method(); - verify(mockRequest, times(1)).uri(); - assertTrue(result); - - // Check the 'q' parameter value and expected status code - assertEquals("routeId", params.get("q")); - verify(mockResponse).setStatusCode(200); + verify(mockResponse).setStatusCode(400); // Verify status 400 due to empty 'q' parameter + assertTrue(result); // Ensure the handler returned true - // Verify the captured JSON response content + // Verify captured response content String jsonResponse = responseCaptor.getValue(); assertNotNull(jsonResponse); - assertTrue(jsonResponse.contains("\"routes\":")); - - // Additional debug output - System.out.println("Parameter 'q' in params: " + params.get("q")); - System.out.println("Response body: " + jsonResponse); + assertTrue(jsonResponse.contains("Bad Request")); // Confirm the response contains "Bad Request" } + @Test - public void testHandleGETRequestWithoutSearchParams() { - // Mocking necessary components - HttpServerRequest mockRequest = mock(HttpServerRequest.class); + public void testHandleGETRequestWithListenersSearchSingleResult() throws InterruptedException { + // Define URI and configure GET request with specific search parameter + String singleListener= "mySingleListener"; + String uri = "/hookRootURI/registrations/listeners"; HttpServerResponse mockResponse = mock(HttpServerResponse.class); - RoutingContext routingContext = mock(RoutingContext.class); + GETRequest request = new GETRequest(uri, mockResponse); + request.addParameter("q", singleListener); - // Setting up an empty MultiMap to simulate no parameters - MultiMap params = MultiMap.caseInsensitiveMultiMap(); - // Configuring mocks - when(routingContext.request()).thenReturn(mockRequest); - when(mockRequest.method()).thenReturn(HttpMethod.GET); - when(mockRequest.uri()).thenReturn("/hookRootURI/registrations/listeners"); - when(mockRequest.response()).thenReturn(mockResponse); - when(mockRequest.params()).thenReturn(params); + setListenerStorageEntryAndTriggerUpdate(buildListenerConfigWithHeadersFilter(null, singleListener, "x-foo: (A|B)")); + // wait a moment to let the listener be registered + Thread.sleep(500); + // Mock RoutingContext and configure response capture + RoutingContext routingContext = mock(RoutingContext.class); + when(routingContext.request()).thenReturn(request); - // Capture status code and response from mockResponse ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); - // Execute the method + // Execute the handler boolean result = hookHandler.handle(routingContext); - - // Verifications - verify(mockRequest, times(1)).method(); - verify(mockRequest, times(1)).uri(); assertTrue(result); - // Check that the parameters MultiMap is empty - assertTrue(params.isEmpty()); - verify(mockResponse).setStatusCode(400); - - // Verify captured response content + // Validate JSON response content for matching listener String jsonResponse = responseCaptor.getValue(); assertNotNull(jsonResponse); - assertTrue(jsonResponse.contains("Bad Request")); - - // Additional debug output - System.out.println("Params is empty: " + params.isEmpty()); - System.out.println("Response body: " + jsonResponse); + assertTrue(jsonResponse.contains(singleListener)); } @Test - public void testHandleGETRequestWithInvalidParam() { - // Mocking necessary components - HttpServerRequest mockRequest = mock(HttpServerRequest.class); + public void testHandleGETRequestWithListenersSearchMultipleResults() throws InterruptedException { + // Define the URI and set up the GET request with a broader search parameter for multiple listeners + String uri = "/hookRootURI/registrations/listeners"; HttpServerResponse mockResponse = mock(HttpServerResponse.class); + GETRequest request = new GETRequest(uri, mockResponse); + request.addParameter("q", "listener"); // Search parameter that should match multiple listeners + + // Add multiple listeners to the MockResourceStorage using the expected configuration and register them + String listenerId1 = "listener112222"; + String listenerId2 = "listener222133"; + setListenerStorageEntryAndTriggerUpdate(buildListenerConfigWithHeadersFilter(null, listenerId1, "x-foo: (A|B)")); + Thread.sleep(500); + setListenerStorageEntryAndTriggerUpdate(buildListenerConfigWithHeadersFilter(null, listenerId2, "x-foo: (A|B)")); + Thread.sleep(500); + + // Mock the RoutingContext and set up the response capture RoutingContext routingContext = mock(RoutingContext.class); - - // Setting up a real MultiMap to simulate an invalid parameter - MultiMap params = MultiMap.caseInsensitiveMultiMap(); - params.add("invalid", "value"); - - // Configuring mocks - when(routingContext.request()).thenReturn(mockRequest); - when(mockRequest.method()).thenReturn(HttpMethod.GET); - when(mockRequest.uri()).thenReturn("/hookRootURI/registrations/listeners"); - when(mockRequest.response()).thenReturn(mockResponse); - when(mockRequest.params()).thenReturn(params); - - // Capture status code and response from mockResponse + when(routingContext.request()).thenReturn(request); ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); - // Execute the method + // Execute the handler boolean result = hookHandler.handle(routingContext); - - // Verifications - verify(mockRequest, times(1)).method(); - verify(mockRequest, times(1)).uri(); assertTrue(result); - // Expecting status code 400 due to invalid parameter - verify(mockResponse).setStatusCode(400); - - // Verify captured response content + // Validate the JSON response content for multiple matching listeners String jsonResponse = responseCaptor.getValue(); assertNotNull(jsonResponse); - assertTrue(jsonResponse.contains("Bad Request")); - - // Additional debug output - System.out.println("Invalid params: " + params); - System.out.println("Response body: " + jsonResponse); + assertTrue(jsonResponse.contains(listenerId1)); + assertTrue(jsonResponse.contains(listenerId2)); } + @Test - public void testHandleGETRequestWithEmptyParam() { - // Mocking necessary components - HttpServerRequest mockRequest = mock(HttpServerRequest.class); + public void testHandleGETRequestWithRoutesSearchEmptyResult() { + // Define URI and configure request with specific 'q' parameter for routes search + String uri = "/hookRootURI/registrations/routes"; HttpServerResponse mockResponse = mock(HttpServerResponse.class); - RoutingContext routingContext = mock(RoutingContext.class); + GETRequest request = new GETRequest(uri, mockResponse); + request.addParameter("q", "routeNotFound"); // Parameter that should result in no matches - // Setting up a real MultiMap to simulate an empty 'q' parameter - MultiMap params = MultiMap.caseInsensitiveMultiMap(); - params.add("q", ""); + // No routes are added to MockResourceStorage to simulate empty result + storage.putMockData("hookRootURI/registrations/routes", new JsonArray().encode()); - // Configuring mocks - when(routingContext.request()).thenReturn(mockRequest); - when(mockRequest.method()).thenReturn(HttpMethod.GET); - when(mockRequest.uri()).thenReturn("/hookRootURI/registrations/listeners"); - when(mockRequest.response()).thenReturn(mockResponse); - when(mockRequest.params()).thenReturn(params); + // Mock RoutingContext and configure response capture + RoutingContext routingContext = mock(RoutingContext.class); + when(routingContext.request()).thenReturn(request); - // Capture status code and response from mockResponse ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); - // Execute the method + // Execute the handler boolean result = hookHandler.handle(routingContext); // Verifications - verify(mockRequest, times(1)).method(); - verify(mockRequest, times(1)).uri(); assertTrue(result); - // Expecting status code 400 due to empty 'q' parameter - verify(mockResponse).setStatusCode(400); + // Verify response content with empty result + String jsonResponse = responseCaptor.getValue(); + assertNotNull(jsonResponse); + assertEquals("{\"routes\":[]}", jsonResponse); + } + @Test + public void testHandleGETRequestWithRoutesSearchMultipleResults() throws InterruptedException { + // Define the URI and set up the GET request with a broad search parameter for multiple routes + String uri = "/hookRootURI/registrations/routes"; + HttpServerResponse mockResponse = mock(HttpServerResponse.class); + GETRequest request = new GETRequest(uri, mockResponse); + request.addParameter("q", "route"); // Search parameter that should match multiple routes + + // Add multiple routes to the MockResourceStorage using the expected configuration and register them + String routeId1 = "route12345"; + String routeId2 = "route67890"; + setRouteStorageEntryAndTriggerUpdate(buildRouteConfig(routeId1)); + Thread.sleep(500); + setRouteStorageEntryAndTriggerUpdate(buildRouteConfig( routeId2)); + Thread.sleep(500); + + // Mock the RoutingContext and set up the response capture + RoutingContext routingContext = mock(RoutingContext.class); + when(routingContext.request()).thenReturn(request); + ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); + when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); + when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); + + // Execute the handler + boolean result = hookHandler.handle(routingContext); + assertTrue(result); - // Verify captured response content + // Validate the JSON response content for multiple matching routes String jsonResponse = responseCaptor.getValue(); assertNotNull(jsonResponse); - assertTrue(jsonResponse.contains("Bad Request")); + assertTrue(jsonResponse.contains(routeId1)); + assertTrue(jsonResponse.contains(routeId2)); + } - // Additional debug output - System.out.println("Empty 'q' param: " + params.get("q")); - System.out.println("Response body: " + jsonResponse); + @Test + public void testHandleListenerWithStorageAndEmptyList() { + // Set up the URI for listeners registration + String uri = "hookRootURI/registrations/listeners"; + HttpServerResponse response = mock(HttpServerResponse.class); + GETRequest request = new GETRequest(uri,response) ; + // Add a valid query parameter for the listener search + request.addParameter("q", "validQueryParam"); + + // Capture the response output + ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); + RoutingContext routingContext = mock(RoutingContext.class); + when(routingContext.request()).thenReturn(request); + + // Execute the handler and validate the response + boolean result = hookHandler.handle(routingContext); + + assertTrue(result); // Ensure the handler returned true + verify(response).end(responseCaptor.capture()); + // Validate the response JSON for an empty listener list + String actualResponse = responseCaptor.getValue(); + assertNotNull(actualResponse); + assertEquals("{\"listeners\":[]}", actualResponse); // Response should contain an empty list } + /////////////////////////////////////////////////////////////////////////////// // Helpers /////////////////////////////////////////////////////////////////////////////// @@ -1097,6 +1121,7 @@ public void addHeader(String headerName, String headerValue) { headers.add(headerName, headerValue); } } + private static class Request extends DummyHttpServerRequest { private MultiMap headers; private HttpMethod httpMethod; @@ -1110,22 +1135,93 @@ public Request(HttpMethod httpMethod, String uri, MultiMap headers, HttpServerRe this.response = response; } - @Override public HttpMethod method() { + @Override + public HttpMethod method() { return httpMethod; } - @Override public String uri() { + + @Override + public String uri() { return uri; } - @Override public HttpServerResponse response() { return response; } - @Override public MultiMap headers() { return headers; } - @Override public HttpServerRequest pause() { return this; } - @Override public HttpServerRequest resume() { return this; } + + @Override + public HttpServerResponse response() { + return response; + } + + @Override + public MultiMap headers() { + return headers; + } + + @Override + public HttpServerRequest pause() { + return this; + } + + @Override + public HttpServerRequest resume() { + return this; + } } private static class Response extends DummyHttpServerResponse { private MultiMap headers = MultiMap.caseInsensitiveMultiMap(); @Override - public MultiMap headers() { return headers; } + public MultiMap headers() { + return headers; + } + } + + static class GETRequest extends DummyHttpServerRequest { + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + MultiMap params = MultiMap.caseInsensitiveMultiMap(); + private HttpServerResponse response; + private String uri; + + public GETRequest(String uri, HttpServerResponse response) { + this.uri = uri; + this.response = response; + } + + @Override + public HttpMethod method() { + return HttpMethod.GET; + } + + @Override + public String uri() { + return uri; + } + + @Override + public MultiMap headers() { + return headers; + } + + @Override + public MultiMap params() { + return params; + } + + @Override + public HttpServerResponse response() { + return response; + } + + @Override + public String getParam(String paramName) { + return params.get(paramName); + } + + public void addHeader(String headerName, String headerValue) { + headers.add(headerName, headerValue); + } + + public void addParameter(String paramName, String paramValue) { + params.add(paramName, paramValue); + } } } From f921c3bdd4eaeb5b1e15acb62ac687b87389276b Mon Sep 17 00:00:00 2001 From: almeidast Date: Wed, 30 Oct 2024 15:01:34 +0000 Subject: [PATCH 28/37] improve the implementation and remove unused methods --- .../swisspush/gateleen/hook/HookHandler.java | 12 ++- .../gateleen/hook/HookHandlerTest.java | 83 +++---------------- 2 files changed, 17 insertions(+), 78 deletions(-) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index a313279d..59999174 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -570,7 +570,6 @@ public boolean handle(final RoutingContext ctx) { } } - // 1. Check if the request method is GET if (requestMethod == GET && !request.params().isEmpty()) { String queryParam = request.getParam("q"); String normalizedRequestUri = requestUri.replaceAll("/$", ""); @@ -628,11 +627,10 @@ private void handleRouteSearch(String queryParam, HttpServerResponse response) { /** * Search the repository for items matching the query parameter. - * Returns a JSON response with the matched results. - * If any essential parameter (repository, response, getDestination) is null, - * a 400 Bad Request is returned. - * - * @param repository The items to search. + * Output a JSON response with the matched results. + * If parameter queryParam is empty or null a 400 Bad Request is returned. + * All params cannot be null + * @param repository The items to search . * @param getDestination Function to extract destinations. * @param queryParam The query string to match. * @param resultKey The key for the result in the response. @@ -640,7 +638,7 @@ private void handleRouteSearch(String queryParam, HttpServerResponse response) { */ private void handleSearch(Map repository, Function getDestination, String queryParam, String resultKey, HttpServerResponse response) { - if (repository == null || getDestination == null || resultKey == null || queryParam == null || queryParam.isEmpty()) { + if (queryParam == null || queryParam.isEmpty()) { response.setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()).end("Bad Request: One or more required parameters are missing or null"); return; } diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index ce47578c..90249c1f 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -8,7 +8,6 @@ import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.core.net.HostAndPort; -import io.vertx.ext.unit.Async; import io.vertx.ext.unit.TestContext; import io.vertx.ext.unit.junit.VertxUnitRunner; import io.vertx.ext.web.RoutingContext; @@ -120,6 +119,7 @@ private JsonObject buildRouteConfig(String routeId) { hook.put("methods", new JsonArray(Collections.singletonList("PUT"))); hook.put("timeout", 42); hook.put("connectionPoolSize", 10); + JsonObject staticHeaders = new JsonObject(); staticHeaders.put("x-custom-header", "route-header-value"); hook.put("staticHeaders", staticHeaders); @@ -662,7 +662,7 @@ public void hookRegistration_RouteWithRouteMultiplier(TestContext testContext) t @Test - public void testHandleGETRequestWithEmptyParam() { + public void testHandleGETRequestWithEmptyParam(TestContext testContext) { // Define URI and configures the request with an empty 'q' parameter String uri = "/hookRootURI/registrations/listeners"; HttpServerResponse mockResponse = mock(HttpServerResponse.class); @@ -670,7 +670,6 @@ public void testHandleGETRequestWithEmptyParam() { request.addParameter("q", ""); // Empty parameter to simulate bad request // Mock RoutingContext - RoutingContext routingContext = mock(RoutingContext.class); when(routingContext.request()).thenReturn(request); // Capture response content @@ -678,17 +677,17 @@ public void testHandleGETRequestWithEmptyParam() { when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); - // Execute the method + // Execute the Handler boolean result = hookHandler.handle(routingContext); - // Verifications - verify(mockResponse).setStatusCode(400); // Verify status 400 due to empty 'q' parameter - assertTrue(result); // Ensure the handler returned true - + // Verify status 400 due to empty 'q' parameter + verify(mockResponse).setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()); + testContext.assertTrue(result); // Verify captured response content String jsonResponse = responseCaptor.getValue(); - assertNotNull(jsonResponse); - assertTrue(jsonResponse.contains("Bad Request")); // Confirm the response contains "Bad Request" + testContext.assertNotNull(jsonResponse); + // Confirm the response contains "Bad Request" + testContext.assertTrue(jsonResponse.contains("Bad Request")); } @@ -706,7 +705,6 @@ public void testHandleGETRequestWithListenersSearchSingleResult() throws Interru // wait a moment to let the listener be registered Thread.sleep(500); // Mock RoutingContext and configure response capture - RoutingContext routingContext = mock(RoutingContext.class); when(routingContext.request()).thenReturn(request); ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); @@ -740,7 +738,6 @@ public void testHandleGETRequestWithListenersSearchMultipleResults() throws Inte Thread.sleep(500); // Mock the RoutingContext and set up the response capture - RoutingContext routingContext = mock(RoutingContext.class); when(routingContext.request()).thenReturn(request); ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); @@ -764,13 +761,12 @@ public void testHandleGETRequestWithRoutesSearchEmptyResult() { String uri = "/hookRootURI/registrations/routes"; HttpServerResponse mockResponse = mock(HttpServerResponse.class); GETRequest request = new GETRequest(uri, mockResponse); - request.addParameter("q", "routeNotFound"); // Parameter that should result in no matches + request.addParameter("q", "routeNotFound"); // No routes are added to MockResourceStorage to simulate empty result storage.putMockData("hookRootURI/registrations/routes", new JsonArray().encode()); // Mock RoutingContext and configure response capture - RoutingContext routingContext = mock(RoutingContext.class); when(routingContext.request()).thenReturn(request); ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); @@ -805,7 +801,6 @@ public void testHandleGETRequestWithRoutesSearchMultipleResults() throws Interru Thread.sleep(500); // Mock the RoutingContext and set up the response capture - RoutingContext routingContext = mock(RoutingContext.class); when(routingContext.request()).thenReturn(request); ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); @@ -828,19 +823,18 @@ public void testHandleListenerWithStorageAndEmptyList() { String uri = "hookRootURI/registrations/listeners"; HttpServerResponse response = mock(HttpServerResponse.class); GETRequest request = new GETRequest(uri,response) ; - // Add a valid query parameter for the listener search request.addParameter("q", "validQueryParam"); // Capture the response output ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); - RoutingContext routingContext = mock(RoutingContext.class); when(routingContext.request()).thenReturn(request); // Execute the handler and validate the response boolean result = hookHandler.handle(routingContext); - assertTrue(result); // Ensure the handler returned true + assertTrue(result); verify(response).end(responseCaptor.capture()); + // Validate the response JSON for an empty listener list String actualResponse = responseCaptor.getValue(); assertNotNull(actualResponse); @@ -1122,59 +1116,6 @@ public void addHeader(String headerName, String headerValue) { } } - private static class Request extends DummyHttpServerRequest { - private MultiMap headers; - private HttpMethod httpMethod; - private String uri; - private HttpServerResponse response; - - public Request(HttpMethod httpMethod, String uri, MultiMap headers, HttpServerResponse response) { - this.httpMethod = httpMethod; - this.uri = uri; - this.headers = headers; - this.response = response; - } - - @Override - public HttpMethod method() { - return httpMethod; - } - - @Override - public String uri() { - return uri; - } - - @Override - public HttpServerResponse response() { - return response; - } - - @Override - public MultiMap headers() { - return headers; - } - - @Override - public HttpServerRequest pause() { - return this; - } - - @Override - public HttpServerRequest resume() { - return this; - } - } - - private static class Response extends DummyHttpServerResponse { - private MultiMap headers = MultiMap.caseInsensitiveMultiMap(); - - @Override - public MultiMap headers() { - return headers; - } - } - static class GETRequest extends DummyHttpServerRequest { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); MultiMap params = MultiMap.caseInsensitiveMultiMap(); From 0f1c5e7d9afe4fe266710e1cad70efcecb935ab2 Mon Sep 17 00:00:00 2001 From: almeidast Date: Thu, 31 Oct 2024 08:38:25 +0000 Subject: [PATCH 29/37] improve code --- .../test/java/org/swisspush/gateleen/hook/HookHandlerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index 90249c1f..4881a47e 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -78,7 +78,7 @@ public void setUp() { requestQueue = mock(RequestQueue.class); reducedPropagationManager = mock(ReducedPropagationManager.class); hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, logAppenderRepository, - monitoringHandler, "userProfilePath", "hookRootURI/", requestQueue, false, reducedPropagationManager); + monitoringHandler, "userProfilePath", HOOK_ROOT_URI, requestQueue, false, reducedPropagationManager); hookHandler.init(); } From 8e3d799e72735a0ee96411b55b2c10459cc71495 Mon Sep 17 00:00:00 2001 From: almeidast Date: Mon, 4 Nov 2024 21:09:07 +0000 Subject: [PATCH 30/37] Add integration tests to compare results between current routes search and new implementation to search with parameters Add more unit tests --- .../gateleen/hook/HookHandlerTest.java | 85 +++++++++++++++++++ .../gateleen/hook/RouteListingTest.java | 53 ++++++++++-- 2 files changed, 132 insertions(+), 6 deletions(-) diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index 4881a47e..6531bb60 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -841,6 +841,91 @@ public void testHandleListenerWithStorageAndEmptyList() { assertEquals("{\"listeners\":[]}", actualResponse); // Response should contain an empty list } + @Test + public void testHandleGETRequestWithExtraParam(TestContext testContext) { + // Define URI and configure the request with an extra parameter besides 'q' + String uri = "/hookRootURI/registrations/listeners"; + HttpServerResponse mockResponse = mock(HttpServerResponse.class); + GETRequest request = new GETRequest(uri, mockResponse); + request.addParameter("q", "validQueryParam"); + request.addParameter("extra", "notAllowedParam"); // Extra parameter, not allowed + + // Mock the RoutingContext + when(routingContext.request()).thenReturn(request); + + // Capture the response content + ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); + when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); + when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); + + // Execute the Handler + boolean result = hookHandler.handle(routingContext); + + // Verify status 400 due to the extra parameter + verify(mockResponse).setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()); + testContext.assertTrue(result); + + // Verify captured response content + String jsonResponse = responseCaptor.getValue(); + testContext.assertNotNull(jsonResponse); + // Confirm that the response contains "Bad Request" + testContext.assertTrue(jsonResponse.contains("Bad Request")); + } + + @Test + public void testHandleGETRequestWithTrailingSlash(TestContext testContext) { + // Define URI with trailing slash and configure the request + String uri = "/hookRootURI/registrations/listeners/"; + HttpServerResponse mockResponse = mock(HttpServerResponse.class); + GETRequest request = new GETRequest(uri, mockResponse); + request.addParameter("q", "validQueryParam"); + + // Mock the RoutingContext + when(routingContext.request()).thenReturn(request); + + // Capture the response content + ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); + when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); + + // Execute the Handler + boolean result = hookHandler.handle(routingContext); + + // Verify the result contains an empty listeners list + testContext.assertTrue(result); + String jsonResponse = responseCaptor.getValue(); + testContext.assertNotNull(jsonResponse); + testContext.assertEquals("{\"listeners\":[]}", jsonResponse); + } + + @Test + public void testHandleGETRequestWithInvalidParam(TestContext testContext) { + // Define URI with an invalid parameter different from 'q' + String uri = "/hookRootURI/registrations/listeners"; + HttpServerResponse mockResponse = mock(HttpServerResponse.class); + GETRequest request = new GETRequest(uri, mockResponse); + request.addParameter("invalidParam", "someValue"); // Invalid parameter, not 'q' + + // Mock the RoutingContext + when(routingContext.request()).thenReturn(request); + + // Capture the response content + ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(String.class); + when(mockResponse.setStatusCode(anyInt())).thenReturn(mockResponse); + when(mockResponse.end(responseCaptor.capture())).thenReturn(Future.succeededFuture()); + + // Execute the Handler + boolean result = hookHandler.handle(routingContext); + + // Verify status 400 due to invalid parameter + verify(mockResponse).setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()); + testContext.assertTrue(result); + + // Verify captured response content + String jsonResponse = responseCaptor.getValue(); + testContext.assertNotNull(jsonResponse); + // Confirm that the response contains "Bad Request" + testContext.assertTrue(jsonResponse.contains("Bad Request")); + } /////////////////////////////////////////////////////////////////////////////// diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java index 6fe83dee..20395e6d 100644 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java @@ -313,22 +313,63 @@ public void testRouteListing_NonMatchingQueryParam(TestContext context) { * Test for route listing when no routes are registered. */ @Test - public void testRouteListing_NoRoutesRegistered(TestContext context) { + public void testSearchRouteListing_WhenNoRoutesRegistered(TestContext context) { Async async = context.async(); delete(); // Ensure there's no previous data initSettings(); // Initialize routing rules String queryParam = "someQuery"; - // No routes registered - // Send GET request with a query param + + // Send GET request with a query param when no routes are registered Response response = given().queryParam("q", queryParam) .when().get(searchUrlBase) .then().assertThat().statusCode(200) .extract().response(); - // Assert that the response body is empty or does not contain routes - Assert.assertFalse("No routes should be registered", - response.getBody().asString().contains(queryParam)); + // Parse response body as JSON + JsonObject jsonResponse = new JsonObject(response.getBody().asString()); + + // Validate that "routes" exists and is an empty array + Assert.assertTrue("Expected 'routes' to be an empty array", + jsonResponse.containsKey("routes") && jsonResponse.getJsonArray("routes").isEmpty()); + + async.complete(); + } + + @Test + public void testRouteListing_WithAndWithoutQueryParam_SingleMatch(TestContext context) { + Async async = context.async(); + delete(); // Clear any existing data before starting the test + initSettings(); // Initialize routing rules + + String routeName = "singleRoute"; + addRoute(routeName, true, true); // Add a route that will be the only matching result + + // Perform search without 'q' parameter + Response responseWithoutParam = get(searchUrlBase) + .then().assertThat().statusCode(200) + .extract().response(); + + // Perform search with 'q' parameter matching the route name + Response responseWithParam = given().queryParam("q", routeName) + .when().get(searchUrlBase) + .then().assertThat().statusCode(200) + .extract().response(); + + // Extract response bodies as strings for comparison + String responseBodyWithoutParam = responseWithoutParam.getBody().asString(); + String responseBodyWithParam = responseWithParam.getBody().asString(); + + // Verify that both responses are identical + Assert.assertEquals("Responses should be identical with and without 'q' when only one matching route exists", + responseBodyWithoutParam, responseBodyWithParam); + + // Ensure the route name is present in both responses + Assert.assertTrue(responseBodyWithoutParam.contains(routeName)); + Assert.assertTrue(responseBodyWithParam.contains(routeName)); + + // Clean up by removing the registered route after the test + removeRoute(routeName); async.complete(); } From 4a4fbffc034fa112be3ac528e2f59e365666eac4 Mon Sep 17 00:00:00 2001 From: almeidast Date: Wed, 13 Nov 2024 18:56:42 +0000 Subject: [PATCH 31/37] Added validations for route and listener search using parameters Fix route search with parameters Update readme with more information about search with query parameters create a hookIdentify to get a requestUrl when search using parameters Add more unit and integrate testes improve code implementations --- gateleen-hook/README_hook.md | 24 +- .../swisspush/gateleen/hook/HookHandler.java | 56 +++-- .../org/swisspush/gateleen/hook/Route.java | 8 +- .../swisspush/gateleen/hook/ListenerTest.java | 227 +++++++++--------- .../gateleen/hook/RouteListingTest.java | 82 ++----- 5 files changed, 209 insertions(+), 188 deletions(-) diff --git a/gateleen-hook/README_hook.md b/gateleen-hook/README_hook.md index c754d133..62a863ed 100644 --- a/gateleen-hook/README_hook.md +++ b/gateleen-hook/README_hook.md @@ -244,11 +244,13 @@ hookHandler.enableResourceLogging(true); Gateleen allows searching for listeners and routes using the query parameter `q`. This simplifies filtering the registered hooks based on query parameters. +The search will be based on the value registered of the destination property + ### Listener Search with `q` Search for listeners based on a query parameter like this: ``` -GET http://myserver:7012/playground/server/hooks/v1/registrations/listeners?q=test +GET http://myserver:7012/playground/server/hooks/v1/registrations/listeners?q=mylistener ``` The response will contain the matching listeners. If no match is found, an empty list is returned: @@ -257,7 +259,7 @@ The response will contain the matching listeners. If no match is found, an empty ```json { "listeners": [ - "first+playground+server+test+nemo+origin+b" + "first+playground+server+test+nemo+origin+mylistener" ] } ``` @@ -273,7 +275,23 @@ The response will contain the matching listeners. If no match is found, an empty Similarly, you can search for routes using a query parameter: ``` -GET http://myserver:7012/playground/server/hooks/v1/registrations/routes/?q=test +GET http://myserver:7012/playground/server/hooks/v1/registrations/routes/?q=myroute ``` The response contains the matching routes, or an empty list if no match is found. + +**Example response with matches:** +```json +{ + "routes": [ + "first+playground+server+test+nemo+origin+myroute" + ] +} + +``` +**Example response with no matches:** +```json +{ + "routes": [] +} +``` \ No newline at end of file diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index 59999174..f6a8288b 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -159,6 +159,8 @@ public class HookHandler implements LoggableResource { private final QueueSplitter queueSplitter; private final String routeBase; private final String listenerBase; + private final String normalizedRouteBase; + private final String normalizedListenerBase; /** * Creates a new HookHandler. @@ -293,6 +295,9 @@ public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage use jsonSchemaHook = JsonSchemaFactory.getInstance().getSchema(hookSchema); this.listenerBase = hookRootUri + HOOK_LISTENER_STORAGE_PATH; this.routeBase = hookRootUri + HOOK_ROUTE_STORAGE_PATH; + this.normalizedListenerBase = this.listenerBase.replaceAll("/+$", ""); + this.normalizedRouteBase = this.routeBase.replaceAll("/+$", ""); + } public void init() { @@ -544,7 +549,7 @@ public void registerListenerRegistrationHandler(Handler readyHandler) { public boolean handle(final RoutingContext ctx) { HttpServerRequest request = ctx.request(); boolean consumed = false; - var requestUri = request.uri(); + var requestUri = request.uri().replaceAll("/+$", "");; /* * 1) Un- / Register Listener / Routes */ @@ -572,11 +577,15 @@ public boolean handle(final RoutingContext ctx) { if (requestMethod == GET && !request.params().isEmpty()) { String queryParam = request.getParam("q"); - String normalizedRequestUri = requestUri.replaceAll("/$", ""); - if (normalizedRequestUri.contains(listenerBase.replaceAll("/$", ""))) { + if (request.params().size() > 1 || (queryParam == null) || (queryParam.isEmpty())) { + request.response().setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()) + .end("Bad Request: Only the 'q' parameter is allowed and can't be empty or null"); + return true; + } + if (requestUri.contains(normalizedListenerBase)) { handleListenerSearch(queryParam, request.response()); return true; - } else if (normalizedRequestUri.contains(routeBase.replaceAll("/$", ""))) { + } else if (requestUri.contains(normalizedRouteBase)) { handleRouteSearch(queryParam, request.response()); return true; } @@ -617,7 +626,7 @@ private void handleListenerSearch(String queryParam, HttpServerResponse response private void handleRouteSearch(String queryParam, HttpServerResponse response) { handleSearch( - routeRepository.getRoutes(), + routeRepository.getRoutes().entrySet().stream().collect(Collectors.toMap(entry -> entry.getValue().getHookIdentify(), Map.Entry::getValue)), route -> route.getHook().getDestination(), queryParam, ROUTES_KEY, @@ -637,18 +646,12 @@ private void handleRouteSearch(String queryParam, HttpServerResponse response) { * @param response The HTTP response to return the results. Must not be null. */ private void handleSearch(Map repository, Function getDestination, String queryParam, String resultKey, HttpServerResponse response) { - - if (queryParam == null || queryParam.isEmpty()) { - response.setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()).end("Bad Request: One or more required parameters are missing or null"); - return; - } - JsonArray matchingResults = new JsonArray(); repository.forEach((key, value) -> { String destination = getDestination.apply(value); if (destination != null && destination.contains(queryParam)) { - matchingResults.add(key); + matchingResults.add(convertToStoragePattern(key)); } }); @@ -1490,7 +1493,7 @@ private void registerListener(Buffer buffer) { target = hook.getDestination(); } else { String urlPattern = hookRootUri + LISTENER_HOOK_TARGET_PATH + target; - routeRepository.addRoute(urlPattern, createRoute(urlPattern, hook)); + routeRepository.addRoute(urlPattern, createRoute(urlPattern, hook, requestUrl)); if (log.isTraceEnabled()) { log.trace("external target, add route for urlPattern: {}", urlPattern); @@ -1672,12 +1675,14 @@ private void registerRoute(Buffer buffer) { } boolean mustCreateNewRoute = true; + + Route existingRoute = routeRepository.getRoutes().get(routedUrl); if (existingRoute != null) { mustCreateNewRoute = mustCreateNewRouteForHook(existingRoute, hook); } if (mustCreateNewRoute) { - routeRepository.addRoute(routedUrl, createRoute(routedUrl, hook)); + routeRepository.addRoute(routedUrl, createRoute(routedUrl , hook, requestUrl)); } else { // see comment in #mustCreateNewRouteForHook() existingRoute.getRule().setHeaderFunction(hook.getHeaderFunction()); @@ -1686,6 +1691,25 @@ private void registerRoute(Buffer buffer) { monitoringHandler.updateRoutesCount(routeRepository.getRoutes().size()); } + /** + * Extracts the route name segment from the given request URL after the HOOKS_ROUTE_URI_PART. + * Returns an empty string if there is no route segment to extract. + * + * @param requestUrl the full URL of the request + * @return the extracted route name or an empty string if no route name is present + */ + private String extractRouteName(String requestUrl) { + int startIdx = requestUrl.indexOf(HOOKS_ROUTE_URI_PART); + if (requestUrl.isEmpty() || startIdx == -1) { + return ""; + } + startIdx += HOOKS_ROUTE_URI_PART.length(); + if (startIdx < requestUrl.length()) { + return requestUrl.substring(startIdx); + } + return ""; + } + /** * check if an existing route must be thrown away because the new Hook does not match the config of the existing Route * @@ -1730,9 +1754,9 @@ private boolean headersFilterPatternEquals(Pattern headersFilterPatternLeft, Pat * @param hook hook * @return Route */ - private Route createRoute(String urlPattern, HttpHook hook) { + private Route createRoute(String urlPattern, HttpHook hook, String hookIdentify) { return new Route(vertx, userProfileStorage, loggingResourceManager, logAppenderRepository, monitoringHandler, - userProfilePath, hook, urlPattern, selfClient); + userProfilePath, hook, urlPattern, selfClient,hookIdentify); } /** diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/Route.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/Route.java index 85080e44..6c61308a 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/Route.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/Route.java @@ -49,6 +49,7 @@ public class Route { private MonitoringHandler monitoringHandler; private String userProfilePath; private ResourceStorage storage; + private String hookIdentify; private String urlPattern; private HttpHook httpHook; @@ -79,7 +80,7 @@ public class Route { * @param urlPattern - this can be a listener or a normal urlPattern (eg. for a route) */ public Route(Vertx vertx, ResourceStorage storage, LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, - MonitoringHandler monitoringHandler, String userProfilePath, HttpHook httpHook, String urlPattern, HttpClient selfClient) { + MonitoringHandler monitoringHandler, String userProfilePath, HttpHook httpHook, String urlPattern, HttpClient selfClient, String hookIdentify) { this.vertx = vertx; this.storage = storage; this.loggingResourceManager = loggingResourceManager; @@ -89,6 +90,7 @@ public Route(Vertx vertx, ResourceStorage storage, LoggingResourceManager loggin this.httpHook = httpHook; this.urlPattern = urlPattern; this.selfClient = selfClient; + this.hookIdentify = hookIdentify; createRule(); @@ -273,4 +275,8 @@ public void cleanup() { public HttpHook getHook() { return httpHook; } + + public String getHookIdentify() { + return hookIdentify; + } } diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java index 8565141e..92f8ae9d 100755 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java @@ -44,10 +44,16 @@ public class ListenerTest extends AbstractTest { private String requestUrlBase; private String targetUrlBase; private String searchUrlBase; + private String defaultRegisterUrlListener; + private String defaultTargetListener; + private String[] defaultMethodsListener; + private final String defaultQueryParam = "defaultQueryParam"; + private final String defaultListenerName = "defaultListener"; @Rule public WireMockRule wireMockRule = new WireMockRule(WIREMOCK_PORT); + /** * Overwrite RestAssured configuration */ @@ -59,6 +65,11 @@ public void initRestAssured() { targetUrlBase = "http://localhost:" + MAIN_PORT + SERVER_ROOT + "/tests/gateleen/targetresource"; searchUrlBase = "http://localhost:" + MAIN_PORT + SERVER_ROOT + "/hooks/v1/registrations/listeners"; + defaultRegisterUrlListener = requestUrlBase + "/" + defaultQueryParam + TestUtils.getHookListenersUrlSuffix() + defaultListenerName; + defaultTargetListener = targetUrlBase + "/" + defaultListenerName; + defaultMethodsListener = new String[]{"PUT", "DELETE", "POST"}; + + } /** @@ -948,103 +959,56 @@ private void checkGETBodyWithAwait(final String requestUrl, final String body) { await().atMost(TEN_SECONDS).until(() -> when().get(requestUrl).then().extract().body().asString(), equalTo(body)); } - /** - * Test for hookHandleSearch with listener storage path and valid query param.
- * requestUrl: http://localhost:7012/playground/server/hooks/v1/registrations/listeners/?q=testQuery - */ - @Test - public void testHookHandleSearch_ListenerPathWithValidQueryParam(TestContext context) { - Async async = context.async(); + private Response searchWithQueryParam(String searchParam, String queryParam, int expectedStatusCode ) { + return given() + .queryParam(searchParam, queryParam) + .when().get(searchUrlBase ) + .then().assertThat().statusCode(expectedStatusCode) + .extract().response(); + } + private void registerDefaultListener() { delete(); initRoutingRules(); - - // Settings - String subresource = "matchingQueryParam"; - String listenerName = "myListener"; - - String registerUrlListener1 = requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix() + listenerName; - String targetListener1 = targetUrlBase + "/" + listenerName; - String[] methodsListener1 = new String[]{"PUT", "DELETE", "POST"}; - final String targetUrlListener1 = targetUrlBase + "/" + listenerName; - - delete(searchUrlBase); - delete(targetUrlListener1); - - //Sending request, one listener hooked - TestUtils.registerListener(registerUrlListener1, targetListener1, methodsListener1, null, null, + delete(defaultTargetListener); + TestUtils.registerListener(defaultRegisterUrlListener, defaultTargetListener, defaultMethodsListener, null, null, null, null, "x-foo: (A|B)"); - - // Verify that the listener was correctly registered - Response response = given() - .queryParam("q", listenerName) - .when().get(searchUrlBase) - .then().assertThat().statusCode(200) - .extract().response(); - - // Assert that the response contains the expected query param - System.out.println(response.getBody().asString()); - Assert.assertTrue(response.getBody().asString().contains(listenerName)); // Fails if not found - TestUtils.unregisterListener(registerUrlListener1); - - async.complete(); } - - /** - * Test for hookHandleSearch with listener storage path and valid query param but no match found.
- * requestUrl: http://localhost:7012/playground/server/hooks/v1/registrations/listeners/?q=nonMatchingQuery - */ @Test - public void testHookHandleSearch_ListenerPathWithNonMatchingQueryParam(TestContext context) { + public void testSearchListenerWithValidAndInvalidSearchParam(TestContext context) { Async async = context.async(); - delete(); - initRoutingRules(); + registerDefaultListener(); - // Settings - String subresource = "matchingQueryParam"; - String listenerName = "myListener"; + searchWithQueryParam("wq", defaultListenerName, 400); + searchWithQueryParam("q", "", 400); - String registerUrlListener1 = requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix() + listenerName; - String targetListener1 = targetUrlBase + "/" + listenerName; - String[] methodsListener1 = new String[]{"PUT", "DELETE", "POST"}; - final String targetUrlListener1 = targetUrlBase + "/" + listenerName + "/" + "test"; + Response response = searchWithQueryParam("q",defaultListenerName,200); + Assert.assertTrue(response.getBody().asString().contains(defaultListenerName)); // Fails if not found + TestUtils.unregisterListener(defaultRegisterUrlListener); - delete(searchUrlBase); - delete(targetUrlListener1); + async.complete(); + } - TestUtils.registerListener(registerUrlListener1, targetListener1, methodsListener1, null, null, - null, null, "x-foo: (A|B)"); + @Test + public void testSearchListenerWithNonMatchingQueryParam(TestContext context) { + Async async = context.async(); + registerDefaultListener(); // Verify that the listener was correctly registered - Response response = given() - .queryParam("q", listenerName) - .when().get(searchUrlBase) - .then().assertThat().statusCode(200) - .extract().response(); - - // Assert that the response contains the expected query param - Assert.assertTrue(response.getBody().asString().contains(listenerName)); // Fails if not found + Response response = searchWithQueryParam("q",defaultListenerName,200); + Assert.assertTrue(response.getBody().asString().contains(defaultListenerName)); - listenerName="nonMatchingQueryParam"; + String localListenerName="nonMatchingQueryParam"; // Verify that the listener search with a no registered listener - response = given() - .queryParam("q", listenerName) - .when().get(searchUrlBase) - .then().assertThat().statusCode(200) - .extract().response(); + response = searchWithQueryParam("q",localListenerName,200); + Assert.assertFalse(response.getBody().asString().contains(localListenerName)); - // Assert that the response contains the expected query param - Assert.assertFalse(response.getBody().asString().contains(listenerName)); // Fails if not found - TestUtils.unregisterListener(registerUrlListener1); + TestUtils.unregisterListener(defaultRegisterUrlListener); async.complete(); } - /** - * Test for hookHandleSearch with listener storage path and valid query param but no listeners registered.
- * requestUrl: http://localhost:7012/playground/server/hooks/v1/registrations/listeners/?q=someQuery - */ @Test public void testHookHandleSearch_NoListenersRegistered(TestContext context) { Async async = context.async(); @@ -1052,21 +1016,19 @@ public void testHookHandleSearch_NoListenersRegistered(TestContext context) { initRoutingRules(); String queryParam = "someQuery"; - String listenerPath = "/hooks/listeners"; // Verify that the listener search with a no registered listener - Response response = given() - .queryParam("q", queryParam) - .when().get(searchUrlBase) - .then().assertThat().statusCode(200) - .extract().response(); + Response response = searchWithQueryParam("q",queryParam,200); + + // Parse response body as JSON + JsonObject jsonResponse = new JsonObject(response.getBody().asString()); // Assert that the response contains the expected query param - Assert.assertFalse(response.getBody().asString().contains(queryParam)); // Fails if not found + Assert.assertTrue("Expected 'listeners' to be an empty array", + jsonResponse.containsKey("listeners") && jsonResponse.getJsonArray("listeners").isEmpty()); async.complete(); } - @Test public void testHookHandleSearch_ListenerPathInvalidParam(TestContext context) { Async async = context.async(); @@ -1074,7 +1036,6 @@ public void testHookHandleSearch_ListenerPathInvalidParam(TestContext context) { initRoutingRules(); String queryParam = "testQuery"; - String listenerPath = "/_hooks/listeners"; String requestUrl = searchUrlBase+ "?www=" + queryParam; // Validate the response @@ -1082,42 +1043,54 @@ public void testHookHandleSearch_ListenerPathInvalidParam(TestContext context) { async.complete(); } - /** - * Test for hookHandleSearch with listener storage path and no query parameter.
- * requestUrl: http://localhost:7012/playground/server/hooks/v1/registrations/listeners/?q= - */ @Test - public void testHookHandleSearch_NoQueryParameter(TestContext context) { + public void testHookHandleSearch_ListenerTwoParam(TestContext context) { Async async = context.async(); delete(); initRoutingRules(); - // Settings - String subresource = "validQueryParameter"; - String listenerName = ""; + String queryParam = "testQuery"; + String requestUrl = searchUrlBase+ "?q=" + queryParam+"&www=" + queryParam;; - String registerUrlListener1 = requestUrlBase + "/" + subresource + TestUtils.getHookListenersUrlSuffix() + listenerName; - String targetListener1 = targetUrlBase + "/" + listenerName; - String[] methodsListener1 = new String[]{"PUT", "DELETE", "POST"}; - final String targetUrlListener1 = targetUrlBase + "/" + listenerName; + // Validate the response + checkGETStatusCodeWithAwait(requestUrl, 400); + async.complete(); + } - delete(searchUrlBase); - delete(targetUrlListener1); + @Test + public void testHookHandleSearch_UseSlashInSearchUrlBase(TestContext context) { + Async async = context.async(); + delete(); + initRoutingRules(); - //Sending request, one listener hooked - TestUtils.registerListener(registerUrlListener1, targetListener1, methodsListener1, null, null, - null, null, "x-foo: (A|B)"); + registerDefaultListener(); - // Verify that the listener was correctly registered Response response = given() - .queryParam("q", listenerName) - .when().get(searchUrlBase) - .then().assertThat().statusCode(400) - .extract().response(); + .queryParam("q", defaultListenerName) + .when().get(searchUrlBase +"/") + .then().assertThat().statusCode(200) + .extract().response(); + + // Assert that the response contains the expected query param + Assert.assertTrue(response.getBody().asString().contains(defaultListenerName)); // Fails if not found + + TestUtils.unregisterListener(defaultRegisterUrlListener); + + async.complete(); + } + + @Test + public void testHookHandleSearch_EmptyQueryParameter(TestContext context) { + Async async = context.async(); + delete(); + initRoutingRules(); + registerDefaultListener(); + + Response response = searchWithQueryParam("q","",400); // Assert that the response contains the expected query param Assert.assertTrue(response.getBody().asString().contains("Bad Request")); // Fails if not found - TestUtils.unregisterListener(registerUrlListener1); + TestUtils.unregisterListener(defaultRegisterUrlListener); async.complete(); } @@ -1156,9 +1129,10 @@ public void testHookHandleSearch_ReturnsTwoOutOfThreeListeners(TestContext conte TestUtils.registerListener(registerUrlListener3, targetListener3, methodsListener, null, null, null, null, "x-foo: (A|B)"); // Perform a search for the listeners that should return only listenerOne and listenerTwo + // added in searchUrlBase a '/' to validate if also works Response response = given() .queryParam("q", "listener") - .when().get(searchUrlBase) + .when().get(searchUrlBase+"/") .then().assertThat().statusCode(200) .extract().response(); @@ -1175,5 +1149,40 @@ public void testHookHandleSearch_ReturnsTwoOutOfThreeListeners(TestContext conte async.complete(); } + /** + * Test to verify that the search for listeners returns identical results + * when performed with and without a query parameter if only one matching listener exists. + * Registers a listener and performs searches, confirming that both responses match. + */ + @Test + public void testSearchListener_IdenticalResultsWithAndWithoutQueryParam(TestContext context) { + Async async = context.async(); + registerDefaultListener(); + // Perform search with 'q' parameter matching the listener name + Response responseWithParam = searchWithQueryParam("q",defaultListenerName,200); + + // Perform search without 'q' parameter + Response responseWithoutParam = given().when().get(searchUrlBase) + .then().assertThat().statusCode(200) + .extract().response(); + + // Extract response bodies as strings for comparison + String responseBodyWithoutParam = responseWithoutParam.getBody().asString(); + String responseBodyWithParam = responseWithParam.getBody().asString(); + + // Verify that both responses are identical + Assert.assertEquals("Responses should be identical with and without 'q' when only one matching listener exists", + responseBodyWithoutParam, responseBodyWithParam); + + // Ensure the listener name is present in both responses + Assert.assertTrue("Listener name should be present in response without parameter", + responseBodyWithoutParam.contains(defaultListenerName)); + Assert.assertTrue("Listener name should be present in response with parameter", + responseBodyWithParam.contains(defaultListenerName)); + + // Clean up by removing the registered listener after the test + TestUtils.unregisterListener(defaultRegisterUrlListener); + async.complete(); + } } diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java index 20395e6d..4ad868ba 100644 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java @@ -15,7 +15,6 @@ import org.swisspush.gateleen.TestUtils; import static io.restassured.RestAssured.*; -import static org.swisspush.gateleen.TestUtils.checkGETStatusCodeWithAwait; /** * Test class for the hook route feature. @@ -196,9 +195,6 @@ public void testListingWithoutStaticAndDynamicCollections(TestContext context) { async.complete(); } - - - private void removeRoute(String name) { String route = requestUrlBase + "/" + name + TestUtils.getHookRouteUrlSuffix(); TestUtils.unregisterRoute(route); @@ -215,7 +211,6 @@ private void addRoute(String name, boolean collection, boolean listable) { TestUtils.registerRoute(route, target, methods, null, collection, listable); } - private void assertResponse(final Response response, final String[] expectedArray) { Assert.assertEquals(200, response.statusCode()); String bodyString = response.getBody().asString(); @@ -226,43 +221,42 @@ private void assertResponse(final Response response, final String[] expectedArra Assert.assertThat(array, Matchers.contains(expectedArray)); } - /** - * Test for route listing with a valid query parameter. - */ + private Response searchWithQueryParam(String searchParam, String queryParam, int expectedStatusCode ) { + return given() + .queryParam(searchParam, queryParam) + .when().get(searchUrlBase ) + .then().assertThat().statusCode(expectedStatusCode) + .extract().response(); + } + @Test - public void testRouteListing_ValidQueryParam(TestContext context) { + public void testHookHandleSearch_WithValidAndInvalidSearchParam(TestContext context) { Async async = context.async(); delete(); // Remove any pre-existing data initSettings(); // Initialize routing rules String queryParam = "routeTests"; - String routePath = "/routes"; - addRoute(queryParam, true, true); // Verify that the route was correctly registered - Response response = given() - .queryParam("q", queryParam) - .when().get(searchUrlBase ) - .then().assertThat().statusCode(200) - .extract().response(); + searchWithQueryParam("w", queryParam, 400); + searchWithQueryParam("q", "", 400); + + // Verify that the route was correctly registered + Response response = searchWithQueryParam("q", queryParam, 200); // Assert that the response contains the expected query param String responseBody = response.getBody().asString(); - Assert.assertTrue(responseBody.contains(queryParam)); // Fails if not found - + Assert.assertTrue(responseBody.contains(queryParam)); // Unregister the route removeRoute(queryParam); async.complete(); } - /** - * Test for route listing with a non-matching query parameter. - */ @Test - public void testRouteListing_NonMatchingQueryParam(TestContext context) { + public void testHookHandleSearch_RouteNonMatchingQueryParam(TestContext context) { Async async = context.async(); delete(); // Clean up before the test initSettings(); // Initialize routing rules @@ -275,45 +269,24 @@ public void testRouteListing_NonMatchingQueryParam(TestContext context) { assertResponse(get(requestUrlBase), new String[]{queryParam+"/"}); // Send GET request with a non-matching query param - Response response = given().queryParam("q", queryParam) - .when().get(searchUrlBase) - .then().assertThat().statusCode(200) - .extract().response(); + Response response = searchWithQueryParam("q", queryParam, 200); Assert.assertTrue("Query param should be found in response", response.getBody().asString().contains(queryParam)); // Send GET request with a non-matching query param - response = given().queryParam("q", nonMatchingQueryParam) - .when().get(searchUrlBase) - .then().assertThat().statusCode(200) - .extract().response(); + response = searchWithQueryParam("q", nonMatchingQueryParam, 200); // Assert the response does not contain the non-matching query param Assert.assertFalse("Non-matching query param should not be found in response", response.getBody().asString().contains(nonMatchingQueryParam)); - // Send GET request with a matching query param - response = given().queryParam("q", queryParam) - .when().get(searchUrlBase) - .then().assertThat().statusCode(200) - .extract().response(); - - // Assert the response contain the matching query param - Assert.assertTrue("matching query param should be found in response", - response.getBody().asString().contains(queryParam)); - - // Unregister the route removeRoute(queryParam); - async.complete(); } - /** - * Test for route listing when no routes are registered. - */ @Test - public void testSearchRouteListing_WhenNoRoutesRegistered(TestContext context) { + public void testHookHandleSearch_RouteWhenNoRoutesRegistered(TestContext context) { Async async = context.async(); delete(); // Ensure there's no previous data initSettings(); // Initialize routing rules @@ -321,14 +294,10 @@ public void testSearchRouteListing_WhenNoRoutesRegistered(TestContext context) { String queryParam = "someQuery"; // Send GET request with a query param when no routes are registered - Response response = given().queryParam("q", queryParam) - .when().get(searchUrlBase) - .then().assertThat().statusCode(200) - .extract().response(); + Response response = searchWithQueryParam("q", queryParam, 200); // Parse response body as JSON JsonObject jsonResponse = new JsonObject(response.getBody().asString()); - // Validate that "routes" exists and is an empty array Assert.assertTrue("Expected 'routes' to be an empty array", jsonResponse.containsKey("routes") && jsonResponse.getJsonArray("routes").isEmpty()); @@ -337,12 +306,12 @@ public void testSearchRouteListing_WhenNoRoutesRegistered(TestContext context) { } @Test - public void testRouteListing_WithAndWithoutQueryParam_SingleMatch(TestContext context) { + public void testHookHandleSearch_ReturnsIdenticalResultsWithAndWithoutSearchQueryParam(TestContext context) { Async async = context.async(); delete(); // Clear any existing data before starting the test initSettings(); // Initialize routing rules - String routeName = "singleRoute"; + String routeName = "GenericSingleRoute"; addRoute(routeName, true, true); // Add a route that will be the only matching result // Perform search without 'q' parameter @@ -351,10 +320,7 @@ public void testRouteListing_WithAndWithoutQueryParam_SingleMatch(TestContext co .extract().response(); // Perform search with 'q' parameter matching the route name - Response responseWithParam = given().queryParam("q", routeName) - .when().get(searchUrlBase) - .then().assertThat().statusCode(200) - .extract().response(); + Response responseWithParam = searchWithQueryParam("q", routeName, 200); // Extract response bodies as strings for comparison String responseBodyWithoutParam = responseWithoutParam.getBody().asString(); @@ -373,6 +339,4 @@ public void testRouteListing_WithAndWithoutQueryParam_SingleMatch(TestContext co async.complete(); } - - } From 27f561fb910a1891806765eb7b3e5fca3d787eea Mon Sep 17 00:00:00 2001 From: almeidast Date: Thu, 14 Nov 2024 09:32:48 +0000 Subject: [PATCH 32/37] Add validation to check if is valid parameters for search request Clear unused code. --- .../swisspush/gateleen/hook/HookHandler.java | 41 ++++++------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index f6a8288b..321a9a0b 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -549,7 +549,7 @@ public void registerListenerRegistrationHandler(Handler readyHandler) { public boolean handle(final RoutingContext ctx) { HttpServerRequest request = ctx.request(); boolean consumed = false; - var requestUri = request.uri().replaceAll("/+$", "");; + var requestUri = request.uri().replaceAll("/+$", ""); /* * 1) Un- / Register Listener / Routes */ @@ -577,15 +577,10 @@ public boolean handle(final RoutingContext ctx) { if (requestMethod == GET && !request.params().isEmpty()) { String queryParam = request.getParam("q"); - if (request.params().size() > 1 || (queryParam == null) || (queryParam.isEmpty())) { - request.response().setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()) - .end("Bad Request: Only the 'q' parameter is allowed and can't be empty or null"); - return true; - } - if (requestUri.contains(normalizedListenerBase)) { + if (requestUri.contains(normalizedListenerBase) && checkValidParams(request,queryParam)) { handleListenerSearch(queryParam, request.response()); return true; - } else if (requestUri.contains(normalizedRouteBase)) { + } else if (requestUri.contains(normalizedRouteBase) && checkValidParams(request,queryParam)) { handleRouteSearch(queryParam, request.response()); return true; } @@ -614,6 +609,15 @@ public boolean handle(final RoutingContext ctx) { } } + private boolean checkValidParams (HttpServerRequest request, String queryParam){ + if (request.params().size() > 1 || (queryParam == null) || (queryParam.isEmpty())) { + request.response().setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()) + .end("Bad Request: Only the 'q' parameter is allowed and can't be empty or null"); + return true; + } + return false; + } + private void handleListenerSearch(String queryParam, HttpServerResponse response) { handleSearch( listenerRepository.getListeners().stream().collect(Collectors.toMap(Listener::getListenerId, listener -> listener)), @@ -646,8 +650,8 @@ private void handleRouteSearch(String queryParam, HttpServerResponse response) { * @param response The HTTP response to return the results. Must not be null. */ private void handleSearch(Map repository, Function getDestination, String queryParam, String resultKey, HttpServerResponse response) { - JsonArray matchingResults = new JsonArray(); + JsonArray matchingResults = new JsonArray(); repository.forEach((key, value) -> { String destination = getDestination.apply(value); if (destination != null && destination.contains(queryParam)) { @@ -1691,25 +1695,6 @@ private void registerRoute(Buffer buffer) { monitoringHandler.updateRoutesCount(routeRepository.getRoutes().size()); } - /** - * Extracts the route name segment from the given request URL after the HOOKS_ROUTE_URI_PART. - * Returns an empty string if there is no route segment to extract. - * - * @param requestUrl the full URL of the request - * @return the extracted route name or an empty string if no route name is present - */ - private String extractRouteName(String requestUrl) { - int startIdx = requestUrl.indexOf(HOOKS_ROUTE_URI_PART); - if (requestUrl.isEmpty() || startIdx == -1) { - return ""; - } - startIdx += HOOKS_ROUTE_URI_PART.length(); - if (startIdx < requestUrl.length()) { - return requestUrl.substring(startIdx); - } - return ""; - } - /** * check if an existing route must be thrown away because the new Hook does not match the config of the existing Route * From 044ca922d73cc9a81ee98ede809a21703023c61e Mon Sep 17 00:00:00 2001 From: almeidast Date: Thu, 14 Nov 2024 09:52:27 +0000 Subject: [PATCH 33/37] Move specific validations --- .../swisspush/gateleen/hook/HookHandler.java | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index 321a9a0b..c86329f3 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -577,11 +577,11 @@ public boolean handle(final RoutingContext ctx) { if (requestMethod == GET && !request.params().isEmpty()) { String queryParam = request.getParam("q"); - if (requestUri.contains(normalizedListenerBase) && checkValidParams(request,queryParam)) { - handleListenerSearch(queryParam, request.response()); + if (requestUri.contains(normalizedListenerBase) ) { + handleListenerSearch(queryParam, request); return true; - } else if (requestUri.contains(normalizedRouteBase) && checkValidParams(request,queryParam)) { - handleRouteSearch(queryParam, request.response()); + } else if (requestUri.contains(normalizedRouteBase) ) { + handleRouteSearch(queryParam, request); return true; } } @@ -609,32 +609,23 @@ public boolean handle(final RoutingContext ctx) { } } - private boolean checkValidParams (HttpServerRequest request, String queryParam){ - if (request.params().size() > 1 || (queryParam == null) || (queryParam.isEmpty())) { - request.response().setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()) - .end("Bad Request: Only the 'q' parameter is allowed and can't be empty or null"); - return true; - } - return false; - } - - private void handleListenerSearch(String queryParam, HttpServerResponse response) { + private void handleListenerSearch(String queryParam, HttpServerRequest request) { handleSearch( listenerRepository.getListeners().stream().collect(Collectors.toMap(Listener::getListenerId, listener -> listener)), listener -> listener.getHook().getDestination(), queryParam, LISTENERS_KEY, - response + request ); } - private void handleRouteSearch(String queryParam, HttpServerResponse response) { + private void handleRouteSearch(String queryParam, HttpServerRequest request) { handleSearch( routeRepository.getRoutes().entrySet().stream().collect(Collectors.toMap(entry -> entry.getValue().getHookIdentify(), Map.Entry::getValue)), route -> route.getHook().getDestination(), queryParam, ROUTES_KEY, - response + request ); } @@ -647,9 +638,14 @@ private void handleRouteSearch(String queryParam, HttpServerResponse response) { * @param getDestination Function to extract destinations. * @param queryParam The query string to match. * @param resultKey The key for the result in the response. - * @param response The HTTP response to return the results. Must not be null. + * @param request The HTTP request to make a specific validations and return the results. */ - private void handleSearch(Map repository, Function getDestination, String queryParam, String resultKey, HttpServerResponse response) { + private void handleSearch(Map repository, Function getDestination, String queryParam, String resultKey, HttpServerRequest request) { + if (request.params().size() > 1 || (queryParam == null) || (queryParam.isEmpty())) { + request.response().setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()) + .end("Bad Request: Only the 'q' parameter is allowed and can't be empty or null"); + return ; + } JsonArray matchingResults = new JsonArray(); repository.forEach((key, value) -> { @@ -664,11 +660,12 @@ private void handleSearch(Map repository, Function get String encodedResult = result.encode(); - response.putHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE_JSON); - response.end(encodedResult); + request.response().putHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE_JSON); + request.response().end(encodedResult); } + /** * Create a listing of routes in the given parent. This happens * only if we have a GET request, the routes are listable and From 9956ebad4ad2af9677b92dcf56a403b1ac79add0 Mon Sep 17 00:00:00 2001 From: almeidast Date: Thu, 14 Nov 2024 09:57:19 +0000 Subject: [PATCH 34/37] improve code --- .../test/java/org/swisspush/gateleen/hook/ListenerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java index 92f8ae9d..10a8ff95 100755 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java @@ -47,7 +47,6 @@ public class ListenerTest extends AbstractTest { private String defaultRegisterUrlListener; private String defaultTargetListener; private String[] defaultMethodsListener; - private final String defaultQueryParam = "defaultQueryParam"; private final String defaultListenerName = "defaultListener"; @Rule @@ -65,7 +64,8 @@ public void initRestAssured() { targetUrlBase = "http://localhost:" + MAIN_PORT + SERVER_ROOT + "/tests/gateleen/targetresource"; searchUrlBase = "http://localhost:" + MAIN_PORT + SERVER_ROOT + "/hooks/v1/registrations/listeners"; - defaultRegisterUrlListener = requestUrlBase + "/" + defaultQueryParam + TestUtils.getHookListenersUrlSuffix() + defaultListenerName; + String defaultQueryParam = "defaultQueryParam"; + defaultRegisterUrlListener = requestUrlBase + "/" + defaultQueryParam + TestUtils.getHookListenersUrlSuffix() + defaultListenerName; defaultTargetListener = targetUrlBase + "/" + defaultListenerName; defaultMethodsListener = new String[]{"PUT", "DELETE", "POST"}; @@ -1050,7 +1050,7 @@ public void testHookHandleSearch_ListenerTwoParam(TestContext context) { initRoutingRules(); String queryParam = "testQuery"; - String requestUrl = searchUrlBase+ "?q=" + queryParam+"&www=" + queryParam;; + String requestUrl = searchUrlBase+ "?q=" + queryParam+"&www=" + queryParam; // Validate the response checkGETStatusCodeWithAwait(requestUrl, 400); From 4fb6453c6507908f47599c1c33e0323a76b7cf5d Mon Sep 17 00:00:00 2001 From: almeidast Date: Thu, 14 Nov 2024 15:18:46 +0000 Subject: [PATCH 35/37] Fix comments improve testes organize code remove unused variables --- .../swisspush/gateleen/hook/HookHandler.java | 26 +-- .../gateleen/hook/HookHandlerTest.java | 34 ++-- .../swisspush/gateleen/hook/ListenerTest.java | 187 +++++++++--------- .../gateleen/hook/RouteListingTest.java | 76 ++++--- 4 files changed, 157 insertions(+), 166 deletions(-) diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java index c86329f3..62be31e9 100755 --- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java +++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java @@ -31,12 +31,7 @@ import org.swisspush.gateleen.core.logging.LoggableResource; import org.swisspush.gateleen.core.logging.RequestLogger; import org.swisspush.gateleen.core.storage.ResourceStorage; -import org.swisspush.gateleen.core.util.CollectionContentComparator; -import org.swisspush.gateleen.core.util.HttpHeaderUtil; -import org.swisspush.gateleen.core.util.HttpRequestHeader; -import org.swisspush.gateleen.core.util.HttpServerRequestUtil; -import org.swisspush.gateleen.core.util.ResourcesUtils; -import org.swisspush.gateleen.core.util.StatusCode; +import org.swisspush.gateleen.core.util.*; import org.swisspush.gateleen.hook.queueingstrategy.DefaultQueueingStrategy; import org.swisspush.gateleen.hook.queueingstrategy.DiscardPayloadQueueingStrategy; import org.swisspush.gateleen.hook.queueingstrategy.QueueingStrategy; @@ -576,12 +571,11 @@ public boolean handle(final RoutingContext ctx) { } if (requestMethod == GET && !request.params().isEmpty()) { - String queryParam = request.getParam("q"); if (requestUri.contains(normalizedListenerBase) ) { - handleListenerSearch(queryParam, request); + handleListenerSearch(request); return true; } else if (requestUri.contains(normalizedRouteBase) ) { - handleRouteSearch(queryParam, request); + handleRouteSearch(request); return true; } } @@ -609,21 +603,19 @@ public boolean handle(final RoutingContext ctx) { } } - private void handleListenerSearch(String queryParam, HttpServerRequest request) { + private void handleListenerSearch(HttpServerRequest request) { handleSearch( listenerRepository.getListeners().stream().collect(Collectors.toMap(Listener::getListenerId, listener -> listener)), listener -> listener.getHook().getDestination(), - queryParam, LISTENERS_KEY, request ); } - private void handleRouteSearch(String queryParam, HttpServerRequest request) { + private void handleRouteSearch(HttpServerRequest request) { handleSearch( routeRepository.getRoutes().entrySet().stream().collect(Collectors.toMap(entry -> entry.getValue().getHookIdentify(), Map.Entry::getValue)), route -> route.getHook().getDestination(), - queryParam, ROUTES_KEY, request ); @@ -636,12 +628,12 @@ private void handleRouteSearch(String queryParam, HttpServerRequest request) { * All params cannot be null * @param repository The items to search . * @param getDestination Function to extract destinations. - * @param queryParam The query string to match. * @param resultKey The key for the result in the response. * @param request The HTTP request to make a specific validations and return the results. */ - private void handleSearch(Map repository, Function getDestination, String queryParam, String resultKey, HttpServerRequest request) { - if (request.params().size() > 1 || (queryParam == null) || (queryParam.isEmpty())) { + private void handleSearch(Map repository, Function getDestination, String resultKey, HttpServerRequest request) { + String queryParam = request.getParam("q"); + if (request.params().size() > 1 || StringUtils.isEmpty(queryParam)) { request.response().setStatusCode(StatusCode.BAD_REQUEST.getStatusCode()) .end("Bad Request: Only the 'q' parameter is allowed and can't be empty or null"); return ; @@ -1738,7 +1730,7 @@ private boolean headersFilterPatternEquals(Pattern headersFilterPatternLeft, Pat */ private Route createRoute(String urlPattern, HttpHook hook, String hookIdentify) { return new Route(vertx, userProfileStorage, loggingResourceManager, logAppenderRepository, monitoringHandler, - userProfilePath, hook, urlPattern, selfClient,hookIdentify); + userProfilePath, hook, urlPattern, selfClient, hookIdentify); } /** diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index 6531bb60..4a8dd393 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -703,7 +703,7 @@ public void testHandleGETRequestWithListenersSearchSingleResult() throws Interru setListenerStorageEntryAndTriggerUpdate(buildListenerConfigWithHeadersFilter(null, singleListener, "x-foo: (A|B)")); // wait a moment to let the listener be registered - Thread.sleep(500); + Thread.sleep(200); // Mock RoutingContext and configure response capture when(routingContext.request()).thenReturn(request); @@ -727,15 +727,18 @@ public void testHandleGETRequestWithListenersSearchMultipleResults() throws Inte String uri = "/hookRootURI/registrations/listeners"; HttpServerResponse mockResponse = mock(HttpServerResponse.class); GETRequest request = new GETRequest(uri, mockResponse); - request.addParameter("q", "listener"); // Search parameter that should match multiple listeners + request.addParameter("q", "myListener"); // Search parameter that should match multiple listeners // Add multiple listeners to the MockResourceStorage using the expected configuration and register them - String listenerId1 = "listener112222"; - String listenerId2 = "listener222133"; + String listenerId1 = "myListener112222"; + String listenerId2 = "myListener222133"; + String notMatchListener = "notMatchListener"; setListenerStorageEntryAndTriggerUpdate(buildListenerConfigWithHeadersFilter(null, listenerId1, "x-foo: (A|B)")); - Thread.sleep(500); + Thread.sleep(200); setListenerStorageEntryAndTriggerUpdate(buildListenerConfigWithHeadersFilter(null, listenerId2, "x-foo: (A|B)")); - Thread.sleep(500); + Thread.sleep(200); + setListenerStorageEntryAndTriggerUpdate(buildListenerConfigWithHeadersFilter(null, notMatchListener, "x-foo: (A|B)")); + Thread.sleep(200); // Mock the RoutingContext and set up the response capture when(routingContext.request()).thenReturn(request); @@ -752,6 +755,7 @@ public void testHandleGETRequestWithListenersSearchMultipleResults() throws Inte assertNotNull(jsonResponse); assertTrue(jsonResponse.contains(listenerId1)); assertTrue(jsonResponse.contains(listenerId2)); + assertFalse(jsonResponse.contains(notMatchListener)); } @@ -790,15 +794,18 @@ public void testHandleGETRequestWithRoutesSearchMultipleResults() throws Interru String uri = "/hookRootURI/registrations/routes"; HttpServerResponse mockResponse = mock(HttpServerResponse.class); GETRequest request = new GETRequest(uri, mockResponse); - request.addParameter("q", "route"); // Search parameter that should match multiple routes + request.addParameter("q", "valid"); // Search parameter that should match multiple routes // Add multiple routes to the MockResourceStorage using the expected configuration and register them - String routeId1 = "route12345"; - String routeId2 = "route67890"; + String routeId1 = "valid12345"; + String routeId2 = "valid67890"; + String notPreset = "notPreset"; setRouteStorageEntryAndTriggerUpdate(buildRouteConfig(routeId1)); - Thread.sleep(500); + Thread.sleep(200); setRouteStorageEntryAndTriggerUpdate(buildRouteConfig( routeId2)); - Thread.sleep(500); + Thread.sleep(200); + setRouteStorageEntryAndTriggerUpdate(buildRouteConfig( notPreset)); + Thread.sleep(200); // Mock the RoutingContext and set up the response capture when(routingContext.request()).thenReturn(request); @@ -815,6 +822,7 @@ public void testHandleGETRequestWithRoutesSearchMultipleResults() throws Interru assertNotNull(jsonResponse); assertTrue(jsonResponse.contains(routeId1)); assertTrue(jsonResponse.contains(routeId2)); + assertFalse(jsonResponse.contains(notPreset)); } @Test @@ -1242,10 +1250,6 @@ public String getParam(String paramName) { return params.get(paramName); } - public void addHeader(String headerName, String headerValue) { - headers.add(headerName, headerValue); - } - public void addParameter(String paramName, String paramValue) { params.add(paramName, paramValue); } diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java index 10a8ff95..5677403b 100755 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/ListenerTest.java @@ -888,92 +888,6 @@ public void testRequestForwardingForTwoListenerAtSameResourceButDifferentHeaders async.complete(); } - /** - * Checks if the DELETE request gets a response - * with the given status code. - * - * @param requestUrl - * @param statusCode - */ - private void checkDELETEStatusCode(String requestUrl, int statusCode) { - delete(requestUrl).then().assertThat().statusCode(statusCode); - } - - /** - * Checks if the DELETE request gets a response - * with the given status code. - * - * @param requestUrl - * @param statusCode - * @param headers - */ - private void checkDELETEStatusCode(String requestUrl, int statusCode, Headers headers) { - with().headers(headers).delete(requestUrl).then().assertThat().statusCode(statusCode); - } - - /** - * Checks if the PUT request gets a response - * with the given status code. - * - * @param requestUrl - * @param body - * @param statusCode - */ - private void checkPUTStatusCode(String requestUrl, String body, int statusCode) { - with().body(body).put(requestUrl).then().assertThat().statusCode(statusCode); - } - - /** - * Checks if the PUT request gets a response - * with the given status code. - * - * @param requestUrl - * @param body - * @param statusCode - * @param headers - */ - private void checkPUTStatusCode(String requestUrl, String body, int statusCode, Headers headers) { - with().headers(headers).body(body).put(requestUrl).then().assertThat().statusCode(statusCode); - } - - /** - * Checks if the GET request for the - * resource gets a response with - * the given status code. - * - * @param request - * @param statusCode - */ - private void checkGETStatusCodeWithAwait(final String request, final Integer statusCode) { - await().atMost(FIVE_SECONDS).until(() -> String.valueOf(when().get(request).getStatusCode()), equalTo(String.valueOf(statusCode))); - } - - /** - * Checks if the GET request of the - * given resource returns the wished body. - * - * @param requestUrl - * @param body - */ - private void checkGETBodyWithAwait(final String requestUrl, final String body) { - await().atMost(TEN_SECONDS).until(() -> when().get(requestUrl).then().extract().body().asString(), equalTo(body)); - } - - private Response searchWithQueryParam(String searchParam, String queryParam, int expectedStatusCode ) { - return given() - .queryParam(searchParam, queryParam) - .when().get(searchUrlBase ) - .then().assertThat().statusCode(expectedStatusCode) - .extract().response(); - } - private void registerDefaultListener() { - delete(); - initRoutingRules(); - delete(searchUrlBase); - delete(defaultTargetListener); - TestUtils.registerListener(defaultRegisterUrlListener, defaultTargetListener, defaultMethodsListener, null, null, - null, null, "x-foo: (A|B)"); - } @Test public void testSearchListenerWithValidAndInvalidSearchParam(TestContext context) { Async async = context.async(); @@ -984,7 +898,7 @@ public void testSearchListenerWithValidAndInvalidSearchParam(TestContext context Response response = searchWithQueryParam("q",defaultListenerName,200); - Assert.assertTrue(response.getBody().asString().contains(defaultListenerName)); // Fails if not found + Assert.assertTrue(response.getBody().asString().contains(defaultListenerName)); TestUtils.unregisterListener(defaultRegisterUrlListener); async.complete(); @@ -1023,7 +937,6 @@ public void testHookHandleSearch_NoListenersRegistered(TestContext context) { // Parse response body as JSON JsonObject jsonResponse = new JsonObject(response.getBody().asString()); - // Assert that the response contains the expected query param Assert.assertTrue("Expected 'listeners' to be an empty array", jsonResponse.containsKey("listeners") && jsonResponse.getJsonArray("listeners").isEmpty()); async.complete(); @@ -1066,13 +979,13 @@ public void testHookHandleSearch_UseSlashInSearchUrlBase(TestContext context) { registerDefaultListener(); Response response = given() - .queryParam("q", defaultListenerName) - .when().get(searchUrlBase +"/") - .then().assertThat().statusCode(200) - .extract().response(); + .queryParam("q", defaultListenerName) + .when().get(searchUrlBase +"/") + .then().assertThat().statusCode(200) + .extract().response(); // Assert that the response contains the expected query param - Assert.assertTrue(response.getBody().asString().contains(defaultListenerName)); // Fails if not found + Assert.assertTrue(response.getBody().asString().contains(defaultListenerName)); TestUtils.unregisterListener(defaultRegisterUrlListener); @@ -1088,8 +1001,7 @@ public void testHookHandleSearch_EmptyQueryParameter(TestContext context) { Response response = searchWithQueryParam("q","",400); - // Assert that the response contains the expected query param - Assert.assertTrue(response.getBody().asString().contains("Bad Request")); // Fails if not found + Assert.assertTrue(response.getBody().asString().contains("Bad Request")); TestUtils.unregisterListener(defaultRegisterUrlListener); async.complete(); @@ -1185,4 +1097,89 @@ public void testSearchListener_IdenticalResultsWithAndWithoutQueryParam(TestCont TestUtils.unregisterListener(defaultRegisterUrlListener); async.complete(); } + + /** + * Checks if the DELETE request gets a response + * with the given status code. + * + * @param requestUrl + * @param statusCode + */ + private void checkDELETEStatusCode(String requestUrl, int statusCode) { + delete(requestUrl).then().assertThat().statusCode(statusCode); + } + + /** + * Checks if the DELETE request gets a response + * with the given status code. + * + * @param requestUrl + * @param statusCode + * @param headers + */ + private void checkDELETEStatusCode(String requestUrl, int statusCode, Headers headers) { + with().headers(headers).delete(requestUrl).then().assertThat().statusCode(statusCode); + } + + /** + * Checks if the PUT request gets a response + * with the given status code. + * + * @param requestUrl + * @param body + * @param statusCode + */ + private void checkPUTStatusCode(String requestUrl, String body, int statusCode) { + with().body(body).put(requestUrl).then().assertThat().statusCode(statusCode); + } + + /** + * Checks if the PUT request gets a response + * with the given status code. + * + * @param requestUrl + * @param body + * @param statusCode + * @param headers + */ + private void checkPUTStatusCode(String requestUrl, String body, int statusCode, Headers headers) { + with().headers(headers).body(body).put(requestUrl).then().assertThat().statusCode(statusCode); + } + + /** + * Checks if the GET request for the + * resource gets a response with + * the given status code. + * + * @param request + * @param statusCode + */ + private void checkGETStatusCodeWithAwait(final String request, final Integer statusCode) { + await().atMost(FIVE_SECONDS).until(() -> String.valueOf(when().get(request).getStatusCode()), equalTo(String.valueOf(statusCode))); + } + + /** + * Checks if the GET request of the + * given resource returns the wished body. + * + * @param requestUrl + * @param body + */ + private void checkGETBodyWithAwait(final String requestUrl, final String body) { + await().atMost(TEN_SECONDS).until(() -> when().get(requestUrl).then().extract().body().asString(), equalTo(body)); + } + + private Response searchWithQueryParam(String searchParam, String queryParam, int expectedStatusCode ) { + return given() + .queryParam(searchParam, queryParam) + .when().get(searchUrlBase ) + .then().assertThat().statusCode(expectedStatusCode) + .extract().response(); + } + private void registerDefaultListener() { + delete(); + initRoutingRules(); + TestUtils.registerListener(defaultRegisterUrlListener, defaultTargetListener, defaultMethodsListener, null, null, + null, null, "x-foo: (A|B)"); + } } diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java index 4ad868ba..16193b24 100644 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java @@ -195,40 +195,6 @@ public void testListingWithoutStaticAndDynamicCollections(TestContext context) { async.complete(); } - private void removeRoute(String name) { - String route = requestUrlBase + "/" + name + TestUtils.getHookRouteUrlSuffix(); - TestUtils.unregisterRoute(route); - } - - private void addRoute(String name, boolean collection, boolean listable) { - String route = requestUrlBase + "/" + name + TestUtils.getHookRouteUrlSuffix(); - String target = targetUrlBase + "/" + name; - String[] methods = new String[]{"GET", "PUT", "DELETE", "POST"}; - - // just for security reasons (unregister route) - TestUtils.unregisterRoute(route); - - TestUtils.registerRoute(route, target, methods, null, collection, listable); - } - - private void assertResponse(final Response response, final String[] expectedArray) { - Assert.assertEquals(200, response.statusCode()); - String bodyString = response.getBody().asString(); - System.out.println("BODY => " + bodyString + " <="); - JsonObject body = new JsonObject(bodyString); - JsonArray array = body.getJsonArray(parentKey); - Assert.assertEquals(expectedArray.length, array.size()); - Assert.assertThat(array, Matchers.contains(expectedArray)); - } - - private Response searchWithQueryParam(String searchParam, String queryParam, int expectedStatusCode ) { - return given() - .queryParam(searchParam, queryParam) - .when().get(searchUrlBase ) - .then().assertThat().statusCode(expectedStatusCode) - .extract().response(); - } - @Test public void testHookHandleSearch_WithValidAndInvalidSearchParam(TestContext context) { Async async = context.async(); @@ -268,18 +234,16 @@ public void testHookHandleSearch_RouteNonMatchingQueryParam(TestContext context) addRoute(queryParam, true, true); assertResponse(get(requestUrlBase), new String[]{queryParam+"/"}); - // Send GET request with a non-matching query param Response response = searchWithQueryParam("q", queryParam, 200); - Assert.assertTrue("Query param should be found in response", response.getBody().asString().contains(queryParam)); - // Send GET request with a non-matching query param response = searchWithQueryParam("q", nonMatchingQueryParam, 200); - - // Assert the response does not contain the non-matching query param Assert.assertFalse("Non-matching query param should not be found in response", response.getBody().asString().contains(nonMatchingQueryParam)); + JsonObject jsonResponse = new JsonObject(response.getBody().asString()); + Assert.assertTrue("Expected 'routes' to be an empty array", + jsonResponse.containsKey("routes") && jsonResponse.getJsonArray("routes").isEmpty()); removeRoute(queryParam); async.complete(); @@ -339,4 +303,38 @@ public void testHookHandleSearch_ReturnsIdenticalResultsWithAndWithoutSearchQuer async.complete(); } + + private void removeRoute(String name) { + String route = requestUrlBase + "/" + name + TestUtils.getHookRouteUrlSuffix(); + TestUtils.unregisterRoute(route); + } + + private void addRoute(String name, boolean collection, boolean listable) { + String route = requestUrlBase + "/" + name + TestUtils.getHookRouteUrlSuffix(); + String target = targetUrlBase + "/" + name; + String[] methods = new String[]{"GET", "PUT", "DELETE", "POST"}; + + // just for security reasons (unregister route) + TestUtils.unregisterRoute(route); + + TestUtils.registerRoute(route, target, methods, null, collection, listable); + } + + private void assertResponse(final Response response, final String[] expectedArray) { + Assert.assertEquals(200, response.statusCode()); + String bodyString = response.getBody().asString(); + System.out.println("BODY => " + bodyString + " <="); + JsonObject body = new JsonObject(bodyString); + JsonArray array = body.getJsonArray(parentKey); + Assert.assertEquals(expectedArray.length, array.size()); + Assert.assertThat(array, Matchers.contains(expectedArray)); + } + + private Response searchWithQueryParam(String searchParam, String queryParam, int expectedStatusCode ) { + return given() + .queryParam(searchParam, queryParam) + .when().get(searchUrlBase ) + .then().assertThat().statusCode(expectedStatusCode) + .extract().response(); + } } From 12f4d783752cbfe4d256a8afbb3eb15f53dc3bcf Mon Sep 17 00:00:00 2001 From: almeidast Date: Thu, 14 Nov 2024 20:05:26 +0000 Subject: [PATCH 36/37] optimize testes creating reusable functions --- .../gateleen/hook/HookHandlerTest.java | 93 +++++++++++-------- .../gateleen/hook/RouteListingTest.java | 2 - 2 files changed, 54 insertions(+), 41 deletions(-) diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index 4a8dd393..6d9021fd 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -50,6 +50,8 @@ @RunWith(VertxUnitRunner.class) public class HookHandlerTest { private static final String HOOK_ROOT_URI = "hookRootURI/"; + private static final String HOOK_LISTENER_URI = "/"+ HOOK_ROOT_URI + "registrations/listeners"; + String HOOK_ROUTE_URI = "/"+ HOOK_ROOT_URI + "registrations/routes"; private static final Logger logger = LoggerFactory.getLogger(HookHandlerTest.class); private Vertx vertx; private HttpClient httpClient; @@ -63,7 +65,7 @@ public class HookHandlerTest { private HookHandler hookHandler; private RoutingContext routingContext; - + private HttpServerResponse mockResponse; @Before public void setUp() { @@ -77,6 +79,7 @@ public void setUp() { monitoringHandler = mock(MonitoringHandler.class); requestQueue = mock(RequestQueue.class); reducedPropagationManager = mock(ReducedPropagationManager.class); + mockResponse = mock(HttpServerResponse.class); hookHandler = new HookHandler(vertx, httpClient, storage, loggingResourceManager, logAppenderRepository, monitoringHandler, "userProfilePath", HOOK_ROOT_URI, requestQueue, false, reducedPropagationManager); hookHandler.init(); @@ -664,9 +667,7 @@ public void hookRegistration_RouteWithRouteMultiplier(TestContext testContext) t @Test public void testHandleGETRequestWithEmptyParam(TestContext testContext) { // Define URI and configures the request with an empty 'q' parameter - String uri = "/hookRootURI/registrations/listeners"; - HttpServerResponse mockResponse = mock(HttpServerResponse.class); - GETRequest request = new GETRequest(uri, mockResponse); + GETRequest request = new GETRequest(HOOK_LISTENER_URI, mockResponse); request.addParameter("q", ""); // Empty parameter to simulate bad request // Mock RoutingContext @@ -690,14 +691,33 @@ public void testHandleGETRequestWithEmptyParam(TestContext testContext) { testContext.assertTrue(jsonResponse.contains("Bad Request")); } + @Test + public void testHandleGETRequestWithNoParam(TestContext testContext) { + GETRequest request = new GETRequest(HOOK_LISTENER_URI, mockResponse); + when(routingContext.request()).thenReturn(request); + + boolean result = hookHandler.handle(routingContext); + + testContext.assertFalse(result); + } + + @Test + public void testHandleGETRequestWithWrongRoute(TestContext testContext) { + String wrongUri = "/hookRootURI/registrati/listeners"; + GETRequest request = new GETRequest(wrongUri, mockResponse); + request.addParameter("q", "value"); + when(routingContext.request()).thenReturn(request); + + boolean result = hookHandler.handle(routingContext); + + testContext.assertFalse(result); + } @Test public void testHandleGETRequestWithListenersSearchSingleResult() throws InterruptedException { // Define URI and configure GET request with specific search parameter String singleListener= "mySingleListener"; - String uri = "/hookRootURI/registrations/listeners"; - HttpServerResponse mockResponse = mock(HttpServerResponse.class); - GETRequest request = new GETRequest(uri, mockResponse); + GETRequest request = new GETRequest(HOOK_LISTENER_URI, mockResponse); request.addParameter("q", singleListener); @@ -724,9 +744,7 @@ public void testHandleGETRequestWithListenersSearchSingleResult() throws Interru @Test public void testHandleGETRequestWithListenersSearchMultipleResults() throws InterruptedException { // Define the URI and set up the GET request with a broader search parameter for multiple listeners - String uri = "/hookRootURI/registrations/listeners"; - HttpServerResponse mockResponse = mock(HttpServerResponse.class); - GETRequest request = new GETRequest(uri, mockResponse); + GETRequest request = new GETRequest(HOOK_LISTENER_URI, mockResponse); request.addParameter("q", "myListener"); // Search parameter that should match multiple listeners // Add multiple listeners to the MockResourceStorage using the expected configuration and register them @@ -762,13 +780,11 @@ public void testHandleGETRequestWithListenersSearchMultipleResults() throws Inte @Test public void testHandleGETRequestWithRoutesSearchEmptyResult() { // Define URI and configure request with specific 'q' parameter for routes search - String uri = "/hookRootURI/registrations/routes"; - HttpServerResponse mockResponse = mock(HttpServerResponse.class); - GETRequest request = new GETRequest(uri, mockResponse); + GETRequest request = new GETRequest(HOOK_ROUTE_URI, mockResponse); request.addParameter("q", "routeNotFound"); // No routes are added to MockResourceStorage to simulate empty result - storage.putMockData("hookRootURI/registrations/routes", new JsonArray().encode()); + storage.putMockData(HOOK_ROUTE_URI, new JsonArray().encode()); // Mock RoutingContext and configure response capture when(routingContext.request()).thenReturn(request); @@ -784,16 +800,17 @@ public void testHandleGETRequestWithRoutesSearchEmptyResult() { assertTrue(result); // Verify response content with empty result - String jsonResponse = responseCaptor.getValue(); - assertNotNull(jsonResponse); - assertEquals("{\"routes\":[]}", jsonResponse); + String actualResponse = responseCaptor.getValue(); + assertNotNull(actualResponse); + JsonObject jsonResponse = new JsonObject(actualResponse); + assertTrue("Expected 'routes' to be an empty array", + jsonResponse.containsKey("routes") && jsonResponse.getJsonArray("routes").isEmpty()); } + @Test public void testHandleGETRequestWithRoutesSearchMultipleResults() throws InterruptedException { // Define the URI and set up the GET request with a broad search parameter for multiple routes - String uri = "/hookRootURI/registrations/routes"; - HttpServerResponse mockResponse = mock(HttpServerResponse.class); - GETRequest request = new GETRequest(uri, mockResponse); + GETRequest request = new GETRequest(HOOK_ROUTE_URI, mockResponse); request.addParameter("q", "valid"); // Search parameter that should match multiple routes // Add multiple routes to the MockResourceStorage using the expected configuration and register them @@ -828,9 +845,7 @@ public void testHandleGETRequestWithRoutesSearchMultipleResults() throws Interru @Test public void testHandleListenerWithStorageAndEmptyList() { // Set up the URI for listeners registration - String uri = "hookRootURI/registrations/listeners"; - HttpServerResponse response = mock(HttpServerResponse.class); - GETRequest request = new GETRequest(uri,response) ; + GETRequest request = new GETRequest(HOOK_LISTENER_URI,mockResponse) ; request.addParameter("q", "validQueryParam"); // Capture the response output @@ -841,20 +856,18 @@ public void testHandleListenerWithStorageAndEmptyList() { boolean result = hookHandler.handle(routingContext); assertTrue(result); - verify(response).end(responseCaptor.capture()); + verify(mockResponse).end(responseCaptor.capture()); // Validate the response JSON for an empty listener list String actualResponse = responseCaptor.getValue(); - assertNotNull(actualResponse); - assertEquals("{\"listeners\":[]}", actualResponse); // Response should contain an empty list + assertEmptyResult(actualResponse); + } @Test public void testHandleGETRequestWithExtraParam(TestContext testContext) { // Define URI and configure the request with an extra parameter besides 'q' - String uri = "/hookRootURI/registrations/listeners"; - HttpServerResponse mockResponse = mock(HttpServerResponse.class); - GETRequest request = new GETRequest(uri, mockResponse); + GETRequest request = new GETRequest(HOOK_LISTENER_URI, mockResponse); request.addParameter("q", "validQueryParam"); request.addParameter("extra", "notAllowedParam"); // Extra parameter, not allowed @@ -883,9 +896,7 @@ public void testHandleGETRequestWithExtraParam(TestContext testContext) { @Test public void testHandleGETRequestWithTrailingSlash(TestContext testContext) { // Define URI with trailing slash and configure the request - String uri = "/hookRootURI/registrations/listeners/"; - HttpServerResponse mockResponse = mock(HttpServerResponse.class); - GETRequest request = new GETRequest(uri, mockResponse); + GETRequest request = new GETRequest(HOOK_LISTENER_URI, mockResponse); request.addParameter("q", "validQueryParam"); // Mock the RoutingContext @@ -901,16 +912,13 @@ public void testHandleGETRequestWithTrailingSlash(TestContext testContext) { // Verify the result contains an empty listeners list testContext.assertTrue(result); String jsonResponse = responseCaptor.getValue(); - testContext.assertNotNull(jsonResponse); - testContext.assertEquals("{\"listeners\":[]}", jsonResponse); + assertEmptyResult(jsonResponse); } @Test public void testHandleGETRequestWithInvalidParam(TestContext testContext) { // Define URI with an invalid parameter different from 'q' - String uri = "/hookRootURI/registrations/listeners"; - HttpServerResponse mockResponse = mock(HttpServerResponse.class); - GETRequest request = new GETRequest(uri, mockResponse); + GETRequest request = new GETRequest(HOOK_LISTENER_URI, mockResponse); request.addParameter("invalidParam", "someValue"); // Invalid parameter, not 'q' // Mock the RoutingContext @@ -1172,6 +1180,13 @@ private Buffer toBuffer(JsonObject jsonObject) { return buffer; } + private static void assertEmptyResult(String actualResponse) { + assertNotNull(actualResponse); + JsonObject jsonResponse = new JsonObject(actualResponse); + assertTrue("Expected 'listeners' to be an empty array", + jsonResponse.containsKey("listeners") && jsonResponse.getJsonArray("listeners").isEmpty()); + } + static class PUTRequest extends DummyHttpServerRequest { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); @@ -1212,8 +1227,8 @@ public void addHeader(String headerName, String headerValue) { static class GETRequest extends DummyHttpServerRequest { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); MultiMap params = MultiMap.caseInsensitiveMultiMap(); - private HttpServerResponse response; - private String uri; + private final HttpServerResponse response; + private final String uri; public GETRequest(String uri, HttpServerResponse response) { this.uri = uri; diff --git a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java index 16193b24..a61332da 100644 --- a/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java +++ b/gateleen-test/src/test/java/org/swisspush/gateleen/hook/RouteListingTest.java @@ -239,8 +239,6 @@ public void testHookHandleSearch_RouteNonMatchingQueryParam(TestContext context) response.getBody().asString().contains(queryParam)); response = searchWithQueryParam("q", nonMatchingQueryParam, 200); - Assert.assertFalse("Non-matching query param should not be found in response", - response.getBody().asString().contains(nonMatchingQueryParam)); JsonObject jsonResponse = new JsonObject(response.getBody().asString()); Assert.assertTrue("Expected 'routes' to be an empty array", jsonResponse.containsKey("routes") && jsonResponse.getJsonArray("routes").isEmpty()); From d7272ec3f08527a4fd6bf8bf85ad8d6b1349554a Mon Sep 17 00:00:00 2001 From: almeidast Date: Thu, 14 Nov 2024 20:57:13 +0000 Subject: [PATCH 37/37] Fix testHandleGETRequestWithTrailingSlash --- .../test/java/org/swisspush/gateleen/hook/HookHandlerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java index 6d9021fd..a0c7a63d 100644 --- a/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java +++ b/gateleen-hook/src/test/java/org/swisspush/gateleen/hook/HookHandlerTest.java @@ -896,7 +896,7 @@ public void testHandleGETRequestWithExtraParam(TestContext testContext) { @Test public void testHandleGETRequestWithTrailingSlash(TestContext testContext) { // Define URI with trailing slash and configure the request - GETRequest request = new GETRequest(HOOK_LISTENER_URI, mockResponse); + GETRequest request = new GETRequest(HOOK_LISTENER_URI+"/", mockResponse); request.addParameter("q", "validQueryParam"); // Mock the RoutingContext