org.ballerinalang.test.service.resiliency.HttpResiliencyTest.java Source code

Java tutorial

Introduction

Here is the source code for org.ballerinalang.test.service.resiliency.HttpResiliencyTest.java

Source

/*
 *  Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
package org.ballerinalang.test.service.resiliency;

import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import org.ballerinalang.test.BaseTest;
import org.ballerinalang.test.context.BServerInstance;
import org.ballerinalang.test.context.BallerinaTestException;
import org.ballerinalang.test.util.HttpClientRequest;
import org.ballerinalang.test.util.HttpResponse;
import org.ballerinalang.test.util.TestConstant;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
import static org.apache.http.HttpStatus.SC_OK;
import static org.apache.http.HttpStatus.SC_SERVICE_UNAVAILABLE;

/**
 * Test cases for the resiliency scenarios.
 */
public class HttpResiliencyTest extends BaseTest {

    protected static BServerInstance serverInstance;
    private static final String TYPICAL_SERVICE_PATH = "fo" + File.separator + "typical";
    private static final String SUCCESS_HELLO_MESSAGE = "Hello World!!!";
    private static final String INTERNAL_ERROR_MESSAGE = "Internal error occurred while processing the request.";
    private static final String UPSTREAM_UNAVAILABLE_MESSAGE = "Upstream service unavailable.";
    private static final String SERVICE_UNAVAILABLE_MESSAGE = "Service unavailable.";
    private static final String IDLE_TIMEOUT_MESSAGE = "Idle timeout triggered before initiating inbound response";
    private static final String REQUEST_PAYLOAD_STRING = "{\"Name\":\"Ballerina\"}";
    private static final String TYPICAL_CB_SERVICE_PATH = "cb" + File.separator + "typical";
    private static final String FORCE_OPEN_SERVICE_PATH = "cb" + File.separator + "forceopen";
    private static final String FORCE_CLOSE_SERVICE_PATH = "cb" + File.separator + "forceclose";
    private static final String GET_STATE_SERVICE_PATH = "cb" + File.separator + "getstate";
    private static final String REQUEST_VOLUME_SERVICE_PATH = "cb" + File.separator + "requestvolume";
    private static final String STATUS_CODE_SERVICE_PATH = "cb" + File.separator + "statuscode";
    private static final String TRIAL_FAILLURE_SERVICE_PATH = "cb" + File.separator + "trialrun";

    @BeforeTest(alwaysRun = true)
    public void start() throws BallerinaTestException {
        int[] requiredPorts = new int[] { 8080, 9300, 8081, 9301, 8082, 9302, 8083, 9303, 8084, 9304, 8085, 9305,
                8086, 9306, 8087, 9307, 8088, 9308, 8089, 9309, 8090, 9310, 8091, 9311, 8092, 9312 };
        String sourcePath = new File(
                "src" + File.separator + "test" + File.separator + "resources" + File.separator + "resiliency")
                        .getAbsolutePath();
        serverInstance = new BServerInstance(balServer);
        serverInstance.startServer(sourcePath, "resiliencyservices", requiredPorts);
    }

    @Test(description = "Test basic failover functionality")
    public void testSimpleFailover() throws IOException {
        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaderNames.CONTENT_TYPE.toString(), TestConstant.CONTENT_TYPE_JSON);
        HttpResponse response = HttpClientRequest.doPost(
                serverInstance.getServiceURLHttp(9300, TYPICAL_SERVICE_PATH), "{\"Name\":\"Ballerina\"}", headers);
        Assert.assertEquals(response.getResponseCode(), 200, "Response code mismatched");
        Assert.assertEquals(response.getHeaders().get(HttpHeaderNames.CONTENT_TYPE.toString()),
                TestConstant.CONTENT_TYPE_TEXT_PLAIN, "Content-Type mismatched");
        Assert.assertEquals(response.getData(), "Mock Resource is Invoked.", "Message content mismatched");
    }

    @Test(description = "Test failover functionality with multipart requests")
    public void testMultiPart() throws IOException {
        String multipartDataBoundary = Long.toHexString(PlatformDependent.threadLocalRandom().nextLong());
        String multipartBody = "--" + multipartDataBoundary + "\r\n"
                + "Content-Disposition: form-data; name=\"foo\"" + "\r\n"
                + "Content-Type: text/plain; charset=UTF-8" + "\r\n" + "\r\n" + "Part1" + "\r\n" + "--"
                + multipartDataBoundary + "\r\n"
                + "Content-Disposition: form-data; name=\"filepart\"; filename=\"file-01.txt\"" + "\r\n"
                + "Content-Type: text/plain" + "\r\n" + "Content-Transfer-Encoding: binary" + "\r\n" + "\r\n"
                + "Part2" + StringUtil.NEWLINE + "\r\n" + "--" + multipartDataBoundary + "--" + "\r\n";
        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaderNames.CONTENT_TYPE.toString(),
                "multipart/form-data; boundary=" + multipartDataBoundary);
        HttpResponse response = HttpClientRequest
                .doPost(serverInstance.getServiceURLHttp(9301, TYPICAL_SERVICE_PATH), multipartBody, headers);
        Assert.assertEquals(response.getResponseCode(), 200, "Response code mismatched");
        Assert.assertTrue(
                response.getHeaders().get(HttpHeaderNames.CONTENT_TYPE.toString())
                        .contains("multipart/form-data;boundary=" + multipartDataBoundary),
                "Response is not form of multipart");
        Assert.assertTrue(response.getData().contains("form-data;name=\"foo\"content-id: 0Part1"),
                "Message content mismatched");
        Assert.assertTrue(
                response.getData()
                        .contains("form-data;name=\"filepart\";filename=\"file-01.txt\"content-id: 1Part2"),
                "Message content mismatched");
    }

    @Test(description = "Test failover functionality when request has nested body parts")
    public void testNestedMultiPart() throws IOException {
        String multipartDataBoundary = Long.toHexString(PlatformDependent.threadLocalRandom().nextLong());
        String multipartMixedBoundary = Long.toHexString(PlatformDependent.threadLocalRandom().nextLong());
        String nestedMultipartBody = "--" + multipartDataBoundary + "\r\n"
                + "Content-Disposition: form-data; name=\"parent1\"" + "\r\n"
                + "Content-Type: text/plain; charset=UTF-8" + "\r\n" + "\r\n" + "Parent Part" + "\r\n" + "--"
                + multipartDataBoundary + "\r\n" + "Content-Disposition: form-data; name=\"parent2\"" + "\r\n"
                + "Content-Type: multipart/mixed; boundary=" + multipartMixedBoundary + "\r\n" + "\r\n" + "--"
                + multipartMixedBoundary + "\r\n" + "Content-Disposition: attachment; filename=\"file-02.txt\""
                + "\r\n" + "Content-Type: text/plain" + "\r\n" + "Content-Transfer-Encoding: binary" + "\r\n"
                + "\r\n" + "Child Part 1" + StringUtil.NEWLINE + "\r\n" + "--" + multipartMixedBoundary + "\r\n"
                + "Content-Disposition: attachment; filename=\"file-02.txt\"" + "\r\n" + "Content-Type: text/plain"
                + "\r\n" + "Content-Transfer-Encoding: binary" + "\r\n" + "\r\n" + "Child Part 2"
                + StringUtil.NEWLINE + "\r\n" + "--" + multipartMixedBoundary + "--" + "\r\n" + "--"
                + multipartDataBoundary + "--" + "\r\n";
        String expectedChildPart1 = "Content-Transfer-Encoding: binary" + "content-type: text/plain"
                + "content-disposition: attachment;filename=\"file-02.txt\"content-id: 0" + "Child Part 1";
        String expectedChildPart2 = "Content-Transfer-Encoding: binary" + "content-type: text/plain"
                + "content-disposition: attachment;filename=\"file-02.txt\"content-id: 1" + "Child Part 2";
        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaderNames.CONTENT_TYPE.toString(),
                "multipart/form-data; boundary=" + multipartDataBoundary);
        HttpResponse response = HttpClientRequest
                .doPost(serverInstance.getServiceURLHttp(9302, TYPICAL_SERVICE_PATH), nestedMultipartBody, headers);
        Assert.assertEquals(response.getResponseCode(), 200, "Response code mismatched");
        Assert.assertTrue(
                response.getHeaders().get(HttpHeaderNames.CONTENT_TYPE.toString())
                        .contains("multipart/form-data;boundary=" + multipartDataBoundary),
                "Response is not form of multipart");
        Assert.assertTrue(response.getData().contains(expectedChildPart1), "Message content mismatched");
        Assert.assertTrue(response.getData().contains(expectedChildPart2), "Message content mismatched");
    }

    @Test(description = "Test the functionality for all endpoints failure scenario")
    public void testAllEndpointFailure() throws IOException {
        String expectedMessage = "All the failover endpoints failed. Last error was Idle timeout"
                + " triggered before initiating inbound response";
        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaderNames.CONTENT_TYPE.toString(), TestConstant.CONTENT_TYPE_JSON);
        HttpResponse response = HttpClientRequest.doPost(serverInstance.getServiceURLHttp(9303, "fo/failures"),
                "{\"Name\":\"Ballerina\"}", headers);
        Assert.assertEquals(response.getResponseCode(), 500, "Response code mismatched");
        Assert.assertEquals(response.getHeaders().get(HttpHeaderNames.CONTENT_TYPE.toString()),
                TestConstant.CONTENT_TYPE_TEXT_PLAIN, "Content-Type mismatched");
        Assert.assertEquals(response.getData(), expectedMessage, "Message content mismatched");
    }

    @Test(description = "Test the functionality for all endpoints failure scenario")
    public void testResponseWithErrorStatusCodes() throws IOException {
        String expectedMessage = "All the failover endpoints failed. "
                + "Last endpoint returned response is: 503 Service Unavailable";
        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaderNames.CONTENT_TYPE.toString(), TestConstant.CONTENT_TYPE_JSON);
        HttpResponse response = HttpClientRequest.doPost(serverInstance.getServiceURLHttp(9304, "fo/failurecodes"),
                "{\"Name\":\"Ballerina\"}", headers);
        Assert.assertEquals(response.getResponseCode(), 500, "Response code mismatched");
        Assert.assertEquals(response.getHeaders().get(HttpHeaderNames.CONTENT_TYPE.toString()),
                TestConstant.CONTENT_TYPE_TEXT_PLAIN, "Content-Type mismatched");
        Assert.assertEquals(response.getData(), expectedMessage, "Message content mismatched");
    }

    @Test(description = "Test to verify whether failover will test from last successful endpoint")
    public void testFailoverStartingPosition() throws IOException {
        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaderNames.CONTENT_TYPE.toString(), TestConstant.CONTENT_TYPE_JSON);
        HttpResponse response = HttpClientRequest.doPost(serverInstance.getServiceURLHttp(9305, "fo/index"),
                "{\"Name\":\"Ballerina\"}", headers);
        Assert.assertEquals(response.getResponseCode(), 200, "Response code mismatched");
        Assert.assertEquals(response.getHeaders().get(HttpHeaderNames.CONTENT_TYPE.toString()),
                TestConstant.CONTENT_TYPE_TEXT_PLAIN, "Content-Type mismatched");
        Assert.assertEquals(response.getData(), "Failover start index is : 0", "Message content mismatched");
        HttpResponse secondResponse = HttpClientRequest.doPost(serverInstance.getServiceURLHttp(9305, "fo/index"),
                "{\"Name\":\"Ballerina\"}", headers);
        Assert.assertEquals(secondResponse.getResponseCode(), 200, "Response code mismatched");
        Assert.assertEquals(secondResponse.getHeaders().get(HttpHeaderNames.CONTENT_TYPE.toString()),
                TestConstant.CONTENT_TYPE_TEXT_PLAIN, "Content-Type mismatched");
        Assert.assertEquals(secondResponse.getData(), "Failover start index is : 2", "Message content mismatched");
    }

    @Test(description = "Test basic circuit breaker functionality", dataProvider = "responseDataProvider")
    public void testTypicalBackendTimeout(int responseCode, String messasge) throws Exception {
        verifyResponses(9306, TYPICAL_CB_SERVICE_PATH, responseCode, messasge);
    }

    @Test(description = "Test for circuit breaker forceOpen functionality", dataProvider = "forceOpenResponseDataProvider")
    public void testForceOPen(int responseCode, String messasge) throws Exception {
        verifyResponses(9307, FORCE_OPEN_SERVICE_PATH, responseCode, messasge);
    }

    @Test(description = "Test for circuit breaker forceClese functionality", dataProvider = "forceCloseResponseDataProvider")
    public void testForceClose(int responseCode, String messasge) throws Exception {
        verifyResponses(9308, FORCE_CLOSE_SERVICE_PATH, responseCode, messasge);
    }

    @Test(description = "Test for circuit breaker getState functionality", dataProvider = "getStateResponseDataProvider")
    public void testgetState(int responseCode, String messasge) throws Exception {
        verifyResponses(9309, GET_STATE_SERVICE_PATH, responseCode, messasge);
    }

    @Test(description = "Test for circuit breaker requestVolumeThreshold functionality", dataProvider = "requestVolumeResponseDataProvider")
    public void requestVolumeTest(int responseCode, String messasge) throws Exception {
        verifyResponses(9310, REQUEST_VOLUME_SERVICE_PATH, responseCode, messasge);

    }

    @Test(description = "Test for circuit breaker failure status codes functionality", dataProvider = "statusCodeResponseDataProvider")
    public void httpStatusCodesTest(int responseCode, String messasge) throws Exception {
        verifyResponses(9311, STATUS_CODE_SERVICE_PATH, responseCode, messasge);
    }

    @Test(description = "Test for circuit breaker trail failure functionality", dataProvider = "trialRunFailureResponseDataProvider")
    public void trialRunFailureTest(int responseCode, String messasge) throws Exception {
        verifyResponses(9312, TRIAL_FAILLURE_SERVICE_PATH, responseCode, messasge);
    }

    @DataProvider(name = "responseDataProvider")
    public Object[][] responseDataProvider() {
        return new Object[][] { new Object[] { SC_OK, SUCCESS_HELLO_MESSAGE },
                new Object[] { SC_OK, SUCCESS_HELLO_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, IDLE_TIMEOUT_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, UPSTREAM_UNAVAILABLE_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, UPSTREAM_UNAVAILABLE_MESSAGE },
                new Object[] { SC_OK, SUCCESS_HELLO_MESSAGE }, new Object[] { SC_OK, SUCCESS_HELLO_MESSAGE }, };
    }

    @DataProvider(name = "forceOpenResponseDataProvider")
    public Object[][] forceOpenResponseDataProvider() {
        return new Object[][] { new Object[] { SC_OK, SUCCESS_HELLO_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, UPSTREAM_UNAVAILABLE_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, UPSTREAM_UNAVAILABLE_MESSAGE }, };
    }

    @DataProvider(name = "forceCloseResponseDataProvider")
    public Object[][] forceCloseResponseDataProvider() {
        return new Object[][] { new Object[] { SC_INTERNAL_SERVER_ERROR, IDLE_TIMEOUT_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, IDLE_TIMEOUT_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, UPSTREAM_UNAVAILABLE_MESSAGE },
                new Object[] { SC_OK, SUCCESS_HELLO_MESSAGE }, new Object[] { SC_OK, SUCCESS_HELLO_MESSAGE }, };
    }

    @DataProvider(name = "getStateResponseDataProvider")
    public Object[][] getStateResponseDataProvider() {
        return new Object[][] { new Object[] { SC_OK, SUCCESS_HELLO_MESSAGE },
                new Object[] { SC_OK, SUCCESS_HELLO_MESSAGE }, new Object[] { SC_OK, SUCCESS_HELLO_MESSAGE }, };
    }

    @DataProvider(name = "requestVolumeResponseDataProvider")
    public Object[][] requestVolumeResponseDataProvider() {
        return new Object[][] { new Object[] { SC_INTERNAL_SERVER_ERROR, INTERNAL_ERROR_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, INTERNAL_ERROR_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, INTERNAL_ERROR_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, INTERNAL_ERROR_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, INTERNAL_ERROR_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, INTERNAL_ERROR_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, UPSTREAM_UNAVAILABLE_MESSAGE }, };
    }

    @DataProvider(name = "statusCodeResponseDataProvider")
    public Object[][] statusCodeResponseDataProvider() {
        return new Object[][] { new Object[] { SC_SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE_MESSAGE },
                new Object[] { SC_SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE_MESSAGE },
                new Object[] { SC_SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, UPSTREAM_UNAVAILABLE_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, UPSTREAM_UNAVAILABLE_MESSAGE }, };
    }

    @DataProvider(name = "trialRunFailureResponseDataProvider")
    public Object[][] trialRunFailureResponseDataProvider() {
        return new Object[][] { new Object[] { SC_SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, UPSTREAM_UNAVAILABLE_MESSAGE },
                new Object[] { SC_SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE_MESSAGE },
                new Object[] { SC_INTERNAL_SERVER_ERROR, UPSTREAM_UNAVAILABLE_MESSAGE }, };
    }

    private void verifyResponses(int port, String path, int responseCode, String expectedMessage) throws Exception {
        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaderNames.CONTENT_TYPE.toString(), TestConstant.CONTENT_TYPE_JSON);
        HttpResponse response = HttpClientRequest.doPost(serverInstance.getServiceURLHttp(port, path),
                REQUEST_PAYLOAD_STRING, headers);
        Assert.assertEquals(response.getResponseCode(), responseCode, "Response code mismatched");
        Assert.assertTrue(response.getData().contains(expectedMessage), "Message content mismatched");
    }

    @AfterTest(alwaysRun = true)
    public void cleanup() throws Exception {
        serverInstance.removeAllLeechers();
        serverInstance.shutdownServer();
    }
}