its.ConnectedModeTest.java Source code

Java tutorial

Introduction

Here is the source code for its.ConnectedModeTest.java

Source

/*
 * SonarLint Core - ITs - Tests
 * 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 its;

import com.sonar.orchestrator.Orchestrator;
import com.sonar.orchestrator.build.MavenBuild;
import com.sonar.orchestrator.container.Server;
import com.sonar.orchestrator.locator.FileLocation;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonar.wsclient.permissions.PermissionParameters;
import org.sonar.wsclient.services.PropertyCreateQuery;
import org.sonar.wsclient.services.PropertyDeleteQuery;
import org.sonar.wsclient.user.UserParameters;
import org.sonarqube.ws.QualityProfiles.SearchWsResponse;
import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile;
import org.sonarqube.ws.client.HttpConnector;
import org.sonarqube.ws.client.PostRequest;
import org.sonarqube.ws.client.WsClient;
import org.sonarqube.ws.client.WsClientFactories;
import org.sonarqube.ws.client.WsRequest;
import org.sonarqube.ws.client.WsResponse;
import org.sonarqube.ws.client.permission.RemoveGroupWsRequest;
import org.sonarqube.ws.client.qualityprofile.SearchWsRequest;
import org.sonarqube.ws.client.setting.SetRequest;
import org.sonarsource.sonarlint.core.ConnectedSonarLintEngineImpl;
import org.sonarsource.sonarlint.core.WsHelperImpl;
import org.sonarsource.sonarlint.core.client.api.common.analysis.AnalysisResults;
import org.sonarsource.sonarlint.core.client.api.connected.ConnectedGlobalConfiguration;
import org.sonarsource.sonarlint.core.client.api.connected.ConnectedSonarLintEngine;
import org.sonarsource.sonarlint.core.client.api.connected.ConnectedSonarLintEngine.State;
import org.sonarsource.sonarlint.core.client.api.connected.ServerConfiguration;
import org.sonarsource.sonarlint.core.client.api.connected.StorageUpdateCheckResult;
import org.sonarsource.sonarlint.core.client.api.connected.WsHelper;
import org.sonarsource.sonarlint.core.client.api.exceptions.UnsupportedServerException;

import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.Assertions.tuple;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;

public class ConnectedModeTest extends AbstractConnectedTest {

    private static final String PROJECT_KEY_JAVA = "sample-java";
    private static final String PROJECT_KEY_JAVA_CUSTOM_SENSOR = "sample-java-custom-sensor";
    private static final String PROJECT_KEY_JAVA_PACKAGE = "sample-java-package";
    private static final String PROJECT_KEY_JAVA_EMPTY = "sample-java-empty";
    private static final String PROJECT_KEY_PHP = "sample-php";
    private static final String PROJECT_KEY_JAVASCRIPT = "sample-javascript";
    private static final String PROJECT_KEY_JAVASCRIPT_CUSTOM = "sample-javascript-custom";
    private static final String PROJECT_KEY_PYTHON = "sample-python";

    @ClassRule
    public static Orchestrator ORCHESTRATOR = Orchestrator.builderEnv().addPlugin("java").addPlugin("javascript")
            .addPlugin("php").addPlugin("python")
            .addPlugin(
                    FileLocation.of("../plugins/javascript-custom-rules/target/javascript-custom-rules-plugin.jar"))
            .addPlugin(FileLocation.of("../plugins/custom-sensor-plugin/target/custom-sensor-plugin.jar"))
            .restoreProfileAtStartup(FileLocation.ofClasspath("/java-sonarlint.xml"))
            .restoreProfileAtStartup(FileLocation.ofClasspath("/java-sonarlint-package.xml"))
            .restoreProfileAtStartup(FileLocation.ofClasspath("/java-empty-sonarlint.xml"))
            .restoreProfileAtStartup(FileLocation.ofClasspath("/javascript-sonarlint.xml"))
            .restoreProfileAtStartup(FileLocation.ofClasspath("/javascript-custom.xml"))
            .restoreProfileAtStartup(FileLocation.ofClasspath("/php-sonarlint.xml"))
            .restoreProfileAtStartup(FileLocation.ofClasspath("/python-sonarlint.xml"))
            .restoreProfileAtStartup(FileLocation.ofClasspath("/custom-sensor.xml")).build();

    @ClassRule
    public static TemporaryFolder temp = new TemporaryFolder();

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

    private static WsClient adminWsClient;
    private static Path sonarUserHome;

    private ConnectedSonarLintEngine engine;

    @BeforeClass
    public static void prepare() throws Exception {
        adminWsClient = newAdminWsClient(ORCHESTRATOR);
        if (ORCHESTRATOR.getServer().version().isGreaterThanOrEquals("6.3")) {
            adminWsClient.settingsService()
                    .set(SetRequest.builder().setKey("sonar.forceAuthentication").setValue("true").build());
        } else {
            ORCHESTRATOR.getServer().getAdminWsClient()
                    .create(new PropertyCreateQuery("sonar.forceAuthentication", "true"));
        }
        sonarUserHome = temp.newFolder().toPath();

        removeGroupPermission("anyone", "scan");

        ORCHESTRATOR.getServer().adminWsClient().userClient().create(UserParameters.create().login(SONARLINT_USER)
                .password(SONARLINT_PWD).passwordConfirmation(SONARLINT_PWD).name("SonarLint"));

        // addUserPermission("sonarlint", "dryRunScan");

        ORCHESTRATOR.getServer().provisionProject(PROJECT_KEY_JAVA, "Sample Java");
        ORCHESTRATOR.getServer().provisionProject(PROJECT_KEY_JAVA_PACKAGE, "Sample Java Package");
        ORCHESTRATOR.getServer().provisionProject(PROJECT_KEY_JAVA_EMPTY, "Sample Java Empty");
        ORCHESTRATOR.getServer().provisionProject(PROJECT_KEY_PHP, "Sample PHP");
        ORCHESTRATOR.getServer().provisionProject(PROJECT_KEY_JAVASCRIPT, "Sample Javascript");
        ORCHESTRATOR.getServer().provisionProject(PROJECT_KEY_JAVASCRIPT_CUSTOM, "Sample Javascript Custom");
        ORCHESTRATOR.getServer().provisionProject(PROJECT_KEY_PYTHON, "Sample Python");
        ORCHESTRATOR.getServer().provisionProject(PROJECT_KEY_JAVA_CUSTOM_SENSOR, "Sample Java Custom");

        ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT_KEY_JAVA, "java", "SonarLint IT Java");
        ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT_KEY_JAVA_PACKAGE, "java",
                "SonarLint IT Java Package");
        ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT_KEY_JAVA_EMPTY, "java",
                "SonarLint IT Java Empty");
        ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT_KEY_PHP, "php", "SonarLint IT PHP");
        ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT_KEY_JAVASCRIPT, "js",
                "SonarLint IT Javascript");
        ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT_KEY_JAVASCRIPT_CUSTOM, "js",
                "SonarLint IT Javascript Custom");
        ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT_KEY_PYTHON, "py", "SonarLint IT Python");
        ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT_KEY_JAVA_CUSTOM_SENSOR, "java",
                "SonarLint IT Custom Sensor");

        // Build project to have bytecode
        ORCHESTRATOR.executeBuild(
                MavenBuild.create(new File("projects/sample-java/pom.xml")).setGoals("clean package"));
    }

    @Before
    public void start() {
        FileUtils.deleteQuietly(sonarUserHome.toFile());
        engine = new ConnectedSonarLintEngineImpl(ConnectedGlobalConfiguration.builder().setServerId("orchestrator")
                .setSonarLintUserHome(sonarUserHome).setLogOutput((msg, level) -> System.out.println(msg)).build());
        assertThat(engine.getGlobalStorageStatus()).isNull();
        assertThat(engine.getState()).isEqualTo(State.NEVER_UPDATED);

        // This profile is altered in a test
        ORCHESTRATOR.getServer().restoreProfile(FileLocation.ofClasspath("/java-sonarlint.xml"));
    }

    @After
    public void stop() {
        ORCHESTRATOR.getServer().getAdminWsClient().delete(new PropertyDeleteQuery("sonar.java.file.suffixes"));
        ORCHESTRATOR.getServer().getAdminWsClient()
                .delete(new PropertyDeleteQuery("sonar.java.file.suffixes", PROJECT_KEY_JAVA));
        try {
            engine.stop(true);
        } catch (Exception e) {
            // Ignore
        }
    }

    @Test
    public void downloadModules() throws Exception {
        updateGlobal();
        assertThat(engine.allModulesByKey()).hasSize(8);
        ORCHESTRATOR.getServer().provisionProject("foo-bar", "Foo");
        assertThat(engine.downloadAllModules(getServerConfig(), null)).hasSize(9).containsKeys("foo-bar",
                PROJECT_KEY_JAVA, PROJECT_KEY_PHP);
        assertThat(engine.allModulesByKey()).hasSize(9).containsKeys("foo-bar", PROJECT_KEY_JAVA, PROJECT_KEY_PHP);
    }

    @Test
    public void updateNoAuth() throws Exception {
        try {
            engine.update(ServerConfiguration.builder().url(ORCHESTRATOR.getServer().getUrl())
                    .userAgent("SonarLint ITs").build(), null);
            fail("Exception expected");
        } catch (Exception e) {
            assertThat(e).hasMessage("Not authorized. Please check server credentials.");
        }
    }

    @Test
    public void parsingErrorJava() throws IOException {
        String fileContent = "pac kage its; public class MyTest { }";
        Path testFile = temp.newFile("MyTestParseError.java").toPath();
        Files.write(testFile, fileContent.getBytes(StandardCharsets.UTF_8));

        updateGlobal();
        updateModule(PROJECT_KEY_JAVA);

        SaveIssueListener issueListener = new SaveIssueListener();
        AnalysisResults results = engine.analyze(createAnalysisConfiguration(PROJECT_KEY_JAVA, testFile.toString()),
                issueListener);

        assertThat(results.failedAnalysisFiles()).hasSize(1);
    }

    @Test
    public void parsingErrorJavascript() throws IOException {
        String fileContent = "asd asd";
        Path testFile = temp.newFile("MyTest.js").toPath();
        Files.write(testFile, fileContent.getBytes(StandardCharsets.UTF_8));

        updateGlobal();
        updateModule(PROJECT_KEY_JAVASCRIPT);

        SaveIssueListener issueListener = new SaveIssueListener();
        AnalysisResults results = engine
                .analyze(createAnalysisConfiguration(PROJECT_KEY_JAVASCRIPT, testFile.toString()), issueListener);

        assertThat(results.failedAnalysisFiles()).hasSize(1);
    }

    @Test
    public void semanticErrorJava() throws IOException {
        String fileContent = "package its;public class MyTest {int a;int a;}";
        Path testFile = temp.newFile("MyTestSemanticError.java").toPath();
        Files.write(testFile, fileContent.getBytes(StandardCharsets.UTF_8));

        updateGlobal();
        updateModule(PROJECT_KEY_JAVA);

        SaveIssueListener issueListener = new SaveIssueListener();
        AnalysisResults results = engine.analyze(createAnalysisConfiguration(PROJECT_KEY_JAVA, testFile.toString()),
                issueListener);

        assertThat(results.failedAnalysisFiles()).hasSize(1);
    }

    @Test
    public void globalUpdate() throws Exception {
        updateGlobal();

        assertThat(engine.getState()).isEqualTo(State.UPDATED);
        assertThat(engine.getGlobalStorageStatus()).isNotNull();
        assertThat(engine.getGlobalStorageStatus().isStale()).isFalse();
        assertThat(engine.getGlobalStorageStatus().getServerVersion())
                .startsWith(StringUtils.substringBefore(ORCHESTRATOR.getServer().version().toString(), "-"));

        if (supportHtmlDesc()) {
            assertThat(engine.getRuleDetails("squid:S106").getHtmlDescription())
                    .contains("When logging a message there are");
        } else {
            assertThat(engine.getRuleDetails("squid:S106").getHtmlDescription())
                    .contains("Rule descriptions are only available in SonarLint with SonarQube 5.1+");
        }

        assertThat(engine.getModuleStorageStatus(PROJECT_KEY_JAVA)).isNull();
    }

    @Test
    public void updateProject() throws Exception {
        updateGlobal();

        updateModule(PROJECT_KEY_JAVA);

        assertThat(engine.getModuleStorageStatus(PROJECT_KEY_JAVA)).isNotNull();
    }

    @Test
    public void verifyExtendedDescription() throws Exception {
        assumeTrue(supportHtmlDesc());
        updateGlobal();

        String ruleKey = "squid:S106";

        assertThat(engine.getRuleDetails(ruleKey).getExtendedDescription()).isEmpty();

        String extendedDescription = "my dummy extended description";

        WsRequest request = new PostRequest("/api/rules/update").setParam("key", ruleKey).setParam("markdown_note",
                extendedDescription);
        WsResponse response = adminWsClient.wsConnector().call(request);
        assertThat(response.code()).isEqualTo(200);

        updateGlobal();

        assertThat(engine.getRuleDetails(ruleKey).getExtendedDescription()).isEqualTo(extendedDescription);
    }

    @Test
    public void analysisJavascript() throws Exception {
        updateGlobal();
        updateModule(PROJECT_KEY_JAVASCRIPT);

        SaveIssueListener issueListener = new SaveIssueListener();
        engine.analyze(createAnalysisConfiguration(PROJECT_KEY_JAVASCRIPT, PROJECT_KEY_JAVASCRIPT, "src/Person.js"),
                issueListener);
        assertThat(issueListener.getIssues()).hasSize(1);
    }

    @Test
    public void analysisJavascriptWithCustomRules() throws Exception {

        updateGlobal();
        updateModule(PROJECT_KEY_JAVASCRIPT_CUSTOM);

        SaveIssueListener issueListener = new SaveIssueListener();
        engine.analyze(createAnalysisConfiguration(PROJECT_KEY_JAVASCRIPT_CUSTOM, PROJECT_KEY_JAVASCRIPT_CUSTOM,
                "src/Person.js"), issueListener);
        assertThat(issueListener.getIssues()).extracting("ruleKey", "startLine").containsOnly(tuple("custom:S1", 3),
                tuple("custom:S1", 7));
    }

    @Test
    public void analysisPHP() throws Exception {
        updateGlobal();
        updateModule(PROJECT_KEY_PHP);

        SaveIssueListener issueListener = new SaveIssueListener();
        engine.analyze(createAnalysisConfiguration(PROJECT_KEY_PHP, PROJECT_KEY_PHP, "src/Math.php"),
                issueListener);
        assertThat(issueListener.getIssues()).hasSize(1);
    }

    @Test
    public void analysisPython() throws Exception {
        updateGlobal();
        updateModule(PROJECT_KEY_PYTHON);

        SaveIssueListener issueListener = new SaveIssueListener();
        engine.analyze(createAnalysisConfiguration(PROJECT_KEY_PYTHON, PROJECT_KEY_PYTHON, "src/hello.py"),
                issueListener);
        assertThat(issueListener.getIssues()).hasSize(1);
    }

    @Test
    public void analysisUseQualityProfile() throws Exception {
        updateGlobal();
        updateModule(PROJECT_KEY_JAVA);

        SaveIssueListener issueListener = new SaveIssueListener();
        engine.analyze(
                createAnalysisConfiguration(PROJECT_KEY_JAVA, PROJECT_KEY_JAVA, "src/main/java/foo/Foo.java",
                        "sonar.java.binaries", new File("projects/sample-java/target/classes").getAbsolutePath()),
                issueListener);

        assertThat(issueListener.getIssues()).hasSize(2);
    }

    @Test
    public void analysisIssueOnDirectory() throws Exception {
        updateGlobal();
        updateModule(PROJECT_KEY_JAVA_PACKAGE);

        SaveIssueListener issueListener = new SaveIssueListener();
        engine.analyze(createAnalysisConfiguration(PROJECT_KEY_JAVA_PACKAGE, PROJECT_KEY_JAVA,
                "src/main/java/foo/Foo.java", "sonar.java.binaries",
                new File("projects/sample-java/target/classes").getAbsolutePath()), issueListener);

        assertThat(issueListener.getIssues()).extracting("ruleKey", "inputFile.path")
                .containsOnly(tuple("squid:S106",
                        Paths.get("projects/sample-java/src/main/java/foo/Foo.java").toAbsolutePath().toString()),
                        tuple("squid:S1228", null));
    }

    @Test
    public void customSensorsNotExecuted() throws Exception {
        updateGlobal();
        updateModule(PROJECT_KEY_JAVA_CUSTOM_SENSOR);

        SaveIssueListener issueListener = new SaveIssueListener();
        engine.analyze(createAnalysisConfiguration(PROJECT_KEY_JAVA_CUSTOM_SENSOR, PROJECT_KEY_JAVA,
                "src/main/java/foo/Foo.java", "sonar.java.binaries",
                new File("projects/sample-java/target/classes").getAbsolutePath()), issueListener);

        assertThat(issueListener.getIssues()).isEmpty();
    }

    @Test
    public void analysisJavaPomXml() throws Exception {
        updateGlobal();
        updateModule(PROJECT_KEY_JAVA);

        SaveIssueListener issueListener = new SaveIssueListener();
        engine.analyze(createAnalysisConfiguration(PROJECT_KEY_JAVA, PROJECT_KEY_JAVA, "pom.xml"), issueListener);

        assertThat(issueListener.getIssues()).hasSize(1);
    }

    @Test
    public void analysisTemplateRule() throws Exception {
        // WS quality profile is not available before 5.2 so let's skip this test
        assumeTrue(ORCHESTRATOR.getServer().version().isGreaterThanOrEquals("5.2"));
        SearchWsRequest searchReq = new SearchWsRequest();
        searchReq.setProfileName("SonarLint IT Java");
        searchReq.setProjectKey(PROJECT_KEY_JAVA);
        searchReq.setDefaults(false);
        SearchWsResponse search = adminWsClient.qualityProfiles().search(searchReq);
        QualityProfile qp = null;
        for (QualityProfile q : search.getProfilesList()) {
            if (q.getName().equals("SonarLint IT Java")) {
                qp = q;
            }
        }
        assertThat(qp).isNotNull();

        WsRequest request = new PostRequest("/api/rules/create").setParam("custom_key", "myrule")
                .setParam("name", "myrule").setParam("markdown_description", "my_rule_description")
                .setParam("params", "methodName=echo;className=foo.Foo;argumentTypes=int")
                .setParam("template_key", "squid:S2253").setParam("severity", "MAJOR");
        WsResponse response = adminWsClient.wsConnector().call(request);
        assertTrue(response.isSuccessful());

        request = new PostRequest("/api/qualityprofiles/activate_rule").setParam("profile_key", qp.getKey())
                .setParam("rule_key", "squid:myrule");
        response = adminWsClient.wsConnector().call(request);
        assertTrue(response.isSuccessful());

        try {

            updateGlobal();
            updateModule(PROJECT_KEY_JAVA);

            SaveIssueListener issueListener = new SaveIssueListener();
            engine.analyze(createAnalysisConfiguration(PROJECT_KEY_JAVA, PROJECT_KEY_JAVA,
                    "src/main/java/foo/Foo.java", "sonar.java.binaries",
                    new File("projects/sample-java/target/classes").getAbsolutePath()), issueListener);

            assertThat(issueListener.getIssues()).hasSize(3);

            assertThat(engine.getRuleDetails("squid:myrule").getHtmlDescription()).contains("my_rule_description");

        } finally {

            request = new PostRequest("/api/rules/delete").setParam("key", "squid:myrule");
            response = adminWsClient.wsConnector().call(request);
            assertTrue(response.isSuccessful());
        }
    }

    @Test
    public void analysisUseEmptyQualityProfile() throws Exception {
        assumeTrue(ORCHESTRATOR.getServer().version().isGreaterThanOrEquals("5.2"));

        updateGlobal();
        updateModule(PROJECT_KEY_JAVA_EMPTY);

        SaveIssueListener issueListener = new SaveIssueListener();
        engine.analyze(
                createAnalysisConfiguration(PROJECT_KEY_JAVA_EMPTY, PROJECT_KEY_JAVA, "src/main/java/foo/Foo.java",
                        "sonar.java.binaries", new File("projects/sample-java/target/classes").getAbsolutePath()),
                issueListener);

        assertThat(issueListener.getIssues()).isEmpty();
    }

    @Test
    public void analysisUseConfiguration() throws Exception {
        updateGlobal();
        updateModule(PROJECT_KEY_JAVA);

        SaveIssueListener issueListener = new SaveIssueListener();
        engine.analyze(
                createAnalysisConfiguration(PROJECT_KEY_JAVA, PROJECT_KEY_JAVA, "src/main/java/foo/Foo.java",
                        "sonar.java.binaries", new File("projects/sample-java/target/classes").getAbsolutePath()),
                issueListener);
        assertThat(issueListener.getIssues()).hasSize(2);

        // Override default file suffixes in global props so that input file is not considered as a Java file
        setSettings(null, "sonar.java.file.suffixes", ".foo");
        updateGlobal();
        updateModule(PROJECT_KEY_JAVA);

        issueListener.clear();
        engine.analyze(
                createAnalysisConfiguration(PROJECT_KEY_JAVA, PROJECT_KEY_JAVA, "src/main/java/foo/Foo.java",
                        "sonar.java.binaries", new File("projects/sample-java/target/classes").getAbsolutePath()),
                issueListener);

        // Override default file suffixes in project props so that input file is considered as a Java file again
        setSettings(PROJECT_KEY_JAVA, "sonar.java.file.suffixes", ".java");
        updateGlobal();
        updateModule(PROJECT_KEY_JAVA);

        engine.analyze(
                createAnalysisConfiguration(PROJECT_KEY_JAVA, PROJECT_KEY_JAVA, "src/main/java/foo/Foo.java",
                        "sonar.java.binaries", new File("projects/sample-java/target/classes").getAbsolutePath()),
                issueListener);
        assertThat(issueListener.getIssues()).hasSize(2);

    }

    @Test
    public void generateToken() {
        WsHelper ws = new WsHelperImpl();
        ServerConfiguration serverConfig = getServerConfig();

        if (!ORCHESTRATOR.getServer().version().isGreaterThanOrEquals("5.4")) {
            exception.expect(UnsupportedServerException.class);
        }

        String token = ws.generateAuthenticationToken(serverConfig, "name", false);
        assertThat(token).isNotNull();

        token = ws.generateAuthenticationToken(serverConfig, "name", true);
        assertThat(token).isNotNull();
    }

    @Test
    public void checkForUpdate() throws Exception {
        updateGlobal();
        updateModule(PROJECT_KEY_JAVA);

        ServerConfiguration serverConfig = getServerConfig();

        StorageUpdateCheckResult result = engine.checkIfGlobalStorageNeedUpdate(serverConfig, null);
        assertThat(result.needUpdate()).isFalse();

        // restarting server should not lead to notify an update
        ORCHESTRATOR.restartServer();
        result = engine.checkIfGlobalStorageNeedUpdate(serverConfig, null);
        assertThat(result.needUpdate()).isFalse();

        // Change a global setting that is not in the whitelist
        setSettings(null, "sonar.foo", "bar");
        result = engine.checkIfGlobalStorageNeedUpdate(serverConfig, null);
        assertThat(result.needUpdate()).isFalse();

        // Change a global setting that *is* in the whitelist
        setMultiValuesSettings(null, "sonar.inclusions", "**/*");
        // Activate a new rule
        SearchWsResponse response = newAdminWsClient(ORCHESTRATOR).qualityProfiles()
                .search(new SearchWsRequest().setLanguage("java"));
        String profileKey = response.getProfilesList().stream().filter(p -> p.getName().equals("SonarLint IT Java"))
                .findFirst().get().getKey();
        ORCHESTRATOR.getServer().adminWsClient().post("api/qualityprofiles/activate_rule", "profile_key",
                profileKey, "rule_key", "squid:S1228");

        result = engine.checkIfGlobalStorageNeedUpdate(serverConfig, null);
        assertThat(result.needUpdate()).isTrue();
        assertThat(result.changelog()).containsOnly("Global settings updated",
                "Quality profile 'SonarLint IT Java' for language 'Java' updated");

        result = engine.checkIfModuleStorageNeedUpdate(serverConfig, PROJECT_KEY_JAVA, null);
        assertThat(result.needUpdate()).isFalse();

        // Change a project setting that is not in the whitelist
        setSettings(PROJECT_KEY_JAVA, "sonar.foo", "biz");
        result = engine.checkIfModuleStorageNeedUpdate(serverConfig, PROJECT_KEY_JAVA, null);
        assertThat(result.needUpdate()).isFalse();

        // Change a project setting that *is* in the whitelist
        setMultiValuesSettings(PROJECT_KEY_JAVA, "sonar.exclusions", "**/*.foo");

        result = engine.checkIfModuleStorageNeedUpdate(serverConfig, PROJECT_KEY_JAVA, null);
        assertThat(result.needUpdate()).isTrue();
        assertThat(result.changelog()).containsOnly("Project settings updated");
    }

    @Test
    public void downloadOrganizations() throws Exception {
        WsHelper helper = new WsHelperImpl();
        if (ORCHESTRATOR.getServer().version().isGreaterThanOrEquals("6.3")) {
            assertThat(helper.listOrganizations(getServerConfig(), null)).hasSize(1);
        } else {
            try {
                helper.listOrganizations(getServerConfig(), null);
                fail("Expected exception");
            } catch (Exception e) {
                assertThat(e).isInstanceOf(UnsupportedServerException.class);
            }
        }
    }

    private void setSettings(@Nullable String moduleKey, String key, String value) {
        if (ORCHESTRATOR.getServer().version().isGreaterThanOrEquals("6.3")) {
            adminWsClient.settingsService()
                    .set(SetRequest.builder().setKey(key).setValue(value).setComponent(moduleKey).build());
        } else {
            ORCHESTRATOR.getServer().getAdminWsClient()
                    .create(new PropertyCreateQuery(key, value).setResourceKeyOrId(moduleKey));
        }
    }

    private void setMultiValuesSettings(@Nullable String moduleKey, String key, String... values) {
        if (ORCHESTRATOR.getServer().version().isGreaterThanOrEquals("6.3")) {
            adminWsClient.settingsService().set(SetRequest.builder().setKey(key).setValues(Arrays.asList(values))
                    .setComponent(moduleKey).build());
        } else {
            ORCHESTRATOR.getServer().getAdminWsClient()
                    .create(new PropertyCreateQuery(key, asList(values).stream().collect(Collectors.joining(",")))
                            .setResourceKeyOrId(moduleKey));
        }
    }

    private boolean supportHtmlDesc() {
        return ORCHESTRATOR.getServer().version().isGreaterThanOrEquals("5.1");
    }

    private void updateModule(String projectKey) {
        engine.updateModule(getServerConfig(), projectKey, null);
    }

    private void updateGlobal() {
        engine.update(getServerConfig(), null);
    }

    private ServerConfiguration getServerConfig() {
        return ServerConfiguration.builder().url(ORCHESTRATOR.getServer().getUrl()).userAgent("SonarLint ITs")
                .credentials(SONARLINT_USER, SONARLINT_PWD).build();
    }

    private static void removeGroupPermission(String groupName, String permission) {
        if (ORCHESTRATOR.getServer().version().isGreaterThanOrEquals("5.2")) {
            adminWsClient.permissions()
                    .removeGroup(new RemoveGroupWsRequest().setGroupName(groupName).setPermission(permission));
        } else {
            ORCHESTRATOR.getServer().adminWsClient().permissionClient()
                    .removePermission(PermissionParameters.create().group(groupName).permission(permission));
        }
    }

    public static WsClient newAdminWsClient(Orchestrator orchestrator) {
        Server server = orchestrator.getServer();
        return WsClientFactories.getDefault().newClient(HttpConnector.newBuilder().url(server.getUrl())
                .credentials(Server.ADMIN_LOGIN, Server.ADMIN_PASSWORD).build());
    }
}