From ca7ffec72804f57d3d8a068dd9a4bd5ba5b38980 Mon Sep 17 00:00:00 2001 From: Joshua Palis Date: Thu, 13 Jul 2023 13:13:48 -0700 Subject: [PATCH 1/2] Initial Hello World Extension integration test Signed-off-by: Ryan Bogan --- build.gradle | 24 ++- .../integTest/TransportCommunicationIT.java | 169 ------------------ .../helloworld/rest/TestHelloWorldIT.java | 124 +++++++++++++ 3 files changed, 139 insertions(+), 178 deletions(-) delete mode 100644 src/test/java/org/opensearch/sdk/integTest/TransportCommunicationIT.java create mode 100644 src/test/java/org/opensearch/sdk/sample/helloworld/rest/TestHelloWorldIT.java diff --git a/build.gradle b/build.gradle index 5d61109e..1b95e02b 100644 --- a/build.gradle +++ b/build.gradle @@ -273,10 +273,15 @@ task integTest(type: RestIntegTestTask) { testClassesDirs = sourceSets.test.output.classesDirs classpath = sourceSets.test.runtimeClasspath } -//tasks.named("check").configure { dependsOn(integTest) } - -task startTestExtension(type: Exec) { - commandLine 'bash', '-c', "./gradlew helloWorld &" +tasks.named("check").configure { dependsOn(integTest) } + +task startTestExtension { + doFirst{ + ext.process = new ProcessBuilder() + .directory(projectDir) + .command("./gradlew", "helloWorld") + .start() + } } integTest { @@ -310,10 +315,12 @@ integTest { } } -def testExtensionYml = new org.yaml.snakeyaml.Yaml().load(new File("src/test/resources/hello-world-extension.yml").newInputStream()) - -task closeTestExtension (type: Exec) { - commandLine 'bash', '-c', "kill \$(lsof -i:${testExtensionYml.port})" +task closeTestExtension { + doFirst { + if (tasks.startTestExtension.process != null) { + tasks.startTestExtension.process.destroy() + } + } } tasks.named("integTest").configure { finalizedBy(closeTestExtension) } @@ -333,5 +340,4 @@ testClusters.integTest { debugPort += 1 } } - } diff --git a/src/test/java/org/opensearch/sdk/integTest/TransportCommunicationIT.java b/src/test/java/org/opensearch/sdk/integTest/TransportCommunicationIT.java deleted file mode 100644 index 1cae4d08..00000000 --- a/src/test/java/org/opensearch/sdk/integTest/TransportCommunicationIT.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -package org.opensearch.sdk; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.opensearch.common.component.Lifecycle; -import org.opensearch.common.network.NetworkAddress; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; -import org.opensearch.transport.netty4.Netty4Transport; -import org.opensearch.test.OpenSearchIntegTestCase; -import org.opensearch.threadpool.ThreadPool; -import org.opensearch.transport.TransportService; -import org.opensearch.transport.TransportSettings; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.net.Socket; -import java.nio.charset.StandardCharsets; - -public class TransportCommunicationIT extends OpenSearchIntegTestCase { - - private Settings settings; - private final int port = 7777; - private final String host = "127.0.0.1"; - private volatile String clientResult; - private ExtensionsRunner extensionsRunner; - private NettyTransport nettyTransport; - - @Override - @BeforeEach - public void setUp() throws IOException { - - // Configure settings for transport serivce using the same port number used to bind the client - settings = Settings.builder() - .put("node.name", "node_extension_test") - .put(TransportSettings.BIND_HOST.getKey(), host) - .put(TransportSettings.PORT.getKey(), port) - .build(); - this.extensionsRunner = new ExtensionsRunnerForTest(); - this.nettyTransport = new NettyTransport(extensionsRunner); - } - - @Test - public void testSocketSetup() throws IOException { - - ThreadPool threadPool = new TestThreadPool("test"); - Netty4Transport transport = nettyTransport.getNetty4Transport(settings, threadPool); - - // start netty transport and ensure that address info is exposed - try { - transport.start(); - assertEquals(Lifecycle.State.STARTED, transport.lifecycleState()); - - // check bound addresses - for (TransportAddress transportAddress : transport.boundAddress().boundAddresses()) { - assertNotNull(transportAddress); - assertEquals(host, transportAddress.getAddress()); - assertEquals(port, transportAddress.getPort()); - } - - // check publish addresses - assertNotNull(transport.boundAddress().publishAddress()); - TransportAddress publishAddress = transport.boundAddress().publishAddress(); - assertEquals(host, NetworkAddress.format(publishAddress.address().getAddress())); - assertEquals(port, publishAddress.address().getPort()); - - } finally { - // terminate server socket and thread pool - transport.close(); - assertEquals(Lifecycle.State.CLOSED, transport.lifecycleState()); - - terminate(threadPool); - } - } - - @Test - public void testInvalidMessageFormat() throws IOException, InterruptedException { - Thread client = new Thread() { - @Override - public void run() { - try { - - // Connect to the server - Socket socket = new Socket(host, port); - - // Create input/output stream to read/write to server - BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)); - PrintStream out = new PrintStream(socket.getOutputStream(), true, StandardCharsets.UTF_8); - - // note : message validation is only done if message length >= 6 bytes - out.println("TRANSPORT_TEST"); - - // disconnection by foreign host indicated by a read return value of -1 - clientResult = String.valueOf(in.read()); - - // Close stream and socket connection - out.close(); - socket.close(); - - } catch (Exception e) { - e.printStackTrace(); - } - } - }; - - // start transport service and attempt tcp client connection - startTransportandClient(settings, client); - - // expecting -1 from client attempt to read from server, indicating connection closed by host - assertEquals("-1", clientResult); - } - - @Test - public void testMismatchingPort() throws IOException, InterruptedException { - - Thread client = new Thread() { - @Override - public void run() { - try { - Socket socket = new Socket(host, 0); - socket.close(); - } catch (Exception e) { - clientResult = "connection refused"; - } - - } - }; - - // start transport service and attempt client connection - startTransportandClient(settings, client); - - // confirm that connect exception was caught - assertEquals("connection refused", clientResult); - } - - private void startTransportandClient(Settings settings, Thread client) throws IOException, InterruptedException { - - // retrieve transport service - ExtensionsRunner extensionsRunner = new ExtensionsRunnerForTest(); - // start transport service - ThreadPool threadPool = new ThreadPool(settings); - TransportService transportService = nettyTransport.initializeExtensionTransportService(settings, threadPool); - - assertEquals(Lifecycle.State.STARTED, transportService.lifecycleState()); - - // connect client server to transport service - client.start(); - - // listen for messages, set timeout to close server socket connection - extensionsRunner.startActionListener(1000); - - // wait for client thread to finish execution, then close server socket connection - client.join(); - transportService.close(); - assertEquals(Lifecycle.State.CLOSED, transportService.lifecycleState()); - } - -} diff --git a/src/test/java/org/opensearch/sdk/sample/helloworld/rest/TestHelloWorldIT.java b/src/test/java/org/opensearch/sdk/sample/helloworld/rest/TestHelloWorldIT.java new file mode 100644 index 00000000..19e8c1a7 --- /dev/null +++ b/src/test/java/org/opensearch/sdk/sample/helloworld/rest/TestHelloWorldIT.java @@ -0,0 +1,124 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.sdk.sample.helloworld.rest; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.Callable; + +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Test; +import org.opensearch.client.Request; +import org.opensearch.client.Response; +import org.opensearch.client.RestClient; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.test.rest.OpenSearchRestTestCase; + +public class TestHelloWorldIT extends OpenSearchRestTestCase { + private static final Logger logger = LogManager.getLogger(TestHelloWorldIT.class); + + public static final String EXTENSION_INIT_URI = "/_extensions/initialize/"; + public static final String HELLO_WORLD_EXTENSION_BASE_URI = "/_extensions/_hello-world"; + public static final String HELLO_BASE_URI = HELLO_WORLD_EXTENSION_BASE_URI + "/hello"; + public static final String HELLO_NAME_URI = HELLO_BASE_URI + "/%s"; + public static final String GOODBYE_URI = HELLO_WORLD_EXTENSION_BASE_URI + "/goodbye"; + + // TODO : Move extension initialization to setUp method prior to adding addtional tests + + @Test + public void testInitializeExtension() throws Exception { + // Send initialization request + String helloWorldInitRequestBody = "{\"name\":\"hello-world\"" + + ",\"uniqueId\":\"hello-world\"" + + ",\"hostAddress\":\"127.0.0.1\"" + + ",\"port\":\"4500\"" + + ",\"version\":\"1.0\"" + + ",\"opensearchVersion\":\"3.0.0\"" + + ",\"minimumCompatibleVersion\":\"3.0.0\"}"; + Response response = makeRequest(client(), "POST", EXTENSION_INIT_URI, null, toHttpEntity(helloWorldInitRequestBody)); + + assertEquals(RestStatus.ACCEPTED, restStatus(response)); + Map responseMap = entityAsMap(response); + String initializationResponse = (String) responseMap.get("success"); + assertEquals("A request to initialize an extension has been sent.", initializationResponse); + } + + /** + * Retrieves the REST status from a response + * + * @param response the REST response + * @return the RestStatus of the response + * + */ + private static RestStatus restStatus(Response response) { + return RestStatus.fromCode(response.getStatusLine().getStatusCode()); + } + + /** + * Converts a JSON string into an HttpEntity + * + * @param jsonString The JSON string + * @return the HttpEntity + * + */ + private static HttpEntity toHttpEntity(String jsonString) throws IOException { + return new StringEntity(jsonString, ContentType.APPLICATION_JSON); + } + + /** + * Helper method to send a REST Request + * + * @param client The REST client + * @param method The request method + * @param endpoint The REST endpoint + * @param params The request parameters + * @param entity The request body + * @return the REST response + * + */ + private static Response makeRequest(RestClient client, String method, String endpoint, Map params, HttpEntity entity) + throws IOException { + + // Create request + Request request = new Request(method, endpoint); + if (params != null) { + params.entrySet().forEach(it -> request.addParameter(it.getKey(), it.getValue())); + } + if (entity != null) { + request.setEntity(entity); + } + return client.performRequest(request); + } + + /** + * Invokes the callable method and asserts that the expected exception is thrown with the given exception message + * + * @param clazz The exception class + * @param message The exception message + * @param callable The callable request method + * + */ + private static void assertFailWith(Class clazz, String message, Callable callable) throws Exception { + try { + callable.call(); + } catch (Throwable e) { + if (e.getClass() != clazz) { + throw e; + } + if (message != null && !e.getMessage().contains(message)) { + throw e; + } + } + } +} From d528ffad1f389cf5ef270b6e822bab56dbf4f597 Mon Sep 17 00:00:00 2001 From: Ryan Bogan Date: Fri, 14 Jul 2023 18:47:11 +0000 Subject: [PATCH 2/2] Change imports and opensearch version on initialize request Signed-off-by: Ryan Bogan --- .../sdk/sample/helloworld/rest/TestHelloWorldIT.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/opensearch/sdk/sample/helloworld/rest/TestHelloWorldIT.java b/src/test/java/org/opensearch/sdk/sample/helloworld/rest/TestHelloWorldIT.java index 19e8c1a7..a9235020 100644 --- a/src/test/java/org/opensearch/sdk/sample/helloworld/rest/TestHelloWorldIT.java +++ b/src/test/java/org/opensearch/sdk/sample/helloworld/rest/TestHelloWorldIT.java @@ -13,16 +13,16 @@ import java.util.Map; import java.util.concurrent.Callable; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.HttpEntity; -import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.http.HttpEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.Test; import org.opensearch.client.Request; import org.opensearch.client.Response; import org.opensearch.client.RestClient; -import org.opensearch.core.rest.RestStatus; +import org.opensearch.rest.RestStatus; import org.opensearch.test.rest.OpenSearchRestTestCase; public class TestHelloWorldIT extends OpenSearchRestTestCase { @@ -44,8 +44,8 @@ public void testInitializeExtension() throws Exception { + ",\"hostAddress\":\"127.0.0.1\"" + ",\"port\":\"4500\"" + ",\"version\":\"1.0\"" - + ",\"opensearchVersion\":\"3.0.0\"" - + ",\"minimumCompatibleVersion\":\"3.0.0\"}"; + + ",\"opensearchVersion\":\"2.9.0\"" + + ",\"minimumCompatibleVersion\":\"2.9.0\"}"; Response response = makeRequest(client(), "POST", EXTENSION_INIT_URI, null, toHttpEntity(helloWorldInitRequestBody)); assertEquals(RestStatus.ACCEPTED, restStatus(response));