From ffff02cbed3e5f94bbf0c1718425d66e19ac3901 Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Mon, 14 Aug 2017 14:34:29 +0200 Subject: [PATCH] SONAR-9704 verify that the compute engine is resilient --- .../client/component/ComponentsService.java | 11 ++ .../component/SuggestionsWsRequest.java | 95 +++++++++++ tests/pom.xml | 15 +- .../sonar-project.properties | 5 + .../src/main/xoo/sample/Sample.xoo | 16 ++ .../src/main/xoo/sample/Sample.xoo.measures | 9 ++ .../sonar-project.properties | 5 + .../src/main/xoo/sample/Sample.xoo | 16 ++ .../src/main/xoo/sample/Sample.xoo.measures | 9 ++ .../src/main/xoo/sample/Sample2.xoo | 16 ++ .../src/main/xoo/sample/Sample2.xoo.measures | 9 ++ .../src/main/xoo/sample/Sample3.xoo | 16 ++ .../src/main/xoo/sample/Sample3.xoo.measures | 9 ++ .../sonar-project.properties | 5 + .../src/main/xoo/sample/Sample.xoo | 16 ++ .../src/main/xoo/sample/Sample.xoo.measures | 9 ++ .../src/main/xoo/sample/Sample3.xoo | 16 ++ .../src/main/xoo/sample/Sample3.xoo.measures | 9 ++ .../making_ce_indexation_failing.btm | 8 + .../java/org/sonarqube/tests/Byteman.java | 50 +++++- .../org/sonarqube/tests/QProfileTester.java | 10 ++ .../analysis/AnalysisEsResilienceTest.java | 150 ++++++++++++++++++ .../ActiveRuleEsResilienceTest.java | 20 ++- .../tests/rule/RuleEsResilienceTest.java | 20 ++- .../tests/user/UserEsResilienceTest.java | 23 ++- 25 files changed, 555 insertions(+), 12 deletions(-) create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/component/SuggestionsWsRequest.java create mode 100644 tests/projects/analysis/resilience/resilience-sample-v1/sonar-project.properties create mode 100644 tests/projects/analysis/resilience/resilience-sample-v1/src/main/xoo/sample/Sample.xoo create mode 100644 tests/projects/analysis/resilience/resilience-sample-v1/src/main/xoo/sample/Sample.xoo.measures create mode 100644 tests/projects/analysis/resilience/resilience-sample-v2/sonar-project.properties create mode 100644 tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample.xoo create mode 100644 tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample.xoo.measures create mode 100644 tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample2.xoo create mode 100644 tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample2.xoo.measures create mode 100644 tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample3.xoo create mode 100644 tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample3.xoo.measures create mode 100644 tests/projects/analysis/resilience/resilience-sample-v3/sonar-project.properties create mode 100644 tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample.xoo create mode 100644 tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample.xoo.measures create mode 100644 tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample3.xoo create mode 100644 tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample3.xoo.measures create mode 100644 tests/resilience/making_ce_indexation_failing.btm create mode 100644 tests/src/test/java/org/sonarqube/tests/analysis/AnalysisEsResilienceTest.java diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java index 005e47d92ed..b7186af495f 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java @@ -21,6 +21,7 @@ package org.sonarqube.ws.client.component; import com.google.common.base.Joiner; import java.util.List; +import java.util.stream.Collectors; import org.sonarqube.ws.WsComponents.SearchProjectsWsResponse; import org.sonarqube.ws.WsComponents.SearchWsResponse; import org.sonarqube.ws.WsComponents.ShowWsResponse; @@ -28,11 +29,13 @@ import org.sonarqube.ws.WsComponents.TreeWsResponse; import org.sonarqube.ws.client.BaseService; import org.sonarqube.ws.client.GetRequest; import org.sonarqube.ws.client.WsConnector; +import org.sonarqube.ws.client.WsResponse; import static org.sonar.api.server.ws.WebService.Param; import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SEARCH; import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SEARCH_PROJECTS; import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SHOW; +import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SUGGESTIONS; import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_TREE; import static org.sonarqube.ws.client.component.ComponentsWsParameters.CONTROLLER_COMPONENTS; import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT; @@ -92,4 +95,12 @@ public class ComponentsService extends BaseService { .setParam(Param.FIELDS, !additionalFields.isEmpty() ? inlineMultipleParamValue(additionalFields) : null); return call(get, SearchProjectsWsResponse.parser()); } + + public WsResponse suggestions(SuggestionsWsRequest request) { + GetRequest get = new GetRequest(path(ACTION_SUGGESTIONS)) + .setParam("more", request.getMore() == null ? null : request.getMore().toString()) + .setParam("recentlyBrowsed", request.getRecentlyBrowsed().stream().collect(Collectors.joining(","))) + .setParam("s", request.getS()); + return call(get); + } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/SuggestionsWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/SuggestionsWsRequest.java new file mode 100644 index 00000000000..ca7fd84d11c --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/SuggestionsWsRequest.java @@ -0,0 +1,95 @@ +/* + * 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.sonarqube.ws.client.component; + +import java.util.Collections; +import java.util.List; + +public class SuggestionsWsRequest { + + public static final int MAX_PAGE_SIZE = 500; + public static final int DEFAULT_PAGE_SIZE = 100; + + public enum More { + VW, + SVW, + APP, + TRK, + BRC, + FIL, + UTS + } + + private final More more; + private final List recentlyBrowsed; + private final String s; + + private SuggestionsWsRequest(Builder builder) { + this.more = builder.more; + this.recentlyBrowsed = builder.recentlyBrowsed; + this.s = builder.s; + } + + public More getMore() { + return more; + } + + public List getRecentlyBrowsed() { + return recentlyBrowsed; + } + + public String getS() { + return s; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private More more; + private List recentlyBrowsed = Collections.emptyList(); + private String s; + + private Builder() { + // enforce static factory method + } + + public Builder setMore(More more) { + this.more = more; + return this; + } + + public Builder setRecentlyBrowsed(List recentlyBrowsed) { + this.recentlyBrowsed = recentlyBrowsed; + return this; + } + + public Builder setS(String s) { + this.s = s; + return this; + } + + public SuggestionsWsRequest build() { + return new SuggestionsWsRequest(this); + } + } +} diff --git a/tests/pom.xml b/tests/pom.xml index fee73c2b92a..000477c8eb2 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -24,6 +24,7 @@ * false 9.3.11.v20160721 + 3.0.10 @@ -41,6 +42,12 @@ zip provided + + org.sonarsource.sonarqube + sonar-process + ${project.parent.version} + provided + org.sonarsource.sonarqube sonar-ws @@ -126,6 +133,11 @@ 2.7 zip + + org.jboss.byteman + byteman-submit + ${byteman.version} + @@ -172,7 +184,7 @@ org.jboss.byteman byteman - 3.0.10 + ${byteman.version} false byteman.jar @@ -184,7 +196,6 @@ - diff --git a/tests/projects/analysis/resilience/resilience-sample-v1/sonar-project.properties b/tests/projects/analysis/resilience/resilience-sample-v1/sonar-project.properties new file mode 100644 index 00000000000..9e4aa0e3584 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v1/sonar-project.properties @@ -0,0 +1,5 @@ +sonar.projectKey=sample +sonar.projectName=Sample +sonar.projectVersion=1.0-SNAPSHOT +sonar.sources=src/main/xoo +sonar.language=xoo diff --git a/tests/projects/analysis/resilience/resilience-sample-v1/src/main/xoo/sample/Sample.xoo b/tests/projects/analysis/resilience/resilience-sample-v1/src/main/xoo/sample/Sample.xoo new file mode 100644 index 00000000000..41871e123a3 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v1/src/main/xoo/sample/Sample.xoo @@ -0,0 +1,16 @@ +package sample; + +public class Sample { + + public Sample(int i) { + int j = i++; + } + + private String myMethod() { + if (foo == bar) { + return "hello"; + } else { + throw new IllegalStateException(); + } + } +} diff --git a/tests/projects/analysis/resilience/resilience-sample-v1/src/main/xoo/sample/Sample.xoo.measures b/tests/projects/analysis/resilience/resilience-sample-v1/src/main/xoo/sample/Sample.xoo.measures new file mode 100644 index 00000000000..06c9b6c2f38 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v1/src/main/xoo/sample/Sample.xoo.measures @@ -0,0 +1,9 @@ +ncloc:13 +#Used by dashboard/widgets tests +complexity:3 +complexity_in_classes:3 +cognitive_complexity:4 +classes:1 +comment_lines:3 +public_api:5 +public_undocumented_api:2 diff --git a/tests/projects/analysis/resilience/resilience-sample-v2/sonar-project.properties b/tests/projects/analysis/resilience/resilience-sample-v2/sonar-project.properties new file mode 100644 index 00000000000..1691dc18d68 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v2/sonar-project.properties @@ -0,0 +1,5 @@ +sonar.projectKey=sample +sonar.projectName=Sample +sonar.projectVersion=2.0-SNAPSHOT +sonar.sources=src/main/xoo +sonar.language=xoo diff --git a/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample.xoo b/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample.xoo new file mode 100644 index 00000000000..41871e123a3 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample.xoo @@ -0,0 +1,16 @@ +package sample; + +public class Sample { + + public Sample(int i) { + int j = i++; + } + + private String myMethod() { + if (foo == bar) { + return "hello"; + } else { + throw new IllegalStateException(); + } + } +} diff --git a/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample.xoo.measures b/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample.xoo.measures new file mode 100644 index 00000000000..06c9b6c2f38 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample.xoo.measures @@ -0,0 +1,9 @@ +ncloc:13 +#Used by dashboard/widgets tests +complexity:3 +complexity_in_classes:3 +cognitive_complexity:4 +classes:1 +comment_lines:3 +public_api:5 +public_undocumented_api:2 diff --git a/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample2.xoo b/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample2.xoo new file mode 100644 index 00000000000..41871e123a3 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample2.xoo @@ -0,0 +1,16 @@ +package sample; + +public class Sample { + + public Sample(int i) { + int j = i++; + } + + private String myMethod() { + if (foo == bar) { + return "hello"; + } else { + throw new IllegalStateException(); + } + } +} diff --git a/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample2.xoo.measures b/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample2.xoo.measures new file mode 100644 index 00000000000..06c9b6c2f38 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample2.xoo.measures @@ -0,0 +1,9 @@ +ncloc:13 +#Used by dashboard/widgets tests +complexity:3 +complexity_in_classes:3 +cognitive_complexity:4 +classes:1 +comment_lines:3 +public_api:5 +public_undocumented_api:2 diff --git a/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample3.xoo b/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample3.xoo new file mode 100644 index 00000000000..41871e123a3 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample3.xoo @@ -0,0 +1,16 @@ +package sample; + +public class Sample { + + public Sample(int i) { + int j = i++; + } + + private String myMethod() { + if (foo == bar) { + return "hello"; + } else { + throw new IllegalStateException(); + } + } +} diff --git a/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample3.xoo.measures b/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample3.xoo.measures new file mode 100644 index 00000000000..06c9b6c2f38 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v2/src/main/xoo/sample/Sample3.xoo.measures @@ -0,0 +1,9 @@ +ncloc:13 +#Used by dashboard/widgets tests +complexity:3 +complexity_in_classes:3 +cognitive_complexity:4 +classes:1 +comment_lines:3 +public_api:5 +public_undocumented_api:2 diff --git a/tests/projects/analysis/resilience/resilience-sample-v3/sonar-project.properties b/tests/projects/analysis/resilience/resilience-sample-v3/sonar-project.properties new file mode 100644 index 00000000000..60fded428f3 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v3/sonar-project.properties @@ -0,0 +1,5 @@ +sonar.projectKey=sample +sonar.projectName=Sample +sonar.projectVersion=3.0-SNAPSHOT +sonar.sources=src/main/xoo +sonar.language=xoo diff --git a/tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample.xoo b/tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample.xoo new file mode 100644 index 00000000000..41871e123a3 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample.xoo @@ -0,0 +1,16 @@ +package sample; + +public class Sample { + + public Sample(int i) { + int j = i++; + } + + private String myMethod() { + if (foo == bar) { + return "hello"; + } else { + throw new IllegalStateException(); + } + } +} diff --git a/tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample.xoo.measures b/tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample.xoo.measures new file mode 100644 index 00000000000..06c9b6c2f38 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample.xoo.measures @@ -0,0 +1,9 @@ +ncloc:13 +#Used by dashboard/widgets tests +complexity:3 +complexity_in_classes:3 +cognitive_complexity:4 +classes:1 +comment_lines:3 +public_api:5 +public_undocumented_api:2 diff --git a/tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample3.xoo b/tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample3.xoo new file mode 100644 index 00000000000..41871e123a3 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample3.xoo @@ -0,0 +1,16 @@ +package sample; + +public class Sample { + + public Sample(int i) { + int j = i++; + } + + private String myMethod() { + if (foo == bar) { + return "hello"; + } else { + throw new IllegalStateException(); + } + } +} diff --git a/tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample3.xoo.measures b/tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample3.xoo.measures new file mode 100644 index 00000000000..06c9b6c2f38 --- /dev/null +++ b/tests/projects/analysis/resilience/resilience-sample-v3/src/main/xoo/sample/Sample3.xoo.measures @@ -0,0 +1,9 @@ +ncloc:13 +#Used by dashboard/widgets tests +complexity:3 +complexity_in_classes:3 +cognitive_complexity:4 +classes:1 +comment_lines:3 +public_api:5 +public_undocumented_api:2 diff --git a/tests/resilience/making_ce_indexation_failing.btm b/tests/resilience/making_ce_indexation_failing.btm new file mode 100644 index 00000000000..020382ba303 --- /dev/null +++ b/tests/resilience/making_ce_indexation_failing.btm @@ -0,0 +1,8 @@ +RULE make indexation on ce failing +CLASS IndexAnalysisStep +METHOD execute +COMPILE +AT ENTRY +IF TRUE +DO return +ENDRULE diff --git a/tests/src/test/java/org/sonarqube/tests/Byteman.java b/tests/src/test/java/org/sonarqube/tests/Byteman.java index ccb025e34f9..59dc06f8112 100644 --- a/tests/src/test/java/org/sonarqube/tests/Byteman.java +++ b/tests/src/test/java/org/sonarqube/tests/Byteman.java @@ -21,6 +21,10 @@ import com.sonar.orchestrator.OrchestratorBuilder; import java.io.File; +import java.net.InetAddress; +import java.util.Collections; +import org.jboss.byteman.agent.submit.Submit; +import org.sonar.process.NetworkUtils; import static java.lang.String.format; @@ -30,14 +34,25 @@ import static java.lang.String.format; */ public class Byteman { - public static OrchestratorBuilder enableScript(OrchestratorBuilder builder, String filename) { + private final int port; + private final OrchestratorBuilder builder; + + public enum Process { + WEB("sonar.web.javaAdditionalOpts"), CE("sonar.ce.javaAdditionalOpts"); + + private final String argument; + + Process(String argument) { + this.argument = argument; + } + } + + public Byteman(OrchestratorBuilder builder, Process process) { + this.builder = builder; String jar = findBytemanJar(); - builder - .setServerProperty("sonar.web.javaAdditionalOpts", - format("-javaagent:%s=script:%s,boot:%s", jar, findBytemanScript(filename), jar)) - .setServerProperty("sonar.search.recovery.delayInMs", "1000") - .setServerProperty("sonar.search.recovery.minAgeInMs", "3000"); - return builder; + port = NetworkUtils.getNextAvailablePort(InetAddress.getLoopbackAddress()); + String bytemanArg = format("-javaagent:%s=boot:%s,port:%d", jar, jar, port); + builder.setServerProperty(process.argument, bytemanArg); } private static String findBytemanJar() { @@ -49,6 +64,12 @@ public class Byteman { return jar.getAbsolutePath(); } + public void activateScript(String filename) throws Exception { + String bytemanScript = findBytemanScript(filename); + Submit submit = new Submit(InetAddress.getLoopbackAddress().getHostAddress(), port); + submit.addRulesFromFiles(Collections.singletonList(bytemanScript)); + } + private static String findBytemanScript(String filename) { // see pom.xml, Maven copies and renames the artifact. File script = new File( filename); @@ -57,4 +78,19 @@ public class Byteman { } return script.getAbsolutePath(); } + + public void deactivateAllRules() throws Exception { + Submit submit = new Submit(InetAddress.getLoopbackAddress().getHostAddress(), port); + try { + submit.deleteAllRules(); + } catch (java.lang.Exception e) { + if (e.getMessage() == null || !e.getMessage().contains("No rule scripts to remove")) { + throw e; + } + } + } + + public OrchestratorBuilder getOrchestratorBuilder() { + return builder; + } } diff --git a/tests/src/test/java/org/sonarqube/tests/QProfileTester.java b/tests/src/test/java/org/sonarqube/tests/QProfileTester.java index 764b80d6955..c2d163151c6 100644 --- a/tests/src/test/java/org/sonarqube/tests/QProfileTester.java +++ b/tests/src/test/java/org/sonarqube/tests/QProfileTester.java @@ -26,8 +26,10 @@ import org.sonarqube.ws.Common; import org.sonarqube.ws.Organizations.Organization; import org.sonarqube.ws.QualityProfiles.CreateWsResponse.QualityProfile; import org.sonarqube.ws.Rules; +import org.sonarqube.ws.WsProjects.CreateWsResponse.Project; import org.sonarqube.ws.client.HttpException; import org.sonarqube.ws.client.qualityprofile.ActivateRuleWsRequest; +import org.sonarqube.ws.client.qualityprofile.AddProjectRequest; import org.sonarqube.ws.client.qualityprofile.CreateRequest; import org.sonarqube.ws.client.qualityprofile.QualityProfilesService; import org.sonarqube.ws.client.rule.SearchWsRequest; @@ -79,6 +81,14 @@ public class QProfileTester { return this; } + public QProfileTester assignQProfileToProject(QualityProfile profile, Project project) { + service().addProject(AddProjectRequest.builder() + .setProjectKey(project.getKey()) + .setProfileKey(profile.getKey()) + .build()); + return this; + } + public QProfileTester assertThatNumberOfActiveRulesEqualsTo(QualityProfile profile, int expectedActiveRules) { return assertThatNumberOfActiveRulesEqualsTo(profile.getKey(), expectedActiveRules); } diff --git a/tests/src/test/java/org/sonarqube/tests/analysis/AnalysisEsResilienceTest.java b/tests/src/test/java/org/sonarqube/tests/analysis/AnalysisEsResilienceTest.java new file mode 100644 index 00000000000..6d6adeb2030 --- /dev/null +++ b/tests/src/test/java/org/sonarqube/tests/analysis/AnalysisEsResilienceTest.java @@ -0,0 +1,150 @@ +/* + * 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.sonarqube.tests.analysis; + +import com.sonar.orchestrator.Orchestrator; +import com.sonar.orchestrator.build.BuildResult; +import com.sonar.orchestrator.build.SonarScanner; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.After; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.sonarqube.tests.Byteman; +import org.sonarqube.tests.Tester; +import org.sonarqube.ws.Issues; +import org.sonarqube.ws.Organizations.Organization; +import org.sonarqube.ws.QualityProfiles.CreateWsResponse.QualityProfile; +import org.sonarqube.ws.WsProjects; +import org.sonarqube.ws.WsUsers.CreateWsResponse.User; +import org.sonarqube.ws.client.component.SuggestionsWsRequest; +import org.sonarqube.ws.client.issue.SearchWsRequest; +import util.ItUtils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.sonarqube.tests.Byteman.Process.CE; +import static util.ItUtils.projectDir; + +public class AnalysisEsResilienceTest { + + @ClassRule + public static final Orchestrator orchestrator; + private static final Byteman byteman; + + static { + byteman = new Byteman(Orchestrator.builderEnv(), CE); + orchestrator = byteman + .getOrchestratorBuilder() + .addPlugin(ItUtils.xooPlugin()) + .build(); + } + + @Rule + public Tester tester = new Tester(orchestrator); + + @After + public void after() throws Exception { + byteman.deactivateAllRules(); + } + + @Test + public void activation_and_deactivation_of_rule_is_resilient_to_indexing_errors() throws Exception { + Organization organization = tester.organizations().generate(); + User orgAdministrator = tester.users().generateAdministrator(organization); + WsProjects.CreateWsResponse.Project project = tester.projects().generate(organization); + String projectKey = project.getKey(); + String fileKey = projectKey + ":src/main/xoo/sample/Sample.xoo"; + String file2Key = projectKey + ":src/main/xoo/sample/Sample2.xoo"; + String file3Key = projectKey + ":src/main/xoo/sample/Sample3.xoo"; + + QualityProfile profile = tester.qProfiles().createXooProfile(organization); + tester.qProfiles() + .activateRule(profile, "xoo:OneIssuePerFile") + .assignQProfileToProject(profile, project); + + executeAnalysis(projectKey, organization, orgAdministrator, "analysis/resilience/resilience-sample-v1"); + assertThat(searchFile(fileKey, organization)).isNotEmpty(); + assertThat(searchFile(file2Key, organization)).isEmpty(); + assertThat(searchFile(file3Key, organization)).isEmpty(); + List issues = searchIssues(projectKey); + assertThat(issues) + .extracting(Issues.Issue::getComponent) + .containsExactlyInAnyOrder(fileKey); + + byteman.activateScript("resilience/making_ce_indexation_failing.btm"); + executeAnalysis(projectKey, organization, orgAdministrator, "analysis/resilience/resilience-sample-v2"); + assertThat(searchFile(fileKey, organization)).isNotEmpty(); + assertThat(searchFile(file2Key, organization)).isEmpty();// inconsistency: in DB there is also file2Key + assertThat(searchFile(file3Key, organization)).isEmpty();// inconsistency: in DB there is also file3Key + issues = searchIssues(projectKey); + assertThat(issues) + .extracting(Issues.Issue::getComponent) + .containsExactlyInAnyOrder(fileKey /* inconsistency: in DB there is also file2Key and file3Key */); + byteman.deactivateAllRules(); + + executeAnalysis(projectKey, organization, orgAdministrator, "analysis/resilience/resilience-sample-v3"); + assertThat(searchFile(fileKey, organization)).isNotEmpty(); + assertThat(searchFile(file2Key, organization)).isEmpty(); + assertThat(searchFile(file3Key, organization)).isNotEmpty(); + issues = searchIssues(projectKey); + assertThat(issues) + .extracting(Issues.Issue::getComponent, Issues.Issue::getStatus) + .containsExactlyInAnyOrder( + tuple(fileKey, "OPEN"), + tuple(file2Key, "CLOSED"), + tuple(file3Key, "OPEN")); + } + + private List searchIssues(String projectKey) { + SearchWsRequest request = new SearchWsRequest() + .setProjectKeys(Collections.singletonList(projectKey)); + Issues.SearchWsResponse results = tester.wsClient().issues().search(request); + return results.getIssuesList(); + } + + private List searchFile(String key, Organization organization) { + SuggestionsWsRequest query = SuggestionsWsRequest.builder() + .setS(key) + .build(); + Map response = ItUtils.jsonToMap( + tester.wsClient().components().suggestions(query).content() + ); + List results = (List) response.get("results"); + Map trkResult = (Map) results.stream().filter(result -> "FIL".equals(((Map) result).get("q"))).findAny().get(); + List items = (List) trkResult.get("items"); + Stream x = items.stream().map(item -> (String) ((Map) item).get("key")); + return x.collect(Collectors.toList()); + } + + private String executeAnalysis(String projectKey, Organization organization, User orgAdministrator, String projectPath) { + BuildResult buildResult = orchestrator.executeBuild(SonarScanner.create(projectDir(projectPath), + "sonar.organization", organization.getKey(), + "sonar.projectKey", projectKey, + "sonar.login", orgAdministrator.getLogin(), + "sonar.password", orgAdministrator.getLogin())); + return ItUtils.extractCeTaskId(buildResult); + } + +} diff --git a/tests/src/test/java/org/sonarqube/tests/qualityProfile/ActiveRuleEsResilienceTest.java b/tests/src/test/java/org/sonarqube/tests/qualityProfile/ActiveRuleEsResilienceTest.java index cfa5f160423..49be3215f07 100644 --- a/tests/src/test/java/org/sonarqube/tests/qualityProfile/ActiveRuleEsResilienceTest.java +++ b/tests/src/test/java/org/sonarqube/tests/qualityProfile/ActiveRuleEsResilienceTest.java @@ -21,6 +21,8 @@ package org.sonarqube.tests.qualityProfile; import com.sonar.orchestrator.Orchestrator; import java.util.concurrent.TimeUnit; +import org.junit.After; +import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -35,19 +37,35 @@ import org.sonarqube.ws.client.rule.SearchWsRequest; import util.ItUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonarqube.tests.Byteman.Process.WEB; public class ActiveRuleEsResilienceTest { private static final String RULE_ONE_BUG_PER_LINE = "xoo:OneBugIssuePerLine"; @ClassRule public static final Orchestrator orchestrator; + private static final Byteman byteman; static { - orchestrator = Byteman.enableScript(Orchestrator.builderEnv(), "resilience/active_rule_indexer.btm") + byteman = new Byteman(Orchestrator.builderEnv(), WEB); + orchestrator = byteman + .getOrchestratorBuilder() + .setServerProperty("sonar.search.recovery.delayInMs", "1000") + .setServerProperty("sonar.search.recovery.minAgeInMs", "3000") .addPlugin(ItUtils.xooPlugin()) .build(); } + @Before + public void before() throws Exception { + byteman.activateScript("resilience/active_rule_indexer.btm"); + } + + @After + public void after() throws Exception { + byteman.deactivateAllRules(); + } + @Rule public TestRule timeout = new DisableOnDebug(Timeout.builder() .withLookingForStuckThread(true) diff --git a/tests/src/test/java/org/sonarqube/tests/rule/RuleEsResilienceTest.java b/tests/src/test/java/org/sonarqube/tests/rule/RuleEsResilienceTest.java index a9f606fb320..c887cf652f3 100644 --- a/tests/src/test/java/org/sonarqube/tests/rule/RuleEsResilienceTest.java +++ b/tests/src/test/java/org/sonarqube/tests/rule/RuleEsResilienceTest.java @@ -21,6 +21,8 @@ package org.sonarqube.tests.rule; import com.sonar.orchestrator.Orchestrator; import java.util.concurrent.TimeUnit; +import org.junit.After; +import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -35,18 +37,34 @@ import util.ItUtils; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonarqube.tests.Byteman.Process.WEB; public class RuleEsResilienceTest { @ClassRule public static final Orchestrator orchestrator; + private static final Byteman byteman; static { - orchestrator = Byteman.enableScript(Orchestrator.builderEnv(), "resilience/rule_indexer.btm") + byteman = new Byteman(Orchestrator.builderEnv(), WEB); + orchestrator = byteman + .getOrchestratorBuilder() + .setServerProperty("sonar.search.recovery.delayInMs", "1000") + .setServerProperty("sonar.search.recovery.minAgeInMs", "3000") .addPlugin(ItUtils.xooPlugin()) .build(); } + @Before + public void before() throws Exception { + byteman.activateScript("resilience/rule_indexer.btm"); + } + + @After + public void after() throws Exception { + byteman.deactivateAllRules(); + } + @Rule public TestRule timeout = new DisableOnDebug(Timeout.builder() .withLookingForStuckThread(true) diff --git a/tests/src/test/java/org/sonarqube/tests/user/UserEsResilienceTest.java b/tests/src/test/java/org/sonarqube/tests/user/UserEsResilienceTest.java index b98661e8fe4..3f2d69fe898 100644 --- a/tests/src/test/java/org/sonarqube/tests/user/UserEsResilienceTest.java +++ b/tests/src/test/java/org/sonarqube/tests/user/UserEsResilienceTest.java @@ -21,6 +21,8 @@ package org.sonarqube.tests.user; import com.sonar.orchestrator.Orchestrator; import java.util.concurrent.TimeUnit; +import org.junit.After; +import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -32,17 +34,36 @@ import org.sonarqube.tests.Tester; import org.sonarqube.ws.WsUsers.CreateWsResponse.User; import org.sonarqube.ws.client.user.SearchRequest; import org.sonarqube.ws.client.user.UpdateRequest; +import util.ItUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonarqube.tests.Byteman.Process.WEB; import static util.ItUtils.expectHttpError; public class UserEsResilienceTest { @ClassRule public static final Orchestrator orchestrator; + private static final Byteman byteman; static { - orchestrator = Byteman.enableScript(Orchestrator.builderEnv(), "resilience/user_indexer.btm").build(); + byteman = new Byteman(Orchestrator.builderEnv(), WEB); + orchestrator = byteman + .getOrchestratorBuilder() + .setServerProperty("sonar.search.recovery.delayInMs", "1000") + .setServerProperty("sonar.search.recovery.minAgeInMs", "3000") + .addPlugin(ItUtils.xooPlugin()) + .build(); + } + + @Before + public void before() throws Exception { + byteman.activateScript("resilience/user_indexer.btm"); + } + + @After + public void after() throws Exception { + byteman.deactivateAllRules(); } @Rule -- 2.39.5