aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server-common/src/test/java/org
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2018-06-22 17:01:46 +0200
committersonartech <sonartech@sonarsource.com>2018-06-29 09:10:15 +0200
commit0316e0b883129594466be5b1088a8ee9b1d06461 (patch)
tree95168e912876065bf282a0ca854d613d07c0a8ef /server/sonar-server-common/src/test/java/org
parent1653051ae4b32591b04691cb4c22bcb75d4a404c (diff)
downloadsonarqube-0316e0b883129594466be5b1088a8ee9b1d06461.tar.gz
sonarqube-0316e0b883129594466be5b1088a8ee9b1d06461.zip
move some classes (including webhooks) to server-common
Diffstat (limited to 'server/sonar-server-common/src/test/java/org')
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/async/AsyncExecutionExecutorServiceImplTest.java58
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/async/AsyncExecutionImplTest.java68
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/async/AsyncExecutionMBeanImplTest.java87
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/project/ProjectTest.java78
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ConditionTest.java128
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/EvaluatedConditionTest.java110
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/EvaluatedQualityGateTest.java200
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/QualityGateTest.java135
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/util/OkHttpClientProviderTest.java88
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/AnalysisTest.java69
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/AsynchronousWebHooksImplTest.java106
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/BranchTest.java55
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/CeTaskTest.java55
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/ProjectAnalysisTest.java154
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/SynchronousWebHooksImplTest.java138
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/TestWebhookCaller.java75
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java218
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java119
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookDeliveryTest.java82
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookModuleTest.java46
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java329
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookTest.java78
22 files changed, 2476 insertions, 0 deletions
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/async/AsyncExecutionExecutorServiceImplTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/async/AsyncExecutionExecutorServiceImplTest.java
new file mode 100644
index 00000000000..8aa2ee5165b
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/async/AsyncExecutionExecutorServiceImplTest.java
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AsyncExecutionExecutorServiceImplTest {
+ private AsyncExecutionExecutorServiceImpl underTest = new AsyncExecutionExecutorServiceImpl();
+
+ @Test
+ public void submit_executes_runnable_in_another_thread() {
+ try (SlowRunnable slowRunnable = new SlowRunnable()) {
+ underTest.submit(slowRunnable);
+ assertThat(slowRunnable.executed).isFalse();
+ }
+ }
+
+ private static final class SlowRunnable implements Runnable, AutoCloseable {
+ private final CountDownLatch latch = new CountDownLatch(1);
+ private volatile boolean executed = false;
+
+ @Override
+ public void run() {
+ try {
+ latch.await(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ executed = true;
+ }
+
+ @Override
+ public void close() {
+ latch.countDown();
+ }
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/async/AsyncExecutionImplTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/async/AsyncExecutionImplTest.java
new file mode 100644
index 00000000000..0ff2d3cec07
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/async/AsyncExecutionImplTest.java
@@ -0,0 +1,68 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AsyncExecutionImplTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ private AsyncExecutionExecutorService synchronousExecutorService = Runnable::run;
+ private AsyncExecutionImpl underTest = new AsyncExecutionImpl(synchronousExecutorService);
+
+ @Test
+ public void addToQueue_fails_with_NPE_if_Runnable_is_null() {
+ expectedException.expect(NullPointerException.class);
+
+ underTest.addToQueue(null);
+ }
+
+ @Test
+ public void addToQueue_submits_runnable_to_executorService_which_does_not_fail_if_Runnable_argument_throws_exception() {
+ underTest.addToQueue(() -> {
+ throw new RuntimeException("Faking an exception thrown by Runnable argument");
+ });
+
+ assertThat(logTester.logs()).hasSize(1);
+ assertThat(logTester.logs(LoggerLevel.ERROR)).containsOnly("Asynchronous task failed");
+ }
+
+ @Test
+ public void addToQueue_submits_runnable_that_fails_if_Runnable_argument_throws_Error() {
+ Error expected = new Error("Faking an exception thrown by Runnable argument");
+ Runnable runnable = () -> {
+ throw expected;
+ };
+
+ expectedException.expect(Error.class);
+ expectedException.expectMessage(expected.getMessage());
+
+ underTest.addToQueue(runnable);
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/async/AsyncExecutionMBeanImplTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/async/AsyncExecutionMBeanImplTest.java
new file mode 100644
index 00000000000..e534273abfb
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/async/AsyncExecutionMBeanImplTest.java
@@ -0,0 +1,87 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+import java.lang.management.ManagementFactory;
+import javax.annotation.CheckForNull;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class AsyncExecutionMBeanImplTest {
+ private AsyncExecutionMonitoring asyncExecutionMonitoring = Mockito.mock(AsyncExecutionMonitoring.class);
+
+ private AsyncExecutionMBeanImpl underTest = new AsyncExecutionMBeanImpl(asyncExecutionMonitoring);
+
+ @Test
+ public void register_and_unregister() throws Exception {
+ assertThat(getMBean()).isNull();
+
+ underTest.start();
+ assertThat(getMBean()).isNotNull();
+
+ underTest.stop();
+ assertThat(getMBean()).isNull();
+ }
+
+ @Test
+ public void getQueueSize_delegates_to_AsyncExecutionMonitoring() {
+ when(asyncExecutionMonitoring.getQueueSize()).thenReturn(12);
+
+ assertThat(underTest.getQueueSize()).isEqualTo(12);
+
+ verify(asyncExecutionMonitoring).getQueueSize();
+ }
+
+ @Test
+ public void getWorkerCount_delegates_to_AsyncExecutionMonitoring() {
+ when(asyncExecutionMonitoring.getWorkerCount()).thenReturn(12);
+
+ assertThat(underTest.getWorkerCount()).isEqualTo(12);
+
+ verify(asyncExecutionMonitoring).getWorkerCount();
+ }
+
+ @Test
+ public void getLargestWorkerCount_delegates_to_AsyncExecutionMonitoring() {
+ when(asyncExecutionMonitoring.getLargestWorkerCount()).thenReturn(12);
+
+ assertThat(underTest.getLargestWorkerCount()).isEqualTo(12);
+
+ verify(asyncExecutionMonitoring).getLargestWorkerCount();
+ }
+
+ @CheckForNull
+ private ObjectInstance getMBean() throws Exception {
+ try {
+ return ManagementFactory.getPlatformMBeanServer().getObjectInstance(new ObjectName(AsyncExecutionMBean.OBJECT_NAME));
+ } catch (InstanceNotFoundException e) {
+ return null;
+ }
+ }
+
+
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/project/ProjectTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/project/ProjectTest.java
new file mode 100644
index 00000000000..4f2a351e844
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/project/ProjectTest.java
@@ -0,0 +1,78 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.project;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectTest {
+ @Test
+ public void test_bean_without_description() {
+ Project project1 = new Project("U1", "K1", "N1");
+ Project project2 = new Project("U1", "K1", "N1", null);
+
+ assertThat(project1.getUuid()).isEqualTo(project2.getUuid()).isEqualTo("U1");
+ assertThat(project1.getKey()).isEqualTo(project2.getKey()).isEqualTo("K1");
+ assertThat(project1.getName()).isEqualTo(project2.getName()).isEqualTo("N1");
+ assertThat(project1.getDescription()).isEqualTo(project2.getDescription()).isNull();
+
+ assertThat(project1.toString())
+ .isEqualTo(project2.toString())
+ .isEqualTo("Project{uuid='U1', key='K1', name='N1', description=null}");
+ }
+
+ @Test
+ public void test_bean_with_description() {
+ Project project1 = new Project("U1", "K1", "N1", "D1");
+
+ assertThat(project1.getUuid()).isEqualTo("U1");
+ assertThat(project1.getKey()).isEqualTo("K1");
+ assertThat(project1.getName()).isEqualTo("N1");
+ assertThat(project1.getDescription()).isEqualTo("D1");
+
+ assertThat(project1.toString())
+ .isEqualTo(project1.toString())
+ .isEqualTo("Project{uuid='U1', key='K1', name='N1', description='D1'}");
+ }
+
+ @Test
+ public void test_equals_and_hashCode() {
+ Project project1 = new Project("U1", "K1", "N1");
+ Project project2 = new Project("U1", "K1", "N1", "D1");
+
+ assertThat(project1).isEqualTo(project1);
+ assertThat(project1).isNotEqualTo(null);
+ assertThat(project1).isNotEqualTo(new Object());
+ assertThat(project1).isEqualTo(new Project("U1", "K1", "N1", null));
+ assertThat(project1).isNotEqualTo(new Project("U1", "K2", "N1", null));
+ assertThat(project1).isNotEqualTo(new Project("U1", "K1", "N2", null));
+ assertThat(project1).isEqualTo(project2);
+
+ assertThat(project1.hashCode()).isEqualTo(project1.hashCode());
+ assertThat(project1.hashCode()).isNotEqualTo(null);
+ assertThat(project1.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(project1.hashCode()).isEqualTo(new Project("U1", "K1", "N1", null).hashCode());
+ assertThat(project1.hashCode()).isNotEqualTo(new Project("U1", "K2", "N1", null).hashCode());
+ assertThat(project1.hashCode()).isNotEqualTo(new Project("U1", "K1", "N2", null).hashCode());
+ assertThat(project1.hashCode()).isEqualTo(project2.hashCode());
+ }
+
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ConditionTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ConditionTest.java
new file mode 100644
index 00000000000..ba0b79a7fef
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ConditionTest.java
@@ -0,0 +1,128 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualitygate;
+
+import java.util.Arrays;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ConditionTest {
+ private static final String METRIC_KEY = "metric_key";
+ private static final Condition.Operator OPERATOR = Condition.Operator.EQUALS;
+ private static final String ERROR_THRESHOLD = "2";
+ private static final String WARN_THRESHOLD = "4";
+ private static final boolean ON_LEAK_PERIOD = true;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private Condition underTest = new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD);
+
+ @Test
+ public void constructor_throws_NPE_if_metricKey_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("metricKey can't be null");
+
+ new Condition(null, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD);
+ }
+
+ @Test
+ public void constructor_throws_NPE_if_operator_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("operator can't be null");
+
+ new Condition(METRIC_KEY, null, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD);
+ }
+
+ @Test
+ public void errorThreshold_can_be_null() {
+ Condition underTest = new Condition(METRIC_KEY, OPERATOR, null, WARN_THRESHOLD, ON_LEAK_PERIOD);
+
+ assertThat(underTest.getErrorThreshold()).isEmpty();
+ }
+
+ @Test
+ public void warnThreshold_can_be_null() {
+ Condition underTest = new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, null, ON_LEAK_PERIOD);
+
+ assertThat(underTest.getWarningThreshold()).isEmpty();
+ }
+
+ @Test
+ public void verify_getters() {
+ assertThat(underTest.getMetricKey()).isEqualTo(METRIC_KEY);
+ assertThat(underTest.getOperator()).isEqualTo(OPERATOR);
+ assertThat(underTest.getErrorThreshold()).contains(ERROR_THRESHOLD);
+ assertThat(underTest.getWarningThreshold()).contains(WARN_THRESHOLD);
+ assertThat(underTest.isOnLeakPeriod()).isEqualTo(ON_LEAK_PERIOD);
+ }
+
+ @Test
+ public void toString_is_override() {
+ assertThat(underTest.toString())
+ .isEqualTo("Condition{metricKey='metric_key', operator=EQUALS, warningThreshold='4', errorThreshold='2', onLeakPeriod=true}");
+ }
+
+ @Test
+ public void toString_does_not_quote_nulls() {
+ Condition withNulls = new Condition("metric_key", Condition.Operator.LESS_THAN, null, null, false);
+ assertThat(withNulls.toString())
+ .isEqualTo("Condition{metricKey='metric_key', operator=LESS_THAN, warningThreshold=null, errorThreshold=null, onLeakPeriod=false}");
+ }
+
+ @Test
+ public void equals_is_based_on_all_fields() {
+ assertThat(underTest).isEqualTo(underTest);
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Object());
+ assertThat(underTest).isEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD));
+ assertThat(underTest).isNotEqualTo(new Condition("other_metric_key", OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD));
+ Arrays.stream(Condition.Operator.values())
+ .filter(s -> !OPERATOR.equals(s))
+ .forEach(otherOperator -> assertThat(underTest)
+ .isNotEqualTo(new Condition(METRIC_KEY, otherOperator, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD)));
+ assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, null, WARN_THRESHOLD, ON_LEAK_PERIOD));
+ assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, "other_error_threshold", WARN_THRESHOLD, ON_LEAK_PERIOD));
+ assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, null, ON_LEAK_PERIOD));
+ assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, "other_warn_threshold", ON_LEAK_PERIOD));
+ assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, !ON_LEAK_PERIOD));
+ }
+
+ @Test
+ public void hashcode_is_based_on_all_fields() {
+ assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(null);
+ assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(underTest.hashCode()).isEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Condition("other_metric_key", OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode());
+ Arrays.stream(Condition.Operator.values())
+ .filter(s -> !OPERATOR.equals(s))
+ .forEach(otherOperator -> assertThat(underTest.hashCode())
+ .isNotEqualTo(new Condition(METRIC_KEY, otherOperator, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode()));
+ assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, null, WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, "other_error_threshold", WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, null, ON_LEAK_PERIOD).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, "other_warn_threshold", ON_LEAK_PERIOD).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, !ON_LEAK_PERIOD).hashCode());
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/EvaluatedConditionTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/EvaluatedConditionTest.java
new file mode 100644
index 00000000000..7cadd61e89f
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/EvaluatedConditionTest.java
@@ -0,0 +1,110 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualitygate;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.qualitygate.Condition.Operator.EQUALS;
+import static org.sonar.server.qualitygate.EvaluatedCondition.EvaluationStatus.OK;
+import static org.sonar.server.qualitygate.EvaluatedCondition.EvaluationStatus.WARN;
+
+public class EvaluatedConditionTest {
+ private static final Condition CONDITION_1 = new Condition("metricKey", EQUALS, "2", "4", false);
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, WARN, "value");
+
+ @Test
+ public void constructor_throws_NPE_if_condition_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("condition can't be null");
+
+ new EvaluatedCondition(null, WARN, "value");
+ }
+
+ @Test
+ public void constructor_throws_NPE_if_EvaluationStatus_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("status can't be null");
+
+ new EvaluatedCondition(CONDITION_1, null, "value");
+ }
+
+ @Test
+ public void constructor_accepts_null_value() {
+ EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, WARN, null);
+
+ assertThat(underTest.getValue()).isEmpty();
+ }
+
+ @Test
+ public void verify_getters() {
+ EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, WARN, "value");
+
+ assertThat(underTest.getCondition()).isEqualTo(CONDITION_1);
+ assertThat(underTest.getStatus()).isEqualTo(WARN);
+ assertThat(underTest.getValue()).contains("value");
+ }
+
+ @Test
+ public void override_toString() {
+ assertThat(underTest.toString()).isEqualTo("EvaluatedCondition{condition=" +
+ "Condition{metricKey='metricKey', operator=EQUALS, warningThreshold='4', errorThreshold='2', onLeakPeriod=false}, " +
+ "status=WARN, value='value'}");
+ }
+
+ @Test
+ public void toString_does_not_quote_null_value() {
+ EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, WARN, null);
+
+ assertThat(underTest.toString()).isEqualTo("EvaluatedCondition{condition=" +
+ "Condition{metricKey='metricKey', operator=EQUALS, warningThreshold='4', errorThreshold='2', onLeakPeriod=false}, " +
+ "status=WARN, value=null}");
+ }
+
+ @Test
+ public void equals_is_based_on_all_fields() {
+ assertThat(underTest).isEqualTo(underTest);
+ assertThat(underTest).isEqualTo(new EvaluatedCondition(CONDITION_1, WARN, "value"));
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Object());
+ assertThat(underTest).isNotEqualTo(new EvaluatedCondition(new Condition("other_metric", EQUALS, "a", "b", true), WARN, "value"));
+ assertThat(underTest).isNotEqualTo(new EvaluatedCondition(CONDITION_1, OK, "value"));
+ assertThat(underTest).isNotEqualTo(new EvaluatedCondition(CONDITION_1, WARN, null));
+ assertThat(underTest).isNotEqualTo(new EvaluatedCondition(CONDITION_1, WARN, "other_value"));
+ }
+
+ @Test
+ public void hashcode_is_based_on_all_fields() {
+ assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+ assertThat(underTest.hashCode()).isEqualTo(new EvaluatedCondition(CONDITION_1, WARN, "value").hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(null);
+ assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new EvaluatedCondition(new Condition("other_metric", EQUALS, "a", "b", true), WARN, "value").hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new EvaluatedCondition(CONDITION_1, OK, "value").hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new EvaluatedCondition(CONDITION_1, WARN, null).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new EvaluatedCondition(CONDITION_1, WARN, "other_value").hashCode());
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/EvaluatedQualityGateTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/EvaluatedQualityGateTest.java
new file mode 100644
index 00000000000..b02d5dd2cd5
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/EvaluatedQualityGateTest.java
@@ -0,0 +1,200 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualitygate;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Random;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.measures.Metric.Level;
+
+import static java.util.Collections.emptySet;
+import static java.util.Collections.singleton;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.qualitygate.EvaluatedQualityGate.newBuilder;
+
+public class EvaluatedQualityGateTest {
+ private static final String QUALITY_GATE_ID = "qg_id";
+ private static final String QUALITY_GATE_NAME = "qg_name";
+ private static final QualityGate NO_CONDITION_QUALITY_GATE = new QualityGate(QUALITY_GATE_ID, QUALITY_GATE_NAME, emptySet());
+ private static final Condition CONDITION_1 = new Condition("metric_key", Condition.Operator.LESS_THAN, "2", "4", true);
+ private static final Condition CONDITION_2 = new Condition("metric_key_2", Condition.Operator.GREATER_THAN, "6", "12", false);
+ private static final QualityGate ONE_CONDITION_QUALITY_GATE = new QualityGate(QUALITY_GATE_ID, QUALITY_GATE_NAME, singleton(CONDITION_1));
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private final Random random = new Random();
+ private final Level randomStatus = Level.values()[random.nextInt(Level.values().length)];
+ private final EvaluatedCondition.EvaluationStatus randomEvaluationStatus = EvaluatedCondition.EvaluationStatus.values()[random
+ .nextInt(EvaluatedCondition.EvaluationStatus.values().length)];
+ private final String randomValue = random.nextBoolean() ? null : RandomStringUtils.randomAlphanumeric(3);
+
+ private EvaluatedQualityGate.Builder builder = newBuilder();
+
+ @Test
+ public void build_fails_with_NPE_if_status_not_set() {
+ builder.setQualityGate(NO_CONDITION_QUALITY_GATE);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("status can't be null");
+
+ builder.build();
+ }
+
+ @Test
+ public void addCondition_fails_with_NPE_if_condition_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("condition can't be null");
+
+ builder.addCondition(null, EvaluatedCondition.EvaluationStatus.WARN, "a_value");
+ }
+
+ @Test
+ public void addCondition_fails_with_NPE_if_status_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("status can't be null");
+
+ builder.addCondition(new Condition("metric_key", Condition.Operator.LESS_THAN, "2", "4", true), null, "a_value");
+ }
+
+ @Test
+ public void addCondition_accepts_null_value() {
+ builder.addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.NO_VALUE, null);
+
+ assertThat(builder.getEvaluatedConditions())
+ .containsOnly(new EvaluatedCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.NO_VALUE, null));
+ }
+
+ @Test
+ public void getEvaluatedConditions_returns_empty_with_no_condition_added_to_builder() {
+ assertThat(builder.getEvaluatedConditions()).isEmpty();
+ }
+
+ @Test
+ public void build_fails_with_IAE_if_condition_added_and_no_on_QualityGate() {
+ builder.setQualityGate(NO_CONDITION_QUALITY_GATE)
+ .setStatus(randomStatus)
+ .addCondition(CONDITION_1, randomEvaluationStatus, randomValue);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Evaluation provided for unknown conditions: [" + CONDITION_1 + "]");
+
+ builder.build();
+ }
+
+ @Test
+ public void build_fails_with_IAE_if_condition_is_missing_for_one_defined_in_QualityGate() {
+ builder.setQualityGate(ONE_CONDITION_QUALITY_GATE)
+ .setStatus(randomStatus);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Evaluation missing for the following conditions: [" + CONDITION_1 + "]");
+
+ builder.build();
+ }
+
+ @Test
+ public void verify_getters() {
+ EvaluatedQualityGate underTest = builder
+ .setQualityGate(ONE_CONDITION_QUALITY_GATE)
+ .setStatus(randomStatus)
+ .addCondition(CONDITION_1, randomEvaluationStatus, randomValue)
+ .build();
+
+ assertThat(underTest.getQualityGate()).isEqualTo(ONE_CONDITION_QUALITY_GATE);
+ assertThat(underTest.getStatus()).isEqualTo(randomStatus);
+ assertThat(underTest.getEvaluatedConditions())
+ .containsOnly(new EvaluatedCondition(CONDITION_1, randomEvaluationStatus, randomValue));
+ }
+
+ @Test
+ public void verify_getters_when_no_condition() {
+ EvaluatedQualityGate underTest = builder
+ .setQualityGate(NO_CONDITION_QUALITY_GATE)
+ .setStatus(randomStatus)
+ .build();
+
+ assertThat(underTest.getQualityGate()).isEqualTo(NO_CONDITION_QUALITY_GATE);
+ assertThat(underTest.getStatus()).isEqualTo(randomStatus);
+ assertThat(underTest.getEvaluatedConditions()).isEmpty();
+ }
+
+ @Test
+ public void verify_getters_when_multiple_conditions() {
+ QualityGate qualityGate = new QualityGate(QUALITY_GATE_ID, QUALITY_GATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2));
+ EvaluatedQualityGate underTest = builder
+ .setQualityGate(qualityGate)
+ .setStatus(randomStatus)
+ .addCondition(CONDITION_1, randomEvaluationStatus, randomValue)
+ .addCondition(CONDITION_2, EvaluatedCondition.EvaluationStatus.WARN, "bad")
+ .build();
+
+ assertThat(underTest.getQualityGate()).isEqualTo(qualityGate);
+ assertThat(underTest.getStatus()).isEqualTo(randomStatus);
+ assertThat(underTest.getEvaluatedConditions()).containsOnly(
+ new EvaluatedCondition(CONDITION_1, randomEvaluationStatus, randomValue),
+ new EvaluatedCondition(CONDITION_2, EvaluatedCondition.EvaluationStatus.WARN, "bad"));
+ }
+
+ @Test
+ public void equals_is_based_on_all_fields() {
+ EvaluatedQualityGate.Builder builder = this.builder
+ .setQualityGate(ONE_CONDITION_QUALITY_GATE)
+ .setStatus(Level.WARN)
+ .addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.WARN, "foo");
+
+ EvaluatedQualityGate underTest = builder.build();
+ assertThat(underTest).isEqualTo(builder.build());
+ assertThat(underTest).isNotSameAs(builder.build());
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Object());
+ assertThat(underTest).isNotEqualTo(builder.setQualityGate(new QualityGate("other_id", QUALITY_GATE_NAME, singleton(CONDITION_1))).build());
+ assertThat(underTest).isNotEqualTo(builder.setQualityGate(ONE_CONDITION_QUALITY_GATE).setStatus(Level.OK).build());
+ assertThat(underTest).isNotEqualTo(newBuilder()
+ .setQualityGate(ONE_CONDITION_QUALITY_GATE)
+ .setStatus(Level.WARN)
+ .addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.OK, "foo")
+ .build());
+ }
+
+ @Test
+ public void hashcode_is_based_on_all_fields() {
+ EvaluatedQualityGate.Builder builder = this.builder
+ .setQualityGate(ONE_CONDITION_QUALITY_GATE)
+ .setStatus(Level.WARN)
+ .addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.WARN, "foo");
+
+ EvaluatedQualityGate underTest = builder.build();
+ assertThat(underTest.hashCode()).isEqualTo(builder.build().hashCode());
+ assertThat(underTest.hashCode()).isNotSameAs(builder.build().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(null);
+ assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(builder.setQualityGate(new QualityGate("other_id", QUALITY_GATE_NAME, singleton(CONDITION_1))).build().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(builder.setQualityGate(ONE_CONDITION_QUALITY_GATE).setStatus(Level.OK).build().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(newBuilder()
+ .setQualityGate(ONE_CONDITION_QUALITY_GATE)
+ .setStatus(Level.WARN)
+ .addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.OK, "foo")
+ .build().hashCode());
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/QualityGateTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/QualityGateTest.java
new file mode 100644
index 00000000000..d1fe1dde3ca
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/QualityGateTest.java
@@ -0,0 +1,135 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualitygate;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static java.util.Collections.emptySet;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class QualityGateTest {
+ private static final String QUALIGATE_ID = "qg_id";
+ private static final String QUALIGATE_NAME = "qg_name";
+ private static final Condition CONDITION_1 = new Condition("m1", Condition.Operator.EQUALS, "1", "2", false);
+ private static final Condition CONDITION_2 = new Condition("m2", Condition.Operator.LESS_THAN, "2", "4", true);
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private QualityGate underTest = new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2));
+
+ @Test
+ public void constructor_fails_with_NPE_if_id_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("id can't be null");
+
+ new QualityGate(null, "name", emptySet());
+ }
+
+ @Test
+ public void constructor_fails_with_NPE_if_name_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("name can't be null");
+
+ new QualityGate("id", null, emptySet());
+ }
+
+ @Test
+ public void constructor_fails_with_NPE_if_conditions_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("conditions can't be null");
+
+ new QualityGate("id", "name", null);
+ }
+
+ @Test
+ public void constructor_fails_with_NPE_if_conditions_contains_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("condition can't be null");
+ Random random = new Random();
+ Set<Condition> conditions = Stream.of(
+ IntStream.range(0, random.nextInt(5))
+ .mapToObj(i -> new Condition("m_before_" + i, Condition.Operator.EQUALS, null, null, false)),
+ Stream.of((Condition) null),
+ IntStream.range(0, random.nextInt(5))
+ .mapToObj(i -> new Condition("m_after_" + i, Condition.Operator.EQUALS, null, null, false)))
+ .flatMap(s -> s)
+ .collect(Collectors.toSet());
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("condition can't be null");
+
+ new QualityGate("id", "name", conditions);
+ }
+
+ @Test
+ public void verify_getters() {
+ assertThat(underTest.getId()).isEqualTo(QUALIGATE_ID);
+ assertThat(underTest.getName()).isEqualTo(QUALIGATE_NAME);
+ assertThat(underTest.getConditions()).containsOnly(CONDITION_1, CONDITION_2);
+ }
+
+ @Test
+ public void toString_is_override() {
+ QualityGate underTest = new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2));
+
+ assertThat(underTest.toString()).isEqualTo("QualityGate{id=qg_id, name='qg_name', conditions=[" +
+ "Condition{metricKey='m2', operator=LESS_THAN, warningThreshold='4', errorThreshold='2', onLeakPeriod=true}" +
+ "]}");
+ }
+
+ @Test
+ public void equals_is_based_on_all_fields() {
+ assertThat(underTest).isEqualTo(underTest);
+ assertThat(underTest).isEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2, CONDITION_1)));
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Object());
+ assertThat(underTest).isNotEqualTo(new QualityGate("other_id", QUALIGATE_NAME, ImmutableSet.of(CONDITION_2, CONDITION_1)));
+ assertThat(underTest).isNotEqualTo(new QualityGate(QUALIGATE_ID, "other_name", ImmutableSet.of(CONDITION_2, CONDITION_1)));
+ assertThat(underTest).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, emptySet()));
+ assertThat(underTest).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1)));
+ assertThat(underTest).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2)));
+ assertThat(underTest).isNotEqualTo(
+ new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2, new Condition("new", Condition.Operator.GREATER_THAN, "a", "b", false))));
+ }
+
+ @Test
+ public void hashcode_is_based_on_all_fields() {
+ assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+ assertThat(underTest.hashCode()).isEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2, CONDITION_1)).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(null);
+ assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate("other_id", QUALIGATE_NAME, ImmutableSet.of(CONDITION_2, CONDITION_1)).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate(QUALIGATE_ID, "other_name", ImmutableSet.of(CONDITION_2, CONDITION_1)).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, emptySet()).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1)).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2)).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(
+ new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2, new Condition("new", Condition.Operator.GREATER_THAN, "a", "b", false))).hashCode());
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/util/OkHttpClientProviderTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/util/OkHttpClientProviderTest.java
new file mode 100644
index 00000000000..b6f2f037a98
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/util/OkHttpClientProviderTest.java
@@ -0,0 +1,88 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.util;
+
+import java.io.IOException;
+import java.util.Base64;
+import okhttp3.OkHttpClient;
+import okhttp3.Protocol;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.SonarQubeSide;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.internal.SonarRuntimeImpl;
+import org.sonar.api.utils.Version;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OkHttpClientProviderTest {
+
+ private MapSettings settings = new MapSettings();
+ private SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("6.2"), SonarQubeSide.SERVER);
+ private final OkHttpClientProvider underTest = new OkHttpClientProvider();
+
+ @Rule
+ public MockWebServer server = new MockWebServer();
+
+ @Test
+ public void get_returns_a_OkHttpClient_with_default_configuration() throws Exception {
+ OkHttpClient client = underTest.provide(settings.asConfig(), runtime);
+
+ assertThat(client.connectTimeoutMillis()).isEqualTo(10_000);
+ assertThat(client.readTimeoutMillis()).isEqualTo(10_000);
+ assertThat(client.proxy()).isNull();
+
+ RecordedRequest recordedRequest = call(client);
+ assertThat(recordedRequest.getHeader("User-Agent")).isEqualTo("SonarQube/6.2");
+ assertThat(recordedRequest.getHeader("Proxy-Authorization")).isNull();
+ }
+
+ @Test
+ public void get_returns_a_OkHttpClient_with_proxy_authentication() throws Exception {
+ settings.setProperty("http.proxyUser", "the-login");
+ settings.setProperty("http.proxyPassword", "the-password");
+
+ OkHttpClient client = underTest.provide(settings.asConfig(), runtime);
+ Response response = new Response.Builder().protocol(Protocol.HTTP_1_1).request(new Request.Builder().url("http://foo").build()).code(407).build();
+ Request request = client.proxyAuthenticator().authenticate(null, response);
+
+ assertThat(request.header("Proxy-Authorization")).isEqualTo("Basic " + Base64.getEncoder().encodeToString("the-login:the-password".getBytes()));
+ }
+
+ @Test
+ public void get_returns_a_singleton() {
+ OkHttpClient client1 = underTest.provide(settings.asConfig(), runtime);
+ OkHttpClient client2 = underTest.provide(settings.asConfig(), runtime);
+ assertThat(client2).isNotNull().isSameAs(client1);
+ }
+
+ private RecordedRequest call(OkHttpClient client) throws IOException, InterruptedException {
+ server.enqueue(new MockResponse().setBody("pong"));
+ client.newCall(new Request.Builder().url(server.url("/ping")).build()).execute();
+
+ return server.takeRequest();
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/AnalysisTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/AnalysisTest.java
new file mode 100644
index 00000000000..5f307d904d4
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/AnalysisTest.java
@@ -0,0 +1,69 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import java.util.Date;
+import java.util.Random;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AnalysisTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void constructor_throws_NPE_when_uuid_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("uuid must not be null");
+
+ new Analysis(null, 1_990L);
+ }
+
+ @Test
+ public void test_equality() {
+ String uuid = randomAlphanumeric(35);
+ long date = new Random().nextLong();
+ Analysis underTest = new Analysis(uuid, date);
+
+ assertThat(underTest).isEqualTo(underTest);
+ assertThat(underTest.getUuid()).isEqualTo(uuid);
+ assertThat(underTest.getDate()).isEqualTo(new Date(date));
+
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Analysis(uuid + "1", date));
+ assertThat(underTest).isNotEqualTo(new Analysis(uuid, date + 1_000L));
+ }
+
+ @Test
+ public void test_hashcode() {
+ String uuid = randomAlphanumeric(35);
+ long date = new Random().nextLong();
+ Analysis underTest = new Analysis(uuid, date);
+
+ assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Analysis(uuid + "1", date).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Analysis(uuid, date + 1_000).hashCode());
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/AsynchronousWebHooksImplTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/AsynchronousWebHooksImplTest.java
new file mode 100644
index 00000000000..b7ca6d75fde
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/AsynchronousWebHooksImplTest.java
@@ -0,0 +1,106 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.organization.OrganizationDbTester;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.webhook.WebhookDbTester;
+import org.sonar.server.async.AsyncExecution;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+
+import static java.util.Objects.requireNonNull;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.sonar.db.DbTester.create;
+import static org.sonar.db.webhook.WebhookTesting.newWebhook;
+import static org.sonar.server.organization.TestDefaultOrganizationProvider.from;
+
+public class AsynchronousWebHooksImplTest {
+
+ private System2 system2 = mock(System2.class);
+
+ @Rule
+ public DbTester db = create(system2);
+ private WebhookDbTester webhookDbTester = db.webhooks();
+ private ComponentDbTester componentDbTester = db.components();
+ private OrganizationDbTester organizationDbTester = db.organizations();
+ private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
+
+ private static final long NOW = 1_500_000_000_000L;
+
+
+ private final TestWebhookCaller caller = new TestWebhookCaller();
+ private final WebhookDeliveryStorage deliveryStorage = mock(WebhookDeliveryStorage.class);
+ private final WebhookPayload mock = mock(WebhookPayload.class);
+ private final RecordingAsyncExecution asyncExecution = new RecordingAsyncExecution();
+
+ private final WebHooksImpl underTest = new WebHooksImpl(caller, deliveryStorage, asyncExecution, db.getDbClient());
+
+ @Test
+ public void send_global_webhooks() {
+
+ OrganizationDto organizationDto = db.getDefaultOrganization() ;
+ ComponentDto project = componentDbTester.insertPrivateProject(componentDto -> componentDto.setOrganizationUuid(organizationDto.getUuid()));
+ webhookDbTester.insert(newWebhook(organizationDto).setName("First").setUrl("http://url1"));
+ webhookDbTester.insert(newWebhook(organizationDto).setName("Second").setUrl("http://url2"));
+
+ caller.enqueueSuccess(NOW, 200, 1_234);
+ caller.enqueueFailure(NOW, new IOException("Fail to connect"));
+
+ underTest.sendProjectAnalysisUpdate(new WebHooks.Analysis(project.uuid(), "1", "#1"), () -> mock);
+
+ assertThat(caller.countSent()).isZero();
+ verifyZeroInteractions(deliveryStorage);
+
+ asyncExecution.executeRecorded();
+
+ assertThat(caller.countSent()).isEqualTo(2);
+ verify(deliveryStorage, times(2)).persist(any(WebhookDelivery.class));
+ verify(deliveryStorage).purge(project.uuid());
+ }
+
+ private static class RecordingAsyncExecution implements AsyncExecution {
+ private final List<Runnable> runnableList = new ArrayList<>();
+
+ @Override
+ public void addToQueue(Runnable r) {
+ runnableList.add(requireNonNull(r));
+ }
+
+ public void executeRecorded() {
+ ArrayList<Runnable> runnables = new ArrayList<>(runnableList);
+ runnableList.clear();
+ runnables.forEach(Runnable::run);
+ }
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/BranchTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/BranchTest.java
new file mode 100644
index 00000000000..b935d89b248
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/BranchTest.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import java.util.Random;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BranchTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private Branch underTest = new Branch(true, "b", Branch.Type.SHORT);
+
+ @Test
+ public void constructor_throws_NPE_if_type_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("type can't be null");
+
+ new Branch(new Random().nextBoolean(), "s", null);
+ }
+
+ @Test
+ public void verify_getters() {
+ assertThat(underTest.isMain()).isTrue();
+ assertThat(underTest.getName()).contains("b");
+ assertThat(underTest.getType()).isEqualTo(Branch.Type.SHORT);
+
+
+ Branch underTestWithNull = new Branch(false, null, Branch.Type.LONG);
+ assertThat(underTestWithNull.isMain()).isFalse();
+ assertThat(underTestWithNull.getName()).isEmpty();
+ assertThat(underTestWithNull.getType()).isEqualTo(Branch.Type.LONG);
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/CeTaskTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/CeTaskTest.java
new file mode 100644
index 00000000000..0ed58b7f00a
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/CeTaskTest.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CeTaskTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private CeTask underTest = new CeTask("A", CeTask.Status.SUCCESS);
+
+ @Test
+ public void constructor_throws_NPE_if_id_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("id can't be null");
+
+ new CeTask(null, CeTask.Status.SUCCESS);
+ }
+
+ @Test
+ public void constructor_throws_NPE_if_status_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("status can't be null");
+
+ new CeTask("B", null);
+ }
+
+ @Test
+ public void verify_getters() {
+ assertThat(underTest.getId()).isEqualTo("A");
+ assertThat(underTest.getStatus()).isEqualTo(CeTask.Status.SUCCESS);
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/ProjectAnalysisTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/ProjectAnalysisTest.java
new file mode 100644
index 00000000000..0a453709b67
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/ProjectAnalysisTest.java
@@ -0,0 +1,154 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.measures.Metric;
+import org.sonar.server.project.Project;
+import org.sonar.server.qualitygate.EvaluatedQualityGate;
+import org.sonar.server.qualitygate.QualityGate;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectAnalysisTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private final CeTask ceTask = new CeTask("id", CeTask.Status.SUCCESS);
+ private final Project project = new Project("uuid", "key", "name");
+ private final Analysis analysis = new Analysis("analysis_uuid", 1_500L);
+ private final Branch branch = new Branch(true, "name", Branch.Type.SHORT);
+ private final EvaluatedQualityGate qualityGate = EvaluatedQualityGate.newBuilder()
+ .setQualityGate(new QualityGate("id", "name", emptySet()))
+ .setStatus(Metric.Level.WARN)
+ .build();
+ private final Map<String, String> properties = ImmutableMap.of("a", "b");
+ private ProjectAnalysis underTest = new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, properties);
+
+ @Test
+ public void constructor_throws_NPE_if_project_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("project can't be null");
+
+ new ProjectAnalysis(null,
+ ceTask,
+ analysis,
+ branch,
+ qualityGate,
+ 1L,
+ emptyMap());
+ }
+
+ @Test
+ public void constructor_throws_NPE_if_properties_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("properties can't be null");
+
+ new ProjectAnalysis(project,
+ ceTask,
+ analysis,
+ branch,
+ qualityGate,
+ 1L,
+ null);
+ }
+
+ @Test
+ public void verify_getters() {
+ assertThat(underTest.getCeTask().get()).isSameAs(ceTask);
+ assertThat(underTest.getProject()).isSameAs(project);
+ assertThat(underTest.getBranch().get()).isSameAs(branch);
+ assertThat(underTest.getQualityGate().get()).isSameAs(qualityGate);
+ assertThat(underTest.getProperties()).isEqualTo(properties);
+ assertThat(underTest.getAnalysis().get()).isEqualTo(analysis);
+
+ ProjectAnalysis underTestWithNulls = new ProjectAnalysis(project, null, null, null, null, null, emptyMap());
+ assertThat(underTestWithNulls.getCeTask()).isEmpty();
+ assertThat(underTestWithNulls.getBranch()).isEmpty();
+ assertThat(underTestWithNulls.getQualityGate()).isEmpty();
+ assertThat(underTestWithNulls.getProperties()).isEmpty();
+ assertThat(underTestWithNulls.getAnalysis()).isEmpty();
+ }
+
+ @Test
+ public void defines_equals_based_on_all_fields() {
+ assertThat(underTest).isEqualTo(underTest);
+ assertThat(underTest).isEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Object());
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, new CeTask("2", CeTask.Status.SUCCESS), analysis, branch, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(new Project("A", "B", "C"), ceTask, analysis, branch, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(new Project("A", "B", "C"), ceTask, analysis, branch, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, null, null, null, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, null, null, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, new Analysis("foo", 1_500L), null, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, null, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, new Branch(false, "B", Branch.Type.SHORT), qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, null, 1L, properties));
+ EvaluatedQualityGate otherQualityGate = EvaluatedQualityGate.newBuilder()
+ .setQualityGate(new QualityGate("A", "B", emptySet()))
+ .setStatus(Metric.Level.WARN)
+ .build();
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, otherQualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, null, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 2L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, emptyMap()));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, ImmutableMap.of("A", "B")));
+ }
+
+ @Test
+ public void defines_hashcode_based_on_all_fields() {
+ assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+ assertThat(underTest.hashCode()).isEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, new CeTask("2", CeTask.Status.SUCCESS), analysis, branch, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(new Project("A", "B", "C"), ceTask, analysis, branch, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(new Project("A", "B", "C"), ceTask, analysis, branch, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, null, null, null, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, null, null, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, new Analysis("foo", 1_500L), null, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, null, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode())
+ .isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, new Branch(false, "B", Branch.Type.SHORT), qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, null, 1L, properties).hashCode());
+ EvaluatedQualityGate otherQualityGate = EvaluatedQualityGate.newBuilder()
+ .setQualityGate(new QualityGate("A", "B", emptySet()))
+ .setStatus(Metric.Level.WARN)
+ .build();
+ assertThat(underTest.hashCode())
+ .isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, otherQualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, null, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, 2L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, 1L, emptyMap()).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, 1L, ImmutableMap.of("B", "C")).hashCode());
+ }
+
+ @Test
+ public void verify_toString() {
+ assertThat(underTest.toString()).isEqualTo(
+ "ProjectAnalysis{project=Project{uuid='uuid', key='key', name='name', description=null}, ceTask=CeTask{id='id', status=SUCCESS}, branch=Branch{main=true, name='name', type=SHORT}, qualityGate=EvaluatedQualityGate{qualityGate=QualityGate{id=id, name='name', conditions=[]}, status=WARN, evaluatedConditions=[]}, updatedAt=1, properties={a=b}, analysis=Analysis{uuid='analysis_uuid', date=1500}}");
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/SynchronousWebHooksImplTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/SynchronousWebHooksImplTest.java
new file mode 100644
index 00000000000..de8d298052f
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/SynchronousWebHooksImplTest.java
@@ -0,0 +1,138 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.webhook.WebhookDbTester;
+import org.sonar.server.async.AsyncExecution;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.sonar.api.utils.log.LoggerLevel.DEBUG;
+import static org.sonar.db.DbTester.create;
+import static org.sonar.db.webhook.WebhookTesting.newWebhook;
+import static org.sonar.server.organization.TestDefaultOrganizationProvider.from;
+
+public class SynchronousWebHooksImplTest {
+
+ private static final long NOW = 1_500_000_000_000L;
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ @Rule
+ public DbTester db = create();
+ private DbClient dbClient = db.getDbClient();
+
+ private WebhookDbTester webhookDbTester = db.webhooks();
+ private ComponentDbTester componentDbTester = db.components();
+ private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
+
+ private final TestWebhookCaller caller = new TestWebhookCaller();
+ private final WebhookDeliveryStorage deliveryStorage = mock(WebhookDeliveryStorage.class);
+ private final WebhookPayload mock = mock(WebhookPayload.class);
+ private final AsyncExecution synchronousAsyncExecution = Runnable::run;
+ private final WebHooksImpl underTest = new WebHooksImpl(caller, deliveryStorage, synchronousAsyncExecution, dbClient);
+
+ @Test
+ public void isEnabled_returns_false_if_no_webhooks() {
+ ComponentDto componentDto = componentDbTester.insertPrivateProject();
+
+ assertThat(underTest.isEnabled(componentDto)).isFalse();
+ }
+
+ @Test
+ public void isEnabled_returns_true_if_one_valid_global_webhook() {
+ ComponentDto componentDto = componentDbTester.insertPrivateProject();
+ webhookDbTester.insert(newWebhook(componentDto).setName("First").setUrl("http://url1"));
+
+ assertThat(underTest.isEnabled(componentDto)).isTrue();
+ }
+
+ @Test
+ public void isEnabled_returns_true_if_one_valid_project_webhook() {
+ String organizationUuid = defaultOrganizationProvider.get().getUuid();
+ ComponentDto componentDto = componentDbTester.insertPrivateProject().setOrganizationUuid(organizationUuid);
+ webhookDbTester.insert(newWebhook(componentDto).setName("First").setUrl("http://url1"));
+
+ assertThat(underTest.isEnabled(componentDto)).isTrue();
+ }
+
+
+ @Test
+ public void do_nothing_if_no_webhooks() {
+ ComponentDto componentDto = componentDbTester.insertPrivateProject().setOrganizationUuid(defaultOrganizationProvider.get().getUuid());
+
+ underTest.sendProjectAnalysisUpdate(new WebHooks.Analysis(componentDto.uuid(), "1", "#1"), () -> mock);
+
+ assertThat(caller.countSent()).isEqualTo(0);
+ assertThat(logTester.logs(DEBUG)).isEmpty();
+ verifyZeroInteractions(deliveryStorage);
+ }
+
+ @Test
+ public void send_global_webhooks() {
+
+ ComponentDto componentDto = componentDbTester.insertPrivateProject();
+ webhookDbTester.insert(newWebhook(componentDto).setName("First").setUrl("http://url1"));
+ webhookDbTester.insert(newWebhook(componentDto).setName("Second").setUrl("http://url2"));
+ caller.enqueueSuccess(NOW, 200, 1_234);
+ caller.enqueueFailure(NOW, new IOException("Fail to connect"));
+
+ underTest.sendProjectAnalysisUpdate(new WebHooks.Analysis(componentDto.uuid(), "1", "#1"), () -> mock);
+
+ assertThat(caller.countSent()).isEqualTo(2);
+ assertThat(logTester.logs(DEBUG)).contains("Sent webhook 'First' | url=http://url1 | time=1234ms | status=200");
+ assertThat(logTester.logs(DEBUG)).contains("Failed to send webhook 'Second' | url=http://url2 | message=Fail to connect");
+ verify(deliveryStorage, times(2)).persist(any(WebhookDelivery.class));
+ verify(deliveryStorage).purge(componentDto.uuid());
+
+ }
+
+ @Test
+ public void send_project_webhooks() {
+
+ String organizationUuid = defaultOrganizationProvider.get().getUuid();
+ ComponentDto componentDto = componentDbTester.insertPrivateProject().setOrganizationUuid(organizationUuid);
+ webhookDbTester.insert(newWebhook(componentDto).setName("First").setUrl("http://url1"));
+ caller.enqueueSuccess(NOW, 200, 1_234);
+
+ underTest.sendProjectAnalysisUpdate(new WebHooks.Analysis(componentDto.uuid(), "1", "#1"), () -> mock);
+
+ assertThat(caller.countSent()).isEqualTo(1);
+ assertThat(logTester.logs(DEBUG)).contains("Sent webhook 'First' | url=http://url1 | time=1234ms | status=200");
+ verify(deliveryStorage).persist(any(WebhookDelivery.class));
+ verify(deliveryStorage).purge(componentDto.uuid());
+
+ }
+
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/TestWebhookCaller.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/TestWebhookCaller.java
new file mode 100644
index 00000000000..0634f160622
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/TestWebhookCaller.java
@@ -0,0 +1,75 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.annotation.Nullable;
+
+import static java.util.Objects.requireNonNull;
+
+public class TestWebhookCaller implements WebhookCaller {
+
+ private final Queue<Item> deliveries = new LinkedList<>();
+ private final AtomicInteger countSent = new AtomicInteger(0);
+
+ public TestWebhookCaller enqueueSuccess(long at, int httpCode, int durationMs) {
+ deliveries.add(new Item(at, httpCode, durationMs, null));
+ return this;
+ }
+
+ public TestWebhookCaller enqueueFailure(long at, Throwable t) {
+ deliveries.add(new Item(at, null, null, t));
+ return this;
+ }
+
+ @Override
+ public WebhookDelivery call(Webhook webhook, WebhookPayload payload) {
+ Item item = requireNonNull(deliveries.poll(), "Queue is empty");
+ countSent.incrementAndGet();
+ return new WebhookDelivery.Builder()
+ .setAt(item.at)
+ .setHttpStatus(item.httpCode)
+ .setDurationInMs(item.durationMs)
+ .setError(item.throwable)
+ .setPayload(payload)
+ .setWebhook(webhook)
+ .build();
+ }
+
+ public int countSent() {
+ return countSent.get();
+ }
+
+ private static class Item {
+ final long at;
+ final Integer httpCode;
+ final Integer durationMs;
+ final Throwable throwable;
+
+ Item(long at, @Nullable Integer httpCode, @Nullable Integer durationMs, @Nullable Throwable throwable) {
+ this.at = at;
+ this.httpCode = httpCode;
+ this.durationMs = durationMs;
+ this.throwable = throwable;
+ }
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java
new file mode 100644
index 00000000000..60ac24ee262
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java
@@ -0,0 +1,218 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import okhttp3.Credentials;
+import okhttp3.HttpUrl;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.DisableOnDebug;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+import org.sonar.api.SonarQubeSide;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.internal.SonarRuntimeImpl;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.Version;
+import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.server.util.OkHttpClientProvider;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class WebhookCallerImplTest {
+
+ private static final long NOW = 1_500_000_000_000L;
+ private static final String PROJECT_UUID = "P_UUID1";
+ private static final String WEBHOOK_UUID = "WH_UUID1";
+ private static final String CE_TASK_UUID = "CE_UUID1";
+ private static final String SOME_JSON = "{\"payload\": {}}";
+ private static final WebhookPayload PAYLOAD = new WebhookPayload("P1", SOME_JSON);
+
+ @Rule
+ public MockWebServer server = new MockWebServer();
+
+ @Rule
+ public TestRule safeguardTimeout = new DisableOnDebug(Timeout.seconds(60));
+
+ private System2 system = new TestSystem2().setNow(NOW);
+
+ @Test
+ public void send_posts_payload_to_http_server() throws Exception {
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", server.url("/ping").toString());
+
+ server.enqueue(new MockResponse().setBody("pong").setResponseCode(201));
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ assertThat(delivery.getHttpStatus()).hasValue(201);
+ assertThat(delivery.getWebhook().getUuid()).isEqualTo(WEBHOOK_UUID);
+ assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
+ assertThat(delivery.getError()).isEmpty();
+ assertThat(delivery.getAt()).isEqualTo(NOW);
+ assertThat(delivery.getWebhook()).isSameAs(webhook);
+ assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
+
+ RecordedRequest recordedRequest = server.takeRequest();
+ assertThat(recordedRequest.getMethod()).isEqualTo("POST");
+ assertThat(recordedRequest.getPath()).isEqualTo("/ping");
+ assertThat(recordedRequest.getBody().readUtf8()).isEqualTo(PAYLOAD.getJson());
+ assertThat(recordedRequest.getHeader("User-Agent")).isEqualTo("SonarQube/6.2");
+ assertThat(recordedRequest.getHeader("Content-Type")).isEqualTo("application/json; charset=utf-8");
+ assertThat(recordedRequest.getHeader("X-SonarQube-Project")).isEqualTo(PAYLOAD.getProjectKey());
+ }
+
+ @Test
+ public void silently_catch_error_when_external_server_does_not_answer() throws Exception {
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", server.url("/ping").toString());
+
+ server.shutdown();
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ assertThat(delivery.getHttpStatus()).isEmpty();
+ assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
+ // message can be "Connection refused" or "connect timed out"
+ assertThat(delivery.getErrorMessage().get()).matches("(.*Connection refused.*)|(.*connect timed out.*)");
+ assertThat(delivery.getAt()).isEqualTo(NOW);
+ assertThat(delivery.getWebhook()).isSameAs(webhook);
+ assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
+ }
+
+ @Test
+ public void silently_catch_error_when_url_is_incorrect() {
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", "this_is_not_an_url");
+
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ assertThat(delivery.getHttpStatus()).isEmpty();
+ assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
+ assertThat(delivery.getError().get()).isInstanceOf(IllegalArgumentException.class);
+ assertThat(delivery.getErrorMessage().get()).isEqualTo("Webhook URL is not valid: this_is_not_an_url");
+ assertThat(delivery.getAt()).isEqualTo(NOW);
+ assertThat(delivery.getWebhook()).isSameAs(webhook);
+ assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
+ }
+
+ /**
+ * SONAR-8799
+ */
+ @Test
+ public void redirects_should_be_followed_with_POST_method() throws Exception {
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", server.url("/redirect").toString());
+
+ // /redirect redirects to /target
+ server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", server.url("target")));
+ server.enqueue(new MockResponse().setResponseCode(200));
+
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ assertThat(delivery.getHttpStatus().get()).isEqualTo(200);
+ assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
+ assertThat(delivery.getError()).isEmpty();
+ assertThat(delivery.getAt()).isEqualTo(NOW);
+ assertThat(delivery.getWebhook()).isSameAs(webhook);
+ assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
+
+ takeAndVerifyPostRequest("/redirect");
+ takeAndVerifyPostRequest("/target");
+ }
+
+ @Test
+ public void credentials_are_propagated_to_POST_redirects() throws Exception {
+ HttpUrl url = server.url("/redirect").newBuilder().username("theLogin").password("thePassword").build();
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString());
+
+ // /redirect redirects to /target
+ server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", server.url("target")));
+ server.enqueue(new MockResponse().setResponseCode(200));
+
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ assertThat(delivery.getHttpStatus().get()).isEqualTo(200);
+
+ RecordedRequest redirectedRequest = takeAndVerifyPostRequest("/redirect");
+ assertThat(redirectedRequest.getHeader("Authorization")).isEqualTo(Credentials.basic(url.username(), url.password()));
+
+ RecordedRequest targetRequest = takeAndVerifyPostRequest("/target");
+ assertThat(targetRequest.getHeader("Authorization")).isEqualTo(Credentials.basic(url.username(), url.password()));
+ }
+
+ @Test
+ public void redirects_throws_ISE_if_header_Location_is_missing() {
+ HttpUrl url = server.url("/redirect");
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString());
+
+ server.enqueue(new MockResponse().setResponseCode(307));
+
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ Throwable error = delivery.getError().get();
+ assertThat(error)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Missing HTTP header 'Location' in redirect of " + url);
+ }
+
+ @Test
+ public void redirects_throws_ISE_if_header_Location_does_not_relate_to_a_supported_protocol() {
+ HttpUrl url = server.url("/redirect");
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString());
+
+ server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", "ftp://foo"));
+
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ Throwable error = delivery.getError().get();
+ assertThat(error)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Unsupported protocol in redirect of " + url + " to ftp://foo");
+ }
+
+ @Test
+ public void send_basic_authentication_header_if_url_contains_credentials() throws Exception {
+ HttpUrl url = server.url("/ping").newBuilder().username("theLogin").password("thePassword").build();
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString());
+ server.enqueue(new MockResponse().setBody("pong"));
+
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ assertThat(delivery.getWebhook().getUrl())
+ .isEqualTo(url.toString())
+ .contains("://theLogin:thePassword@");
+ RecordedRequest recordedRequest = takeAndVerifyPostRequest("/ping");
+ assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(Credentials.basic(url.username(), url.password()));
+ }
+
+ private RecordedRequest takeAndVerifyPostRequest(String expectedPath) throws Exception {
+ RecordedRequest request = server.takeRequest();
+
+ assertThat(request.getMethod()).isEqualTo("POST");
+ assertThat(request.getPath()).isEqualTo(expectedPath);
+ assertThat(request.getHeader("User-Agent")).isEqualTo("SonarQube/6.2");
+ return request;
+ }
+
+ private WebhookCaller newSender() {
+ SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("6.2"), SonarQubeSide.SERVER);
+ return new WebhookCallerImpl(system, new OkHttpClientProvider().provide(new MapSettings().asConfig(), runtime));
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java
new file mode 100644
index 00000000000..7273b947c86
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java
@@ -0,0 +1,119 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import java.io.IOException;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.webhook.WebhookDbTesting;
+import org.sonar.db.webhook.WebhookDeliveryDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.webhook.WebhookDbTesting.selectAllDeliveryUuids;
+
+public class WebhookDeliveryStorageTest {
+
+ private static final String DELIVERY_UUID = "abcde1234";
+ private static final long NOW = 1_500_000_000_000L;
+ private static final long TWO_MONTHS_AGO = NOW - 60L * 24 * 60 * 60 * 1000;
+ private static final long TWO_WEEKS_AGO = NOW - 14L * 24 * 60 * 60 * 1000;
+
+ private final System2 system = mock(System2.class);
+
+ @Rule
+ public final DbTester dbTester = DbTester.create(system).setDisableDefaultOrganization(true);
+
+ private DbClient dbClient = dbTester.getDbClient();
+ private DbSession dbSession = dbTester.getSession();
+ private UuidFactory uuidFactory = mock(UuidFactory.class);
+ private WebhookDeliveryStorage underTest = new WebhookDeliveryStorage(dbClient, system, uuidFactory);
+
+ @Test
+ public void persist_generates_uuid_then_inserts_record() {
+ when(uuidFactory.create()).thenReturn(DELIVERY_UUID);
+ WebhookDelivery delivery = newBuilderTemplate().build();
+
+ underTest.persist(delivery);
+
+ WebhookDeliveryDto dto = dbClient.webhookDeliveryDao().selectByUuid(dbSession, DELIVERY_UUID).get();
+ assertThat(dto.getUuid()).isEqualTo(DELIVERY_UUID);
+ assertThat(dto.getWebhookUuid()).isEqualTo("WEBHOOK_UUID_1");
+ assertThat(dto.getComponentUuid()).isEqualTo(delivery.getWebhook().getComponentUuid());
+ assertThat(dto.getCeTaskUuid()).isEqualTo(delivery.getWebhook().getCeTaskUuid().get());
+ assertThat(dto.getName()).isEqualTo(delivery.getWebhook().getName());
+ assertThat(dto.getUrl()).isEqualTo(delivery.getWebhook().getUrl());
+ assertThat(dto.getCreatedAt()).isEqualTo(delivery.getAt());
+ assertThat(dto.getHttpStatus()).isEqualTo(delivery.getHttpStatus().get());
+ assertThat(dto.getDurationMs()).isEqualTo(delivery.getDurationInMs().get());
+ assertThat(dto.getPayload()).isEqualTo(delivery.getPayload().getJson());
+ assertThat(dto.getErrorStacktrace()).isNull();
+ }
+
+ @Test
+ public void persist_error_stacktrace() {
+ when(uuidFactory.create()).thenReturn(DELIVERY_UUID);
+ WebhookDelivery delivery = newBuilderTemplate()
+ .setError(new IOException("fail to connect"))
+ .build();
+
+ underTest.persist(delivery);
+
+ WebhookDeliveryDto dto = dbClient.webhookDeliveryDao().selectByUuid(dbSession, DELIVERY_UUID).get();
+ assertThat(dto.getErrorStacktrace()).contains("java.io.IOException", "fail to connect");
+ }
+
+ @Test
+ public void purge_deletes_records_older_than_one_month_on_the_project() {
+ when(system.now()).thenReturn(NOW);
+ dbClient.webhookDeliveryDao().insert(dbSession, newDto("D1", "PROJECT_1", TWO_MONTHS_AGO));
+ dbClient.webhookDeliveryDao().insert(dbSession, newDto("D2", "PROJECT_1", TWO_WEEKS_AGO));
+ dbClient.webhookDeliveryDao().insert(dbSession, newDto("D3", "PROJECT_2", TWO_MONTHS_AGO));
+ dbSession.commit();
+
+ underTest.purge("PROJECT_1");
+
+ // do not purge another project PROJECT_2
+ assertThat(selectAllDeliveryUuids(dbTester, dbSession)).containsOnly("D2", "D3");
+ }
+
+ private static WebhookDelivery.Builder newBuilderTemplate() {
+ return new WebhookDelivery.Builder()
+ .setWebhook(new Webhook("WEBHOOK_UUID_1", "COMPONENT1", "TASK1", RandomStringUtils.randomAlphanumeric(40),"Jenkins", "http://jenkins"))
+ .setPayload(new WebhookPayload("my-project", "{json}"))
+ .setAt(1_000_000L)
+ .setHttpStatus(200)
+ .setDurationInMs(1_000);
+ }
+
+ private static WebhookDeliveryDto newDto(String uuid, String componentUuid, long at) {
+ return WebhookDbTesting.newDto()
+ .setUuid(uuid)
+ .setComponentUuid(componentUuid)
+ .setCreatedAt(at);
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookDeliveryTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookDeliveryTest.java
new file mode 100644
index 00000000000..1e627f560e3
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookDeliveryTest.java
@@ -0,0 +1,82 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import java.io.IOException;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+
+public class WebhookDeliveryTest {
+
+ @Test
+ public void isSuccess_returns_false_if_failed_to_send_http_request() {
+ WebhookDelivery delivery = newBuilderTemplate()
+ .setError(new IOException("Fail to connect"))
+ .build();
+
+ assertThat(delivery.isSuccess()).isFalse();
+ }
+
+ @Test
+ public void isSuccess_returns_false_if_http_response_returns_error_status() {
+ WebhookDelivery delivery = newBuilderTemplate()
+ .setHttpStatus(404)
+ .build();
+
+ assertThat(delivery.isSuccess()).isFalse();
+ }
+
+ @Test
+ public void isSuccess_returns_true_if_http_response_returns_2xx_code() {
+ WebhookDelivery delivery = newBuilderTemplate()
+ .setHttpStatus(204)
+ .build();
+
+ assertThat(delivery.isSuccess()).isTrue();
+ }
+
+ @Test
+ public void getErrorMessage_returns_empty_if_no_error() {
+ WebhookDelivery delivery = newBuilderTemplate().build();
+
+ assertThat(delivery.getErrorMessage()).isEmpty();
+ }
+
+ @Test
+ public void getErrorMessage_returns_root_cause_message_if_error() {
+ Exception rootCause = new IOException("fail to connect");
+ Exception cause = new IOException("nested", rootCause);
+ WebhookDelivery delivery = newBuilderTemplate()
+ .setError(cause)
+ .build();
+
+ assertThat(delivery.getErrorMessage().get()).isEqualTo("fail to connect");
+ }
+
+ private static WebhookDelivery.Builder newBuilderTemplate() {
+ return new WebhookDelivery.Builder()
+ .setWebhook(mock(Webhook.class))
+ .setPayload(mock(WebhookPayload.class))
+ .setAt(1_000L);
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookModuleTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookModuleTest.java
new file mode 100644
index 00000000000..58b17bf35ee
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookModuleTest.java
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.server.webhook.WebhookModule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.platform.ComponentContainer.COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER;
+
+public class WebhookModuleTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private WebhookModule underTest = new WebhookModule();
+
+ @Test
+ public void verify_count_of_added_components() {
+ ComponentContainer container = new ComponentContainer();
+
+ underTest.configure(container);
+
+ assertThat(container.size()).isEqualTo(4 + COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER);
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java
new file mode 100644
index 00000000000..a494b9a86e7
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java
@@ -0,0 +1,329 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.platform.Server;
+import org.sonar.api.utils.System2;
+import org.sonar.server.project.Project;
+import org.sonar.server.qualitygate.Condition;
+import org.sonar.server.qualitygate.EvaluatedCondition;
+import org.sonar.server.qualitygate.EvaluatedQualityGate;
+import org.sonar.server.qualitygate.QualityGate;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+import static java.util.Collections.singleton;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.test.JsonAssert.assertJson;
+
+public class WebhookPayloadFactoryImplTest {
+
+ private static final String PROJECT_KEY = "P1";
+
+ private Server server = mock(Server.class);
+ private System2 system2 = mock(System2.class);
+ private WebhookPayloadFactory underTest = new WebhookPayloadFactoryImpl(server, system2);
+
+ @Before
+ public void setUp() throws Exception {
+ when(server.getPublicRootUrl()).thenReturn("http://foo");
+ when(system2.now()).thenReturn(1_500_999L);
+ }
+
+ @Test
+ public void create_payload_for_successful_analysis() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ Condition condition = new Condition("coverage", Condition.Operator.GREATER_THAN, "70.0", "75.0", true);
+ EvaluatedQualityGate gate = EvaluatedQualityGate.newBuilder()
+ .setQualityGate(new QualityGate("G1", "Gate One", singleton(condition)))
+ .setStatus(Metric.Level.WARN)
+ .addCondition(condition, EvaluatedCondition.EvaluationStatus.WARN, "74.0")
+ .build();
+ ProjectAnalysis analysis = newAnalysis(task, gate, null, 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ " \"serverUrl\": \"http://foo\"," +
+ " \"taskId\": \"#1\"," +
+ " \"status\": \"SUCCESS\"," +
+ " \"analysedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"changedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"project\": {" +
+ " \"key\": \"P1\"," +
+ " \"name\": \"Project One\"," +
+ " \"url\": \"http://foo/dashboard?id=P1\"" +
+ " }," +
+ " \"qualityGate\": {" +
+ " \"name\": \"Gate One\"," +
+ " \"status\": \"WARN\"," +
+ " \"conditions\": [" +
+ " {" +
+ " \"metric\": \"coverage\"," +
+ " \"operator\": \"GREATER_THAN\"," +
+ " \"value\": \"74.0\"," +
+ " \"status\": \"WARN\"," +
+ " \"onLeakPeriod\": true," +
+ " \"errorThreshold\": \"70.0\"," +
+ " \"warningThreshold\": \"75.0\"" +
+ " }" +
+ " ]" +
+ " }," +
+ " \"properties\": {" +
+ " }" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_with_gate_conditions_without_value() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+
+ Condition condition = new Condition("coverage", Condition.Operator.GREATER_THAN, "70.0", "75.0", false);
+ EvaluatedQualityGate gate = EvaluatedQualityGate.newBuilder()
+ .setQualityGate(new QualityGate("G1", "Gate One", singleton(condition)))
+ .setStatus(Metric.Level.WARN)
+ .addCondition(condition, EvaluatedCondition.EvaluationStatus.NO_VALUE, null)
+ .build();
+ ProjectAnalysis analysis = newAnalysis(task, gate, null, 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ " \"serverUrl\": \"http://foo\"," +
+ " \"taskId\": \"#1\"," +
+ " \"status\": \"SUCCESS\"," +
+ " \"analysedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"changedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"project\": {" +
+ " \"key\": \"P1\"," +
+ " \"name\": \"Project One\"," +
+ " \"url\": \"http://foo/dashboard?id=P1\"" +
+ " }," +
+ " \"qualityGate\": {" +
+ " \"name\": \"Gate One\"," +
+ " \"status\": \"WARN\"," +
+ " \"conditions\": [" +
+ " {" +
+ " \"metric\": \"coverage\"," +
+ " \"operator\": \"GREATER_THAN\"," +
+ " \"status\": \"NO_VALUE\"," +
+ " \"onLeakPeriod\": false," +
+ " \"errorThreshold\": \"70.0\"," +
+ " \"warningThreshold\": \"75.0\"" +
+ " }" +
+ " ]" +
+ " }" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_with_analysis_properties() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ EvaluatedQualityGate gate = EvaluatedQualityGate.newBuilder()
+ .setQualityGate(new QualityGate("G1", "Gate One", emptySet()))
+ .setStatus(Metric.Level.WARN)
+ .build();
+ Map<String, String> scannerProperties = ImmutableMap.of(
+ "sonar.analysis.revision", "ab45d24",
+ "sonar.analysis.buildNumber", "B123",
+ "not.prefixed.with.sonar.analysis", "should be ignored",
+ "ignored", "should be ignored too");
+ ProjectAnalysis analysis = newAnalysis(task, gate, null, 1_500_000_000_000L, scannerProperties);
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ " \"serverUrl\": \"http://foo\"," +
+ " \"taskId\": \"#1\"," +
+ " \"status\": \"SUCCESS\"," +
+ " \"analysedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"changedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"project\": {" +
+ " \"key\": \"P1\"," +
+ " \"name\": \"Project One\"," +
+ " \"url\": \"http://foo/dashboard?id=P1\"" +
+ " }," +
+ " \"qualityGate\": {" +
+ " \"name\": \"Gate One\"," +
+ " \"status\": \"WARN\"," +
+ " \"conditions\": [" +
+ " ]" +
+ " }," +
+ " \"properties\": {" +
+ " \"sonar.analysis.revision\": \"ab45d24\"," +
+ " \"sonar.analysis.buildNumber\": \"B123\"" +
+ " }" +
+ "}");
+ assertThat(payload.getJson())
+ .doesNotContain("not.prefixed.with.sonar.analysis")
+ .doesNotContain("ignored");
+ }
+
+ @Test
+ public void create_payload_for_failed_analysis() {
+ CeTask ceTask = new CeTask("#1", CeTask.Status.FAILED);
+ ProjectAnalysis analysis = newAnalysis(ceTask, null, null, 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+
+ assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ " \"serverUrl\": \"http://foo\"," +
+ " \"taskId\": \"#1\"," +
+ " \"status\": \"FAILED\"," +
+ " \"changedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"project\": {" +
+ " \"key\": \"P1\"," +
+ " \"name\": \"Project One\"," +
+ " \"url\": \"http://foo/dashboard?id=P1\"" +
+ " }," +
+ " \"properties\": {" +
+ " }" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_for_no_analysis_date() {
+ CeTask ceTask = new CeTask("#1", CeTask.Status.FAILED);
+ ProjectAnalysis analysis = newAnalysis(ceTask, null, null, null, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+
+ assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ " \"serverUrl\": \"http://foo\"," +
+ " \"taskId\": \"#1\"," +
+ " \"status\": \"FAILED\"," +
+ " \"changedAt\": \"1970-01-01T01:25:00+0100\"," +
+ " \"project\": {" +
+ " \"key\": \"P1\"," +
+ " \"name\": \"Project One\"" +
+ " }," +
+ " \"properties\": {" +
+ " }" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_on_short_branch() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ ProjectAnalysis analysis = newAnalysis(task, null, new Branch(false, "feature/foo", Branch.Type.SHORT), 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ "\"branch\": {" +
+ " \"name\": \"feature/foo\"," +
+ " \"type\": \"SHORT\"," +
+ " \"isMain\": false," +
+ " \"url\": \"http://foo/project/issues?branch=feature%2Ffoo&id=P1&resolved=false\"" +
+ "}" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_on_pull_request() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ ProjectAnalysis analysis = newAnalysis(task, null, new Branch(false, "pr/foo", Branch.Type.PULL_REQUEST), 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ "\"branch\": {" +
+ " \"name\": \"pr/foo\"," +
+ " \"type\": \"PULL_REQUEST\"," +
+ " \"isMain\": false," +
+ " \"url\": \"http://foo/project/issues?pullRequest=pr%2Ffoo&id=P1&resolved=false\"" +
+ "}" +
+ "}");
+ }
+
+ @Test
+ public void create_without_ce_task() {
+ ProjectAnalysis analysis = newAnalysis(null, null, null, null, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ String json = payload.getJson();
+ assertThat(json).doesNotContain("taskId");
+ assertJson(json)
+ .isSimilarTo("{" +
+ " \"serverUrl\": \"http://foo\"," +
+ " \"status\": \"SUCCESS\"," +
+ " \"changedAt\": \"1970-01-01T01:25:00+0100\"," +
+ " \"project\": {" +
+ " \"key\": \"P1\"," +
+ " \"name\": \"Project One\"" +
+ " }," +
+ " \"properties\": {" +
+ " }" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_on_long_branch() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ ProjectAnalysis analysis = newAnalysis(task, null, new Branch(false, "feature/foo", Branch.Type.LONG), 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ "\"branch\": {" +
+ " \"name\": \"feature/foo\"" +
+ " \"type\": \"LONG\"" +
+ " \"isMain\": false," +
+ " \"url\": \"http://foo/dashboard?branch=feature%2Ffoo&id=P1\"" +
+ "}" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_on_main_branch_without_name() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ ProjectAnalysis analysis = newAnalysis(task, null, new Branch(true, null, Branch.Type.LONG), 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ "\"branch\": {" +
+ " \"type\": \"LONG\"" +
+ " \"isMain\": true," +
+ " \"url\": \"http://foo/dashboard?id=P1\"" +
+ "}" +
+ "}");
+ }
+
+ private static ProjectAnalysis newAnalysis(@Nullable CeTask task, @Nullable EvaluatedQualityGate gate,
+ @Nullable Branch branch, @Nullable Long analysisDate, Map<String, String> scannerProperties) {
+ return new ProjectAnalysis(new Project("P1_UUID", PROJECT_KEY, "Project One"), task, analysisDate == null ? null : new Analysis("A_UUID1", analysisDate), branch,
+ gate, analysisDate, scannerProperties);
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookTest.java
new file mode 100644
index 00000000000..64778c0dfa4
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookTest.java
@@ -0,0 +1,78 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook;
+
+import java.util.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class WebhookTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void constructor_with_null_componentUuid_should_throw_NPE() {
+ expectedException.expect(NullPointerException.class);
+
+ new Webhook(randomAlphanumeric(40), null, null, null, randomAlphanumeric(10), randomAlphanumeric(10));
+ }
+
+ @Test
+ public void constructor_with_null_name_should_throw_NPE() {
+ expectedException.expect(NullPointerException.class);
+
+ new Webhook(randomAlphanumeric(40), randomAlphanumeric(10), null, null, null, randomAlphanumeric(10));
+ }
+
+ @Test
+ public void constructor_with_null_url_should_throw_NPE() {
+ expectedException.expect(NullPointerException.class);
+
+ new Webhook(randomAlphanumeric(40), randomAlphanumeric(10), null, null, randomAlphanumeric(10), null);
+ }
+
+ @Test
+ public void constructor_with_null_ceTaskUuid_or_analysisUuidurl_should_return_Optional_empty() {
+ String componentUuid = randomAlphanumeric(10);
+ String name = randomAlphanumeric(10);
+ String url = randomAlphanumeric(10);
+ Webhook underTest = new Webhook(randomAlphanumeric(40), componentUuid, null, null, name, url);
+
+ assertThat(underTest.getComponentUuid()).isEqualTo(componentUuid);
+ assertThat(underTest.getName()).isEqualTo(name);
+ assertThat(underTest.getUrl()).isEqualTo(url);
+ assertThat(underTest.getCeTaskUuid()).isEqualTo(Optional.empty());
+ assertThat(underTest.getAnalysisUuid()).isEqualTo(Optional.empty());
+
+ String ceTaskUuid = randomAlphanumeric(10);
+ String analysisUuid = randomAlphanumeric(10);
+ underTest = new Webhook(randomAlphanumeric(40), componentUuid, ceTaskUuid, analysisUuid, name, url);
+ assertThat(underTest.getComponentUuid()).isEqualTo(componentUuid);
+ assertThat(underTest.getName()).isEqualTo(name);
+ assertThat(underTest.getUrl()).isEqualTo(url);
+ assertThat(underTest.getCeTaskUuid().get()).isEqualTo(ceTaskUuid);
+ assertThat(underTest.getAnalysisUuid().get()).isEqualTo(analysisUuid);
+ }
+}