]> source.dussan.org Git - sonarqube.git/blob
aff151adb4892075353f2e35be9351049a61bb5d
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2017 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.computation.task.projectanalysis.webhook;
21
22 import okhttp3.HttpUrl;
23 import okhttp3.mockwebserver.MockResponse;
24 import okhttp3.mockwebserver.MockWebServer;
25 import okhttp3.mockwebserver.RecordedRequest;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.junit.rules.DisableOnDebug;
29 import org.junit.rules.TestRule;
30 import org.junit.rules.Timeout;
31 import org.sonar.api.SonarQubeSide;
32 import org.sonar.api.SonarRuntime;
33 import org.sonar.api.config.internal.MapSettings;
34 import org.sonar.api.internal.SonarRuntimeImpl;
35 import org.sonar.api.utils.System2;
36 import org.sonar.api.utils.Version;
37 import org.sonar.api.utils.internal.TestSystem2;
38 import org.sonar.server.util.OkHttpClientProvider;
39
40 import static org.assertj.core.api.Assertions.assertThat;
41
42 public class WebhookCallerImplTest {
43
44   private static final long NOW = 1_500_000_000_000L;
45   private static final String PROJECT_UUID = "P_UUID1";
46   private static final String CE_TASK_UUID = "CE_UUID1";
47   private static final String SOME_JSON = "{\"payload\": {}}";
48   private static final WebhookPayload PAYLOAD = new WebhookPayload("P1", SOME_JSON);
49
50   @Rule
51   public MockWebServer server = new MockWebServer();
52
53   @Rule
54   public TestRule safeguardTimeout = new DisableOnDebug(Timeout.seconds(60));
55
56   private System2 system = new TestSystem2().setNow(NOW);
57
58   @Test
59   public void send_posts_payload_to_http_server() throws Exception {
60     Webhook webhook = new Webhook(PROJECT_UUID, CE_TASK_UUID, "my-webhook", server.url("/ping").toString());
61
62     server.enqueue(new MockResponse().setBody("pong").setResponseCode(201));
63     WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
64
65     assertThat(delivery.getHttpStatus()).hasValue(201);
66     assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
67     assertThat(delivery.getError()).isEmpty();
68     assertThat(delivery.getAt()).isEqualTo(NOW);
69     assertThat(delivery.getWebhook()).isSameAs(webhook);
70     assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
71
72     RecordedRequest recordedRequest = server.takeRequest();
73     assertThat(recordedRequest.getMethod()).isEqualTo("POST");
74     assertThat(recordedRequest.getPath()).isEqualTo("/ping");
75     assertThat(recordedRequest.getBody().readUtf8()).isEqualTo(PAYLOAD.getJson());
76     assertThat(recordedRequest.getHeader("User-Agent")).isEqualTo("SonarQube/6.2");
77     assertThat(recordedRequest.getHeader("Content-Type")).isEqualTo("application/json; charset=utf-8");
78     assertThat(recordedRequest.getHeader("X-SonarQube-Project")).isEqualTo(PAYLOAD.getProjectKey());
79   }
80
81   @Test
82   public void silently_catch_error_when_external_server_does_not_answer() throws Exception {
83     Webhook webhook = new Webhook(PROJECT_UUID, CE_TASK_UUID, "my-webhook", server.url("/ping").toString());
84
85     server.shutdown();
86     WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
87
88     assertThat(delivery.getHttpStatus()).isEmpty();
89     assertThat(delivery.getDurationInMs()).isEmpty();
90     // message can be "Connection refused" or "connect timed out"
91     assertThat(delivery.getErrorMessage().get()).matches("(.*Connection refused.*)|(.*connect timed out.*)");
92     assertThat(delivery.getAt()).isEqualTo(NOW);
93     assertThat(delivery.getWebhook()).isSameAs(webhook);
94     assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
95   }
96
97   @Test
98   public void silently_catch_error_when_url_is_incorrect() throws Exception {
99     Webhook webhook = new Webhook(PROJECT_UUID, CE_TASK_UUID, "my-webhook", "this_is_not_an_url");
100
101     WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
102
103     assertThat(delivery.getHttpStatus()).isEmpty();
104     assertThat(delivery.getDurationInMs()).isEmpty();
105     assertThat(delivery.getError().get()).isInstanceOf(IllegalArgumentException.class);
106     assertThat(delivery.getErrorMessage().get()).isEqualTo("unexpected url: this_is_not_an_url");
107     assertThat(delivery.getAt()).isEqualTo(NOW);
108     assertThat(delivery.getWebhook()).isSameAs(webhook);
109     assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
110   }
111
112   /**
113    * SONAR-8799
114    */
115   @Test
116   public void redirects_should_be_followed_with_POST_method() throws Exception {
117     Webhook webhook = new Webhook(PROJECT_UUID, CE_TASK_UUID, "my-webhook", server.url("/redirect").toString());
118
119     // /redirect redirects to /target
120     server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", server.url("target")));
121     server.enqueue(new MockResponse().setResponseCode(200));
122
123     WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
124
125     assertThat(delivery.getHttpStatus().get()).isEqualTo(200);
126     assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
127     assertThat(delivery.getError()).isEmpty();
128     assertThat(delivery.getAt()).isEqualTo(NOW);
129     assertThat(delivery.getWebhook()).isSameAs(webhook);
130     assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
131
132     takeAndVerifyPostRequest("/redirect");
133     takeAndVerifyPostRequest("/target");
134   }
135
136   @Test
137   public void redirects_throws_ISE_if_header_Location_is_missing() throws Exception {
138     HttpUrl url = server.url("/redirect");
139     Webhook webhook = new Webhook(PROJECT_UUID, CE_TASK_UUID, "my-webhook", url.toString());
140
141     server.enqueue(new MockResponse().setResponseCode(307));
142
143     WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
144
145     Throwable error = delivery.getError().get();
146     assertThat(error)
147       .isInstanceOf(IllegalStateException.class)
148       .hasMessage("Missing HTTP header 'Location' in redirect of " + url);
149   }
150
151   @Test
152   public void redirects_throws_ISE_if_header_Location_does_not_relate_to_a_supported_protocol() throws Exception {
153     HttpUrl url = server.url("/redirect");
154     Webhook webhook = new Webhook(PROJECT_UUID, CE_TASK_UUID, "my-webhook", url.toString());
155
156     server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", "ftp://foo"));
157
158     WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
159
160     Throwable error = delivery.getError().get();
161     assertThat(error)
162       .isInstanceOf(IllegalStateException.class)
163       .hasMessage("Unsupported protocol in redirect of " + url + " to ftp://foo");
164   }
165
166   private void takeAndVerifyPostRequest(String expectedPath) throws Exception {
167     RecordedRequest redirectedRequest = server.takeRequest();
168
169     assertThat(redirectedRequest.getMethod()).isEqualTo("POST");
170     assertThat(redirectedRequest.getPath()).isEqualTo(expectedPath);
171   }
172
173   private WebhookCaller newSender() {
174     SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("6.2"), SonarQubeSide.SERVER);
175     return new WebhookCallerImpl(system, new OkHttpClientProvider().provide(new MapSettings().asConfig(), runtime));
176   }
177 }