org.sonar.server.ws.WebServiceEngineTest.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.server.ws.WebServiceEngineTest.java

Source

/*
 * SonarQube
 * Copyright (C) 2009-2017 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.server.ws;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.internal.ValidatingRequest;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.server.exceptions.BadRequestException;
import org.sonarqube.ws.MediaTypes;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class WebServiceEngineTest {

    @Rule
    public LogTester logTester = new LogTester();

    @Rule
    public ExpectedException expectedException = ExpectedException.none();

    private WebServiceEngine underTest = new WebServiceEngine(new WebService[] { new SystemWs() });

    @Before
    public void start() {
        underTest.start();
    }

    @After
    public void stop() {
        underTest.stop();
    }

    @Test
    public void load_ws_definitions_at_startup() {
        assertThat(underTest.controllers()).hasSize(1);
        assertThat(underTest.controllers().get(0).path()).isEqualTo("api/system");
    }

    @Test
    public void execute_request() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/health");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString()).isEqualTo("good");
    }

    @Test
    public void execute_request_when_path_does_not_begin_with_slash() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/health");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString()).isEqualTo("good");
    }

    @Test
    public void execute_request_with_action_suffix() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/health");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString()).isEqualTo("good");
    }

    @Test
    public void bad_request_if_action_suffix_is_not_supported() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/health.bat");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().status()).isEqualTo(400);
        assertThat(response.stream().mediaType()).isEqualTo(MediaTypes.JSON);
        assertThat(response.stream().outputAsString())
                .isEqualTo("{\"errors\":[{\"msg\":\"Unknown action extension: bat\"}]}");
    }

    @Test
    public void no_content() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/alive");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString()).isEmpty();
    }

    @Test
    public void bad_controller() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/xxx/health");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString())
                .isEqualTo("{\"errors\":[{\"msg\":\"Unknown url : /api/xxx/health\"}]}");
        assertThat(response.stream().status()).isEqualTo(404);
    }

    @Test
    public void bad_controller_with_no_action() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/bad");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString())
                .isEqualTo("{\"errors\":[{\"msg\":\"Unknown url : /api/bad\"}]}");
        assertThat(response.stream().status()).isEqualTo(404);
    }

    @Test
    public void bad_action() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/xxx");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString())
                .isEqualTo("{\"errors\":[{\"msg\":\"Unknown url : /api/system/xxx\"}]}");
        assertThat(response.stream().status()).isEqualTo(404);
    }

    @Test
    public void method_get_not_allowed() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/ping");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString())
                .isEqualTo("{\"errors\":[{\"msg\":\"HTTP method POST is required\"}]}");
    }

    @Test
    public void method_put_not_allowed() {
        ValidatingRequest request = new TestRequest().setMethod("PUT").setPath("/api/system/ping");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString())
                .isEqualTo("{\"errors\":[{\"msg\":\"HTTP method PUT is not allowed\"}]}");
    }

    @Test
    public void method_delete_not_allowed() {
        ValidatingRequest request = new TestRequest().setMethod("DELETE").setPath("/api/system/ping");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString())
                .isEqualTo("{\"errors\":[{\"msg\":\"HTTP method DELETE is not allowed\"}]}");
    }

    @Test
    public void method_post_required() {
        ValidatingRequest request = new TestRequest().setMethod("POST").setPath("/api/system/ping");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString()).isEqualTo("pong");
    }

    @Test
    public void unknown_parameter_is_set() {
        ValidatingRequest request = new TestRequest().setMethod("GET")
                .setPath("/api/system/fail_with_undeclared_parameter").setParam("unknown", "Unknown");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString()).isEqualTo(
                "{\"errors\":[{\"msg\":\"BUG - parameter 'unknown' is undefined for action 'fail_with_undeclared_parameter'\"}]}");
    }

    @Test
    public void required_parameter_is_not_set() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString())
                .isEqualTo("{\"errors\":[{\"msg\":\"The 'message' parameter is missing\"}]}");
    }

    @Test
    public void optional_parameter_is_not_set() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print")
                .setParam("message", "Hello World");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString()).isEqualTo("Hello World by -");
    }

    @Test
    public void optional_parameter_is_set() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print")
                .setParam("message", "Hello World").setParam("author", "Marcel");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString()).isEqualTo("Hello World by Marcel");
    }

    @Test
    public void param_value_is_in_possible_values() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print")
                .setParam("message", "Hello World").setParam("format", "json");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString()).isEqualTo("Hello World by -");
    }

    @Test
    public void param_value_is_not_in_possible_values() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print")
                .setParam("message", "Hello World").setParam("format", "html");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString()).isEqualTo(
                "{\"errors\":[{\"msg\":\"Value of parameter 'format' (html) must be one of: [json, xml]\"}]}");
    }

    @Test
    public void internal_error() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/fail");
        DumbResponse response = new DumbResponse();
        underTest.execute(request, response);

        assertThat(response.stream().outputAsString())
                .isEqualTo("{\"errors\":[{\"msg\":\"An error has occurred. Please contact your administrator\"}]}");
        assertThat(response.stream().status()).isEqualTo(500);
        assertThat(response.stream().mediaType()).isEqualTo(MediaTypes.JSON);
        assertThat(logTester.logs(LoggerLevel.ERROR)).filteredOn(l -> l.contains("Fail to process request"))
                .isNotEmpty();
    }

    @Test
    public void bad_request() {
        ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/fail_bad_request")
                .setParam("count", "3");
        DumbResponse response = new DumbResponse();

        underTest.execute(request, response);

        assertThat(response.stream().outputAsString()).isEqualTo("{\"errors\":[{\"msg\":\"Bad request !\"}]}");
        assertThat(response.stream().status()).isEqualTo(400);
        assertThat(response.stream().mediaType()).isEqualTo(MediaTypes.JSON);
    }

    @Test
    public void bad_request_with_multiple_messages() {
        ValidatingRequest request = new TestRequest().setMethod("GET")
                .setPath("/api/system/fail_with_multiple_messages").setParam("count", "3");
        DumbResponse response = new DumbResponse();

        underTest.execute(request, response);

        assertThat(response.stream().outputAsString())
                .isEqualTo("{\"errors\":[" + "{\"msg\":\"Bad request reason #0\"},"
                        + "{\"msg\":\"Bad request reason #1\"}," + "{\"msg\":\"Bad request reason #2\"}" + "]}");
        assertThat(response.stream().status()).isEqualTo(400);
        assertThat(response.stream().mediaType()).isEqualTo(MediaTypes.JSON);
    }

    @Test
    public void does_not_fail_to_render_error_message_having_percent() {
        ValidatingRequest request = new TestRequest().setMethod("GET")
                .setPath("/api/system/error_message_having_percent");
        DumbResponse response = new DumbResponse();

        underTest.execute(request, response);

        assertThat(response.stream().outputAsString())
                .isEqualTo("{\"errors\":[{\"msg\":\"this should not fail %s\"}]}");
        assertThat(response.stream().status()).isEqualTo(400);
        assertThat(response.stream().mediaType()).isEqualTo(MediaTypes.JSON);
    }

    @Test
    public void should_handle_headers() {
        DumbResponse response = new DumbResponse();
        String name = "Content-Disposition";
        String value = "attachment; filename=sonarqube.zip";
        response.setHeader(name, value);
        assertThat(response.getHeaderNames()).containsExactly(name);
        assertThat(response.getHeader(name)).isEqualTo(value);
    }

    @Test
    public void does_not_fail_when_request_is_aborted_and_response_is_committed() throws Exception {
        ValidatingRequest request = new TestRequest().setMethod("GET")
                .setPath("/api/system/fail_with_client_abort_exception");
        Response response = mock(Response.class);
        ServletResponse.ServletStream servletStream = mock(ServletResponse.ServletStream.class);
        when(response.stream()).thenReturn(servletStream);
        HttpServletResponse httpServletResponse = mock(HttpServletResponse.class);
        when(httpServletResponse.isCommitted()).thenReturn(true);
        when(servletStream.response()).thenReturn(httpServletResponse);
        underTest.execute(request, response);

        assertThat(logTester.logs(LoggerLevel.DEBUG)).isNotEmpty();
    }

    static class SystemWs implements WebService {
        @Override
        public void define(Context context) {
            NewController newController = context.createController("api/system");
            createNewDefaultAction(newController, "health").setHandler((request, response) -> {
                try {
                    response.stream().output().write("good".getBytes());
                } catch (IOException e) {
                    throw new IllegalStateException(e);
                }
            });
            createNewDefaultAction(newController, "ping").setPost(true).setHandler((request, response) -> {
                try {
                    response.stream().output().write("pong".getBytes());
                } catch (IOException e) {
                    throw new IllegalStateException(e);
                }
            });
            createNewDefaultAction(newController, "fail").setHandler((request, response) -> {
                throw new IllegalStateException("Unexpected");
            });
            createNewDefaultAction(newController, "fail_bad_request").setHandler((request, response) -> {
                throw BadRequestException.create("Bad request !");
            });
            createNewDefaultAction(newController, "fail_with_multiple_messages")
                    .createParam("count", "Number of error messages to generate")
                    .setHandler((request, response) -> {
                        List<String> errors = new ArrayList<>();
                        for (int count = 0; count < Integer.valueOf(request.param("count")); count++) {
                            errors.add("Bad request reason #" + count);
                        }
                        throw BadRequestException.create(errors);
                    });
            createNewDefaultAction(newController, "error_message_having_percent")
                    .setHandler((request, response) -> {
                        throw new IllegalArgumentException("this should not fail %s");
                    });
            createNewDefaultAction(newController, "alive").setHandler((request, response) -> response.noContent());

            createNewDefaultAction(newController, "fail_with_undeclared_parameter").setHandler(
                    (request, response) -> response.newJsonWriter().prop("unknown", request.param("unknown")));

            // parameter "message" is required but not "author"
            NewAction print = createNewDefaultAction(newController, "print");
            print.createParam("message").setDescription("required message").setRequired(true);
            print.createParam("author").setDescription("optional author").setDefaultValue("-");
            print.createParam("format").setDescription("optional format").setPossibleValues("json", "xml");
            print.setHandler((request, response) -> {
                try {
                    request.param("format");
                    IOUtils.write(request.mandatoryParam("message") + " by " + request.param("author", "nobody"),
                            response.stream().output());
                } catch (IOException e) {
                    throw new IllegalStateException(e);
                }
            });

            createNewDefaultAction(newController, "fail_with_client_abort_exception")
                    .setHandler((request, response) -> {
                        throw new IllegalStateException("fail!", new ClientAbortException());
                    });

            newController.done();
        }

        private NewAction createNewDefaultAction(NewController controller, String key) {
            return controller.createAction(key).setDescription("Dummy Description").setSince("5.3")
                    .setResponseExample(getClass().getResource("web-service-engine-test.txt"));
        }
    }
}