--- /dev/null
+/*
+ * 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.badge.ws;
+
+import java.nio.charset.Charset;
+
+public class ETagUtils {
+ // Format for Expires Header
+ static final String RFC1123_DATE = "EEE, dd MMM yyyy HH:mm:ss zzz";
+
+ private static final long FNV1_INIT = 0xcbf29ce484222325L;
+ private static final long FNV1_PRIME = 0x100000001b3L;
+
+ private ETagUtils() {
+ // Utility class no instantiation allowed
+ }
+
+ /**
+ * hash method of a String independant of the JVM
+ * FNV-1a hash method @see <a href="https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash"></a>
+ */
+ private static long hash(byte[] input) {
+ long hash = FNV1_INIT;
+ for (byte b : input) {
+ hash ^= b & 0xff;
+ hash *= FNV1_PRIME;
+ }
+ return hash;
+ }
+
+ /**
+ * Calculate the ETag of the badge
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/HTTP_ETag"></a>
+ * <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.11"></a>
+ */
+ static String getETag(String output) {
+ return "W/" + hash(output.getBytes(Charset.forName("UTF-8")));
+ }
+}
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Resources;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.EnumMap;
+import java.util.Locale;
import java.util.Map;
+import java.util.Optional;
import java.util.function.Function;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
import static org.sonar.api.measures.Metric.Level;
-import static org.sonar.api.measures.Metric.ValueType;
import static org.sonar.api.measures.Metric.Level.ERROR;
import static org.sonar.api.measures.Metric.Level.OK;
import static org.sonar.api.measures.Metric.Level.WARN;
+import static org.sonar.api.measures.Metric.ValueType;
+import static org.sonar.server.badge.ws.ETagUtils.RFC1123_DATE;
+import static org.sonar.server.badge.ws.ETagUtils.getETag;
import static org.sonar.server.badge.ws.SvgFormatter.formatDuration;
import static org.sonar.server.badge.ws.SvgFormatter.formatNumeric;
import static org.sonar.server.badge.ws.SvgFormatter.formatPercent;
@Override
public void handle(Request request, Response response) throws Exception {
+ response.setHeader("Cache-Control", "no-cache");
response.stream().setMediaType(SVG);
String metricKey = request.mandatoryParam(PARAM_METRIC);
try (DbSession dbSession = dbClient.openSession(false)) {
MetricDto metric = dbClient.metricDao().selectByKey(dbSession, metricKey);
checkState(metric != null && metric.isEnabled(), "Metric '%s' hasn't been found", metricKey);
LiveMeasureDto measure = getMeasure(dbSession, project, metricKey);
- write(generateSvg(metric, measure), response.stream().output(), UTF_8);
+ String result = generateSvg(metric, measure);
+ String eTag = getETag(result);
+ Optional<String> requestedETag = request.header("If-None-Match");
+ if (requestedETag.filter(eTag::equals).isPresent()) {
+ response.stream().setStatus(304);
+ return;
+ }
+ response.setHeader("ETag", eTag);
+ write(result, response.stream().output(), UTF_8);
} catch (ProjectBadgesException | ForbiddenException | NotFoundException e) {
+ // There is an issue, so do not return any ETag but make this response expire now
+ SimpleDateFormat sdf = new SimpleDateFormat(RFC1123_DATE, Locale.US);
+ response.setHeader("Expires", sdf.format(new Date()));
write(svgGenerator.generateError(e.getMessage()), response.stream().output(), UTF_8);
}
}
return value;
}
+
}
package org.sonar.server.badge.ws;
import com.google.common.io.Resources;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Optional;
import org.sonar.api.measures.Metric.Level;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.commons.io.IOUtils.write;
import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
+import static org.sonar.server.badge.ws.ETagUtils.RFC1123_DATE;
+import static org.sonar.server.badge.ws.ETagUtils.getETag;
import static org.sonarqube.ws.MediaTypes.SVG;
-public class QualityGateAction implements ProjectBadgesWsAction {
+public class QualityGateAction implements ProjectBadgesWsAction {
private final DbClient dbClient;
private final ProjectBadgesSupport support;
@Override
public void handle(Request request, Response response) throws Exception {
+ response.setHeader("Cache-Control", "no-cache");
response.stream().setMediaType(SVG);
try (DbSession dbSession = dbClient.openSession(false)) {
ComponentDto project = support.getComponent(dbSession, request);
Level qualityGateStatus = getQualityGate(dbSession, project);
- write(svgGenerator.generateQualityGate(qualityGateStatus), response.stream().output(), UTF_8);
+ String result = svgGenerator.generateQualityGate(qualityGateStatus);
+ String eTag = getETag(result);
+ Optional<String> requestedETag = request.header("If-None-Match");
+ if (requestedETag.filter(eTag::equals).isPresent()) {
+ response.stream().setStatus(304);
+ return;
+ }
+ response.setHeader("ETag", eTag);
+ write(result, response.stream().output(), UTF_8);
} catch (ProjectBadgesException | ForbiddenException | NotFoundException e) {
+ // There is an issue, so do not return any ETag but make this response expire now
+ SimpleDateFormat sdf = new SimpleDateFormat(RFC1123_DATE, Locale.US);
+ response.setHeader("Expires", sdf.format(new Date()));
write(svgGenerator.generateError(e.getMessage()), response.stream().output(), UTF_8);
}
}
--- /dev/null
+package org.sonar.server.badge.ws;/*
+ * 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.
+ */
+
+import org.junit.Test;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+public class ETagUtilsTest {
+
+ @Test
+ public void getETag_should_start_with_W_SLASH() {
+ assertThat(ETagUtils.getETag(randomAlphanumeric(15))).startsWith("W/");
+ }
+
+ @Test
+ public void getETag_should_return_same_value_for_same_input() {
+ String input = randomAlphanumeric(200);
+ assertThat(ETagUtils.getETag(input)).isEqualTo(ETagUtils.getETag(input));
+ }
+}
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.computation.task.projectanalysis.qualitymodel.Rating;
import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;
import static org.assertj.core.api.Assertions.assertThat;
MetricDto metric = db.measures().insertMetric(m -> m.setKey(BUGS_KEY).setValueType(INT.name()));
db.measures().insertLiveMeasure(project, metric, m -> m.setValue(10_000d));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkSvg(response, "bugs", "10k", DEFAULT);
+
+ // Second call with If-None-Match must return 304
+ checkWithIfNoneMatchHeader(project, metric, response);
}
@Test
MetricDto metric = db.measures().insertMetric(m -> m.setKey(COVERAGE_KEY).setValueType(PERCENT.name()));
db.measures().insertLiveMeasure(project, metric, m -> m.setValue(12.345d));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkSvg(response, "coverage", "12.3%", DEFAULT);
+
+ // Second call with If-None-Match must return 304
+ checkWithIfNoneMatchHeader(project, metric, response);
}
@Test
MetricDto metric = db.measures().insertMetric(m -> m.setKey(TECHNICAL_DEBT_KEY).setValueType(WORK_DUR.name()));
db.measures().insertLiveMeasure(project, metric, m -> m.setValue(10_000d));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkSvg(response, "technical debt", "21d", DEFAULT);
+
+ // Second call with If-None-Match must return 304
+ checkWithIfNoneMatchHeader(project, metric, response);
}
@DataProvider
MetricDto metric = db.measures().insertMetric(m -> m.setKey(SQALE_RATING_KEY).setValueType(RATING.name()));
db.measures().insertLiveMeasure(project, metric, m -> m.setValue((double) rating.getIndex()).setData(rating.name()));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkSvg(response, "maintainability", rating.name(), color);
+
+ // Second call with If-None-Match must return 304
+ checkWithIfNoneMatchHeader(project, metric, response);
}
@DataProvider
MetricDto metric = createQualityGateMetric();
db.measures().insertLiveMeasure(project, metric, m -> m.setData(status.name()));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkSvg(response, "quality gate", expectedValue, expectedColor);
+
+ // Second call with If-None-Match must return 304
+ checkWithIfNoneMatchHeader(project, metric, response);
}
@Test
ComponentDto longBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(LONG));
db.measures().insertLiveMeasure(longBranch, metric, m -> m.setValue(10_000d));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", longBranch.getKey())
.setParam("branch", longBranch.getBranch())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkSvg(response, "bugs", "10k", DEFAULT);
+
+ // Second call with If-None-Match must return 304
+ response = ws.newRequest()
+ .setHeader("If-None-Match", response.getHeader("ETag"))
+ .setParam("project", longBranch.getKey())
+ .setParam("branch", longBranch.getBranch())
+ .setParam("metric", metric.getKey())
+ .execute();
+
+ assertThat(response.getInput()).isEmpty();
+ assertThat(response.getStatus()).isEqualTo(304);
}
@Test
MetricDto metric = db.measures().insertMetric(m -> m.setKey(BUGS_KEY).setValueType(INT.name()));
db.measures().insertLiveMeasure(application, metric, m -> m.setValue(10_000d));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", application.getKey())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkSvg(response, "bugs", "10k", DEFAULT);
+
+ // Second call with If-None-Match must return 304
+ checkWithIfNoneMatchHeader(application, metric, response);
}
@Test
- public void return_error_if_project_does_not_exist() {
+ public void return_error_if_project_does_not_exist() throws ParseException {
MetricDto metric = db.measures().insertMetric(m -> m.setKey(BUGS_KEY));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", "unknown")
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Project has not been found");
}
@Test
- public void return_error_if_branch_does_not_exist() {
+ public void return_error_if_branch_does_not_exist() throws ParseException {
ComponentDto project = db.components().insertMainBranch();
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
userSession.addProjectPermission(USER, project);
MetricDto metric = db.measures().insertMetric(m -> m.setKey(BUGS_KEY));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", branch.getKey())
.setParam("branch", "unknown")
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Project has not been found");
}
@Test
- public void return_error_if_measure_not_found() {
+ public void return_error_if_measure_not_found() throws ParseException {
ComponentDto project = db.components().insertPublicProject();
userSession.registerComponents(project);
MetricDto metric = db.measures().insertMetric(m -> m.setKey(BUGS_KEY));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Measure has not been found");
}
@Test
- public void return_error_on_directory() {
+ public void return_error_on_directory() throws ParseException {
ComponentDto project = db.components().insertPublicProject();
ComponentDto directory = db.components().insertComponent(ComponentTesting.newDirectory(project, "path"));
userSession.registerComponents(project);
MetricDto metric = db.measures().insertMetric(m -> m.setKey(BUGS_KEY).setValueType(INT.name()));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", directory.getKey())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Project is invalid");
}
@Test
- public void return_error_on_short_living_branch() {
+ public void return_error_on_short_living_branch() throws ParseException {
ComponentDto project = db.components().insertMainBranch();
ComponentDto shortBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(SHORT));
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(USER, project);
MetricDto metric = db.measures().insertMetric(m -> m.setKey(BUGS_KEY).setValueType(INT.name()));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", shortBranch.getKey())
.setParam("branch", shortBranch.getBranch())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Project is invalid");
}
@Test
- public void return_error_on_private_project() {
+ public void return_error_on_private_project() throws ParseException {
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(USER, project);
MetricDto metric = db.measures().insertMetric(m -> m.setKey(BUGS_KEY).setValueType(INT.name()));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Project is invalid");
}
@Test
- public void return_error_on_provisioned_project() {
+ public void return_error_on_provisioned_project() throws ParseException {
ComponentDto project = db.components().insertPublicProject();
userSession.registerComponents(project);
MetricDto metric = db.measures().insertMetric(m -> m.setKey(BUGS_KEY).setValueType(INT.name()));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Measure has not been found");
}
@Test
- public void return_error_if_unauthorized() {
+ public void return_error_if_unauthorized() throws ParseException {
ComponentDto project = db.components().insertPublicProject();
MetricDto metric = db.measures().insertMetric(m -> m.setKey(BUGS_KEY));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Insufficient privileges");
}
tuple("metric", true));
}
- private void checkSvg(String svg, String expectedLabel, String expectedValue, Color expectedColorValue) {
- assertThat(svg).contains(
+ private void checkSvg(TestResponse response, String expectedLabel, String expectedValue, Color expectedColorValue) {
+ assertThat(response.getHeader("ETag")).startsWith("W/");
+ assertThat(response.getHeader("Cache-Control")).contains("no-cache");
+ assertThat(response.getHeader("Expires")).isNull();
+
+ assertThat(response.getInput()).contains(
"<text", expectedLabel + "</text>",
"<text", expectedValue + "</text>",
"rect fill=\"" + expectedColorValue.getValue() + "\"");
}
- private void checkError(String svg, String expectedError) {
- assertThat(svg).contains("<text", ">" + expectedError + "</text>");
+ private void checkError(TestResponse response, String expectedError) throws ParseException {
+ SimpleDateFormat expiresDateFormat = new SimpleDateFormat(ETagUtils.RFC1123_DATE, Locale.US);
+ assertThat(response.getHeader("Cache-Control")).contains("no-cache");
+ assertThat(response.getHeader("Expires")).isNotNull();
+ assertThat(response.getHeader("ETag")).isNull();
+ assertThat(expiresDateFormat.parse(response.getHeader("Expires"))).isBefore(new Date());
+ assertThat(response.getInput()).contains("<text", ">" + expectedError + "</text>");
}
+ private void checkWithIfNoneMatchHeader(ComponentDto application, MetricDto metric, TestResponse response) {
+ TestResponse newResponse = ws.newRequest()
+ .setHeader("If-None-Match", response.getHeader("ETag"))
+ .setParam("project", application.getKey())
+ .setParam("metric", metric.getKey())
+ .execute();
+
+ assertThat(newResponse.getInput()).isEmpty();
+ assertThat(newResponse.getStatus()).isEqualTo(304);
+ }
private MetricDto createQualityGateMetric() {
return db.measures().insertMetric(m -> m.setKey(CoreMetrics.ALERT_STATUS_KEY).setValueType(LEVEL.name()));
}
*/
package org.sonar.server.badge.ws;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Locale;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;
import static org.assertj.core.api.Assertions.assertThat;
MetricDto metric = createQualityGateMetric();
db.measures().insertLiveMeasure(project, metric, m -> m.setData(OK.name()));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
- .execute().getInput();
+ .execute();
checkResponse(response, OK);
}
MetricDto metric = createQualityGateMetric();
db.measures().insertLiveMeasure(project, metric, m -> m.setData(WARN.name()));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
- .execute().getInput();
+ .execute();
checkResponse(response, WARN);
}
MetricDto metric = createQualityGateMetric();
db.measures().insertLiveMeasure(project, metric, m -> m.setData(ERROR.name()));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
- .execute().getInput();
+ .execute();
checkResponse(response, ERROR);
}
+ @Test
+ public void etag_should_be_different_if_quality_gate_is_different() {
+ ComponentDto project = db.components().insertPublicProject();
+ userSession.registerComponents(project);
+ MetricDto metric = createQualityGateMetric();
+ LiveMeasureDto liveMeasure = db.measures().insertLiveMeasure(project, metric, m -> m.setData(OK.name()));
+
+ TestResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .execute();
+ String eTagOK = response.getHeader("ETag");
+
+ liveMeasure.setData(WARN.name());
+ db.getDbClient().liveMeasureDao().insertOrUpdate(db.getSession(), liveMeasure, null);
+ db.commit();
+
+ response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .execute();
+
+ String eTagWARN = response.getHeader("ETag");
+
+ liveMeasure.setData(ERROR.name());
+ db.getDbClient().liveMeasureDao().insertOrUpdate(db.getSession(), liveMeasure, null);
+ db.commit();
+
+ response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .execute();
+
+ String eTagERROR = response.getHeader("ETag");
+
+ assertThat(Arrays.asList(eTagOK, eTagWARN, eTagERROR))
+ .doesNotContainNull()
+ .doesNotHaveDuplicates();
+ }
+
+ @Test
+ public void when_IfNoneMatch_match_etag_http_304_must_be_send() {
+ ComponentDto project = db.components().insertPublicProject();
+ userSession.registerComponents(project);
+ MetricDto metric = createQualityGateMetric();
+ db.measures().insertLiveMeasure(project, metric, m -> m.setData(OK.name()));
+
+ TestResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .execute();
+ String eTag = response.getHeader("ETag");
+
+ response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .setHeader("If-None-Match", eTag)
+ .execute();
+
+ assertThat(response.getInput()).isEmpty();
+ assertThat(response.getStatus()).isEqualTo(304);
+ }
+
@Test
public void quality_gate_on_long_living_branch() {
ComponentDto project = db.components().insertMainBranch(p -> p.setPrivate(false));
ComponentDto longBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(LONG));
db.measures().insertLiveMeasure(longBranch, metric, m -> m.setData(WARN.name()));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", longBranch.getKey())
.setParam("branch", longBranch.getBranch())
- .execute().getInput();
+ .execute();
checkResponse(response, WARN);
}
MetricDto metric = createQualityGateMetric();
db.measures().insertLiveMeasure(application, metric, m -> m.setData(WARN.name()));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", application.getKey())
- .execute().getInput();
+ .execute();
checkResponse(response, WARN);
}
@Test
- public void return_error_on_directory() {
+ public void return_error_on_directory() throws ParseException {
ComponentDto project = db.components().insertPublicProject();
ComponentDto directory = db.components().insertComponent(ComponentTesting.newDirectory(project, "path"));
userSession.registerComponents(project);
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", directory.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Project is invalid");
}
@Test
- public void return_error_on_short_living_branch() {
+ public void return_error_on_short_living_branch() throws ParseException {
ComponentDto project = db.components().insertMainBranch(p -> p.setPrivate(false));
userSession.registerComponents(project);
ComponentDto shortBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(SHORT));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", shortBranch.getKey())
.setParam("branch", shortBranch.getBranch())
- .execute().getInput();
+ .execute();
checkError(response, "Project is invalid");
}
@Test
- public void return_error_on_private_project() {
+ public void return_error_on_private_project() throws ParseException {
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(USER, project);
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Project is invalid");
}
@Test
- public void return_error_on_provisioned_project() {
+ public void return_error_on_provisioned_project() throws ParseException {
ComponentDto project = db.components().insertPublicProject();
userSession.registerComponents(project);
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Quality gate has not been found");
}
@Test
- public void return_error_on_not_existing_project() {
- String response = ws.newRequest()
+ public void return_error_on_not_existing_project() throws ParseException {
+ TestResponse response = ws.newRequest()
.setParam("project", "unknown")
- .execute().getInput();
+ .execute();
checkError(response, "Project has not been found");
}
@Test
- public void return_error_on_not_existing_branch() {
+ public void return_error_on_not_existing_branch() throws ParseException {
ComponentDto project = db.components().insertMainBranch(p -> p.setPrivate(false));
userSession.registerComponents(project);
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(LONG));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", branch.getKey())
.setParam("branch", "unknown")
- .execute().getInput();
+ .execute();
checkError(response, "Project has not been found");
}
@Test
- public void return_error_if_measure_not_found() {
+ public void return_error_if_measure_not_found() throws ParseException {
ComponentDto project = db.components().insertPublicProject();
userSession.registerComponents(project);
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Quality gate has not been found");
}
@Test
- public void return_error_if_measure_value_is_null() {
+ public void return_error_if_measure_value_is_null() throws ParseException {
ComponentDto project = db.components().insertPublicProject();
userSession.registerComponents(project);
MetricDto metric = createQualityGateMetric();
db.measures().insertLiveMeasure(project, metric, m -> m.setValue(null).setData((String) null));
- String response = ws.newRequest()
+ TestResponse response = ws.newRequest()
.setParam("project", project.getKey())
.setParam("metric", metric.getKey())
- .execute().getInput();
+ .execute();
checkError(response, "Quality gate has not been found");
}
return db.measures().insertMetric(m -> m.setKey(ALERT_STATUS_KEY).setValueType(LEVEL.name()));
}
- private void checkError(String svg, String expectedError) {
- assertThat(svg).contains("<text", ">" + expectedError + "</text>");
+ private void checkError(TestResponse response, String expectedError) throws ParseException {
+ SimpleDateFormat expiresDateFormat = new SimpleDateFormat(ETagUtils.RFC1123_DATE, Locale.US);
+ assertThat(response.getHeader("Cache-Control")).contains("no-cache");
+ assertThat(response.getHeader("Expires")).isNotNull();
+ assertThat(response.getHeader("ETag")).isNull();
+ assertThat(expiresDateFormat.parse(response.getHeader("Expires"))).isBefore(new Date());
+ assertThat(response.getInput()).contains("<text", ">" + expectedError + "</text>");
}
- private void checkResponse(String response, Level status) {
+ private void checkResponse(TestResponse response, Level status) {
+ assertThat(response.getHeader("ETag")).startsWith("W/");
+ assertThat(response.getHeader("Cache-Control")).contains("no-cache");
+ assertThat(response.getHeader("Expires")).isNull();
switch (status) {
case OK:
- assertThat(response).contains("<!-- SONARQUBE QUALITY GATE PASS -->");
+ assertThat(response.getInput()).contains("<!-- SONARQUBE QUALITY GATE PASS -->");
break;
case WARN:
- assertThat(response).contains("<!-- SONARQUBE QUALITY GATE WARN -->");
+ assertThat(response.getInput()).contains("<!-- SONARQUBE QUALITY GATE WARN -->");
break;
case ERROR:
- assertThat(response).contains("<!-- SONARQUBE QUALITY GATE FAIL -->");
+ assertThat(response.getInput()).contains("<!-- SONARQUBE QUALITY GATE FAIL -->");
break;
}
}
import com.google.common.base.Throwables;
import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.protobuf.GeneratedMessageV3;
return this;
}
+ @Override
+ public Map<String, String> getHeaders() {
+ return ImmutableMap.copyOf(headers);
+ }
+
@Override
public Optional<String> header(String name) {
return Optional.ofNullable(headers.get(name));
}
public TestRequest setHeader(String name, String value) {
- this.headers.put(requireNonNull(name), requireNonNull(value));
+ headers.put(requireNonNull(name), requireNonNull(value));
return this;
}