Repository: authorjapps/zerocode
Pull request #299 (2019-08-20 07:08:38)

ISS-22 - Http Max Timeout or Implicit Wait


Patch:
diff --git a/core/src/main/java/org/jsmart/zerocode/core/httpclient/BasicHttpClient.java b/core/src/main/java/org/jsmart/zerocode/core/httpclient/BasicHttpClient.java
index 8b7731d2..d072a0e1 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/httpclient/BasicHttpClient.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/httpclient/BasicHttpClient.java
@@ -405,7 +405,7 @@ public class BasicHttpClient {
                 return createFormUrlEncodedRequestBuilder(httpUrl, methodName, reqBodyAsString);
             }
             // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-            // Extention- Any other header types to be specially handled here
+            // Extension - Any other header types to be specially handled here
             // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
             // else if(contentType.equals("OTHER-TYPES")){
             //    Handling logic
diff --git a/core/src/main/java/org/jsmart/zerocode/core/httpclient/ssl/SslTrustHttpClient.java b/core/src/main/java/org/jsmart/zerocode/core/httpclient/ssl/SslTrustHttpClient.java
index bf806bab..8dd74763 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/httpclient/ssl/SslTrustHttpClient.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/httpclient/ssl/SslTrustHttpClient.java
@@ -1,6 +1,9 @@
 package org.jsmart.zerocode.core.httpclient.ssl;
 
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
 import org.apache.http.client.CookieStore;
+import org.apache.http.client.config.RequestConfig;
 import org.apache.http.conn.ssl.NoopHostnameVerifier;
 import org.apache.http.ssl.SSLContextBuilder;
 import org.apache.http.impl.client.BasicCookieStore;
@@ -15,9 +18,27 @@ import java.security.KeyManagementException;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 
-public class SslTrustHttpClient extends BasicHttpClient{
+public class SslTrustHttpClient extends BasicHttpClient {
     private static final Logger LOGGER = LoggerFactory.getLogger(SslTrustHttpClient.class);
 
+    /**
+     * This is an optional config for the HttpClient.
+     * If this is configured, then the client will implicitly wait till the configured
+     * amount of time for the server response.
+     * If it doesn't receive the response from the server within that time,
+     * then it will throw java.net.SocketTimeoutException.
+     * <p>
+     * If this is not configured, then the client will continue working as usual.
+     * In this case :
+     * e.g. if the server takes more time to respond, the client will keep on waiting
+     * till the server responds or till a network-timeout occurs.
+     */
+    public static final String HTTP_MAX_TIMEOUT_MILLISECONDS = "http.max.timeout.milliseconds";
+
+    @Inject(optional = true)
+    @Named(HTTP_MAX_TIMEOUT_MILLISECONDS)
+    private Integer implicitWait;
+
     public SslTrustHttpClient() {
         super();
     }
@@ -29,11 +50,11 @@ public class SslTrustHttpClient extends BasicHttpClient{
     /**
      * This method has been overridden here simply to show how a custom/project-specific http client
      * can be plugged into the framework.
-     *
+     * <p>
      * e.g. You can create your own project specific http client needed for http/https/tls connections.
      * Sometimes you may not need a SSLContext, sometimes you need one, some other times you need a
      * simple default http client e.g. HttpClients.createDefault() provided by Apache.
-     *
+     * <p>
      * If you do not override this method, the framework creates a http client suitable for both http/https.
      */
     @Override
@@ -45,12 +66,51 @@ public class SslTrustHttpClient extends BasicHttpClient{
 
         CookieStore cookieStore = new BasicCookieStore();
 
+        RequestConfig timeOutConfig = createMaxTimeOutConfig();
+
         return HttpClients.custom()
                 .setSSLContext(sslContext)
                 .setSSLHostnameVerifier(new NoopHostnameVerifier())
                 .setDefaultCookieStore(cookieStore)
+                .setDefaultRequestConfig(timeOutConfig)
                 .build();
     }
 
+    /**
+     * Timeout Parameters:
+     * 1) setConnectTimeout – The time needed to establish the connection with the remote host
+     * 2) setSocketTimeout – The time needed to wait for the data after establishing the connection to remote host
+     * 3) setConnectionRequestTimeout – The time needed to wait to get a connection from the connection manager/pool
+     *
+     * @return RequestConfig
+     */
+    private RequestConfig createMaxTimeOutConfig() {
+        RequestConfig timeOutConfig;
+        if (implicitWait == null) {
+            timeOutConfig = RequestConfig.DEFAULT;
+            LOGGER.warn("\n*Implicit-Wait/Connection-Timeout not configured.*" +
+                            "\nE.g. to configure it for 10sec, use: '{}={}' in the host-config properties. " +
+                            "\n**You can safely ignore this warning to retain the default httpClient behavior**\n",
+                    HTTP_MAX_TIMEOUT_MILLISECONDS, 10000);
+        } else {
+            int timeout = implicitWait.intValue();
+            timeOutConfig = RequestConfig.custom()
+                    .setConnectTimeout(timeout)
+                    .setSocketTimeout(timeout)
+                    .setConnectionRequestTimeout(timeout)
+                    .build();
+            LOGGER.info("\n----------------------------------------------------------------\n" +
+                    "Implicit-Wait/Connection-Timeout config = " + implicitWait +
+                    " milli-second." +
+                    "\n----------------------------------------------------------------\n");
+        }
+
+        return timeOutConfig;
+    }
+
+    // Unit testing
+    void setImplicitWait(Integer implicitWait) {
+        this.implicitWait = implicitWait;
+    }
 }
 


Test patch:
diff --git a/core/src/test/java/org/jsmart/zerocode/core/envprop/suite/ZeroCodeEnvPropertyReaderPackageTest.java b/core/src/test/java/org/jsmart/zerocode/core/envprop/suite/ZeroCodeEnvPropertyReaderPackageTest.java
index d5a7e68c..ee57d5d8 100644
--- a/core/src/test/java/org/jsmart/zerocode/core/envprop/suite/ZeroCodeEnvPropertyReaderPackageTest.java
+++ b/core/src/test/java/org/jsmart/zerocode/core/envprop/suite/ZeroCodeEnvPropertyReaderPackageTest.java
@@ -9,7 +9,7 @@ import org.junit.runner.RunWith;
 // If not found, then defaults to "app_config.properties"
 
 @EnvProperty("_${ENV_NAME}")
-@TargetEnv("env_config_properties/app_config.properties")
+@TargetEnv("env_config_test_files/app_config.properties")
 @TestPackageRoot("integration_test_files/env_prop")
 @RunWith(NewPortTestZeroCodeUnitRunner.class)
 //@RunWith(ZeroCodePackageRunner.class)
diff --git a/core/src/test/java/org/jsmart/zerocode/core/httpclient/ssl/SslTrustHttpClientTest.java b/core/src/test/java/org/jsmart/zerocode/core/httpclient/ssl/SslTrustHttpClientTest.java
index 72b2bc0d..c1347efc 100644
--- a/core/src/test/java/org/jsmart/zerocode/core/httpclient/ssl/SslTrustHttpClientTest.java
+++ b/core/src/test/java/org/jsmart/zerocode/core/httpclient/ssl/SslTrustHttpClientTest.java
@@ -1,8 +1,16 @@
 package org.jsmart.zerocode.core.httpclient.ssl;
 
+import com.github.tomakehurst.wiremock.WireMockServer;
+import java.net.SocketTimeoutException;
+import java.util.HashMap;
+import javax.ws.rs.core.Response;
+import org.apache.http.HttpResponse;
 import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpUriRequest;
 import org.apache.http.impl.client.CloseableHttpClient;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -10,9 +18,11 @@ import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 
-import javax.ws.rs.core.Response;
-import java.util.HashMap;
-
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -26,6 +36,38 @@ public class SslTrustHttpClientTest {
     @InjectMocks
     SslTrustHttpClient sslTrustHttpClient;
 
+    static String basePath;
+    static String fullPath;
+    static int port = 8383;
+
+    static WireMockServer mockServer = new WireMockServer(port);
+
+    public static final int SERVER_DELAY = 2000;
+    public static final int MAX_IMPLICIT_WAIT_HIGH = 3000;
+    public static final int MAX_IMPLICIT_WAIT_LOW = 1500;
+
+    @BeforeClass
+    public static void setUpWireMock() throws Exception {
+        basePath = "http://localhost:" + port;
+        String path = "/delay/ids/1";
+        fullPath = basePath + path;
+
+        mockServer.start();
+
+        mockServer.stubFor(
+                get(urlEqualTo(path))
+                        .willReturn(aResponse()
+                                .withStatus(200)
+                                .withFixedDelay(SERVER_DELAY)
+                        ));
+
+    }
+
+    @AfterClass
+    public static void tearDown() {
+        mockServer.shutdown();
+    }
+
     @Ignore("TODO-- unit test. Already Covered in the integration tests")
     @Test
     public void testNulPointerNotThrown_emptyBody() throws Exception {
@@ -44,5 +86,39 @@ public class SslTrustHttpClientTest {
         System.out.println("" + actualResponse);
     }
 
+    @Test
+    public void testImplicitDelay() throws Exception {
+        SslTrustHttpClient sslTrustHttpClient = new SslTrustHttpClient();
+        sslTrustHttpClient.setImplicitWait(MAX_IMPLICIT_WAIT_HIGH); //Ok - More than the implicit
+
+        CloseableHttpClient httpClient = sslTrustHttpClient.createHttpClient();
+
+        HttpGet request = new HttpGet(fullPath);
+        HttpResponse response = httpClient.execute(request);
+
+        assertThat(response.getStatusLine().getStatusCode(), is(200));
+    }
+
+    @Test(expected = SocketTimeoutException.class)
+    public void testImplicitDelay_timeout() throws Exception {
+        SslTrustHttpClient sslTrustHttpClient = new SslTrustHttpClient();
+        sslTrustHttpClient.setImplicitWait(MAX_IMPLICIT_WAIT_LOW); //Timeout - Less than the implicit
 
+        CloseableHttpClient httpClient = sslTrustHttpClient.createHttpClient();
+
+        HttpGet request = new HttpGet(fullPath);
+        HttpResponse response = httpClient.execute(request);
+    }
+
+    @Test
+    public void testImplicitDelay_noConfig() throws Exception {
+        SslTrustHttpClient sslTrustHttpClient = new SslTrustHttpClient();
+        //sslTrustHttpClient.setImplicitWait(none); //not configured
+
+        CloseableHttpClient httpClient = sslTrustHttpClient.createHttpClient();
+
+        HttpGet request = new HttpGet(fullPath);
+        HttpResponse response = httpClient.execute(request);
+        assertThat(response.getStatusLine().getStatusCode(), is(200));
+    }
 }
\ No newline at end of file
diff --git a/core/src/test/java/org/jsmart/zerocode/core/runner/retry/RetryWithStateTest.java b/core/src/test/java/org/jsmart/zerocode/core/runner/retry/RetryWithStateTest.java
index 98065228..f1716594 100644
--- a/core/src/test/java/org/jsmart/zerocode/core/runner/retry/RetryWithStateTest.java
+++ b/core/src/test/java/org/jsmart/zerocode/core/runner/retry/RetryWithStateTest.java
@@ -13,6 +13,8 @@ import org.junit.BeforeClass;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
 import static com.github.tomakehurst.wiremock.client.WireMock.get;
@@ -25,15 +27,17 @@ import static org.hamcrest.MatcherAssert.assertThat;
 @TargetEnv("dev_test.properties")
 @RunWith(ZeroCodeUnitRunner.class)
 public class RetryWithStateTest {
+    private static final Logger LOGGER = LoggerFactory.getLogger(RetryWithStateTest.class);
 
     static String basePath;
     static String fullPath;
-    static int port = 8383;
+    static int port = 8484;
 
-    static WireMockServer mockServer = new WireMockServer(port);
+    static WireMockServer mockServer;
 
     @BeforeClass
     public static void setUpWireMock() throws Exception {
+        mockServer = new WireMockServer(port);
         basePath = "http://localhost:" + port;
         String path = "/retry/ids/1";
         fullPath = basePath + path;
@@ -58,7 +62,10 @@ public class RetryWithStateTest {
 
     @AfterClass
     public static void tearDown() {
+        LOGGER.info("##Stopping the mock server and then shutting down");
+        mockServer.stop();
         mockServer.shutdown();
+        LOGGER.info("##Successfully stopped the mock server and then SHUTDOWN.");
     }
 
     @Test
diff --git a/core/src/test/resources/env_config_properties/app_config.properties b/core/src/test/resources/env_config_test_files/app_config.properties
similarity index 100%
rename from core/src/test/resources/env_config_properties/app_config.properties
rename to core/src/test/resources/env_config_test_files/app_config.properties
diff --git a/core/src/test/resources/env_config_properties/app_config_ci.properties b/core/src/test/resources/env_config_test_files/app_config_ci.properties
similarity index 100%
rename from core/src/test/resources/env_config_properties/app_config_ci.properties
rename to core/src/test/resources/env_config_test_files/app_config_ci.properties
diff --git a/core/src/test/resources/integration_test_files/retry_test_cases/04_REST_retry_with_state_test.json b/core/src/test/resources/integration_test_files/retry_test_cases/04_REST_retry_with_state_test.json
index 5908cfa8..a31eea53 100755
--- a/core/src/test/resources/integration_test_files/retry_test_cases/04_REST_retry_with_state_test.json
+++ b/core/src/test/resources/integration_test_files/retry_test_cases/04_REST_retry_with_state_test.json
@@ -7,7 +7,7 @@
                 "delay": 500
             },
             "name": "GetDatesRequest",
-            "url": "http://localhost:8383/retry/ids/1",
+            "url": "http://localhost:8484/retry/ids/1",
             "operation": "GET",
             "request": {
                 "headers": {
diff --git a/http-testing/src/main/java/org/jsmart/zerocode/zerocodejavaexec/wiremock/ZeroCodeWireMockRunner.java b/http-testing/src/main/java/org/jsmart/zerocode/zerocodejavaexec/wiremock/ZeroCodeWireMockRunner.java
new file mode 100644
index 00000000..ad1f179a
--- /dev/null
+++ b/http-testing/src/main/java/org/jsmart/zerocode/zerocodejavaexec/wiremock/ZeroCodeWireMockRunner.java
@@ -0,0 +1,42 @@
+package org.jsmart.zerocode.zerocodejavaexec.wiremock;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import org.jsmart.zerocode.core.runner.ZeroCodeUnitRunner;
+import org.junit.runners.model.InitializationError;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+
+public class ZeroCodeWireMockRunner extends ZeroCodeUnitRunner {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ZeroCodeWireMockRunner.class);
+    static String basePath;
+    static String fullPath;
+    static int port = 8383;
+
+    static WireMockServer mockServer = new WireMockServer(port);
+    public ZeroCodeWireMockRunner(Class<?> klass) throws InitializationError {
+        super(klass);
+        simulateServerDelay();
+    }
+
+
+    public static void simulateServerDelay() {
+        LOGGER.info("Setting up WireMock with server delay...");
+
+        basePath = "http://localhost:" + port;
+        String path = "/delay/ids/2";
+        fullPath = basePath + path;
+
+        mockServer.start();
+
+        mockServer.stubFor(
+                get(urlEqualTo(path))
+                        .willReturn(aResponse()
+                                .withStatus(200)
+                                .withFixedDelay(2000)
+                        ));
+    }
+}
diff --git a/http-testing/src/test/java/org/jsmart/zerocode/testhelp/tests/helloworldimplicitdelay/JustHelloImplicitDelayTimeOutTest.java b/http-testing/src/test/java/org/jsmart/zerocode/testhelp/tests/helloworldimplicitdelay/JustHelloImplicitDelayTimeOutTest.java
new file mode 100644
index 00000000..bd79b7d1
--- /dev/null
+++ b/http-testing/src/test/java/org/jsmart/zerocode/testhelp/tests/helloworldimplicitdelay/JustHelloImplicitDelayTimeOutTest.java
@@ -0,0 +1,24 @@
+package org.jsmart.zerocode.testhelp.tests.helloworldimplicitdelay;
+
+import org.jsmart.zerocode.core.domain.Scenario;
+import org.jsmart.zerocode.core.domain.TargetEnv;
+import org.jsmart.zerocode.zerocodejavaexec.wiremock.ZeroCodeWireMockRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@TargetEnv("localhost_app.properties")
+@RunWith(ZeroCodeWireMockRunner.class)
+public class JustHelloImplicitDelayTimeOutTest {
+
+    /**
+     * Server response delay = 2000 milli sec (2sec) - See the WireMock delay
+     * Max timeout = 1000 milli sec (1 sec) - See the localhost_app.properties
+     *  - The below test fails due to Max-timeout config
+     *  - You can tweak this to different value and make it/Pass/Fail
+     */
+    @Test
+    @Scenario("helloworld_implicit_delay/http_implicit_delay_max_timeout_scenario.json")
+    public void testImplicitDelay_max1Sec() {
+    }
+
+}
diff --git a/http-testing/src/test/resources/helloworld_implicit_delay/http_implicit_delay_max_timeout_scenario.json b/http-testing/src/test/resources/helloworld_implicit_delay/http_implicit_delay_max_timeout_scenario.json
new file mode 100644
index 00000000..247653e6
--- /dev/null
+++ b/http-testing/src/test/resources/helloworld_implicit_delay/http_implicit_delay_max_timeout_scenario.json
@@ -0,0 +1,16 @@
+{
+   "scenarioName": "Validate Http Implicit Delay or Max Timeout",
+   "steps": [
+      {
+         "name": "verify_mock",
+         "url": "/delay/ids/2",
+         "operation": "GET",
+         "request": {
+         },
+         "assertions": {
+            "status": 200
+         }
+      }
+
+   ]
+}
\ No newline at end of file
diff --git a/http-testing/src/test/resources/localhost_app.properties b/http-testing/src/test/resources/localhost_app.properties
new file mode 100644
index 00000000..18d13fa4
--- /dev/null
+++ b/http-testing/src/test/resources/localhost_app.properties
@@ -0,0 +1,8 @@
+web.application.endpoint.host=http://localhost
+web.application.endpoint.port=8383
+web.application.endpoint.context=
+
+# To make the test PASS, update this value to 3000(3 sec) or anything higher than 2000.
+# Where 2000 milli-sec(2sec) is the delay from the Web(WireMock) server in responding
+# to the http request.
+http.max.timeout.milliseconds=1000
\ No newline at end of file