Skip to content

Commit

Permalink
SONAR-10457 Short living branch quality guate is based only on open &…
Browse files Browse the repository at this point in the history
… reopen issues
  • Loading branch information
Guillaume Jambet authored and gjambet committed Mar 14, 2018
1 parent da9523e commit 0edf8a3
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 56 deletions.
Expand Up @@ -158,13 +158,14 @@ private static void setBranchStatus(ProjectBranches.Branch.Builder builder, Bran
ProjectBranches.Status.Builder statusBuilder = ProjectBranches.Status.newBuilder();
if (qualityGateMeasure != null) {
Protobuf.setNullable(qualityGateMeasure.getDataAsString(), statusBuilder::setQualityGateStatus);
builder.setStatus(statusBuilder);
}
if (branch.getBranchType() == BranchType.SHORT) {
statusBuilder.setBugs(branchStatistics == null ? 0L : branchStatistics.getBugs());
statusBuilder.setVulnerabilities(branchStatistics == null ? 0L : branchStatistics.getVulnerabilities());
statusBuilder.setCodeSmells(branchStatistics == null ? 0L : branchStatistics.getCodeSmells());
builder.setStatus(statusBuilder);
}
builder.setStatus(statusBuilder);
}

private void checkPermission(ComponentDto component) {
Expand Down
Expand Up @@ -34,14 +34,12 @@ public final class ShortLivingBranchQualityGate {
public static final long ID = -1_963_456_987L;
public static final String NAME = "Hardcoded short living branch quality gate";
public static final List<Condition> CONDITIONS = ImmutableList.of(
new Condition(CoreMetrics.BUGS_KEY, OPERATOR_GREATER_THAN, "0", false),
new Condition(CoreMetrics.VULNERABILITIES_KEY, OPERATOR_GREATER_THAN, "0", false),
new Condition(CoreMetrics.CODE_SMELLS_KEY, OPERATOR_GREATER_THAN, "0", false));
new Condition(CoreMetrics.OPEN_ISSUES_KEY, OPERATOR_GREATER_THAN, "0", false),
new Condition(CoreMetrics.REOPENED_ISSUES_KEY, OPERATOR_GREATER_THAN, "0", false));

public static final QualityGate GATE = new QualityGate(String.valueOf(ID), NAME, ImmutableSet.of(
new org.sonar.server.qualitygate.Condition(CoreMetrics.BUGS_KEY, org.sonar.server.qualitygate.Condition.Operator.GREATER_THAN, "0", null, false),
new org.sonar.server.qualitygate.Condition(CoreMetrics.VULNERABILITIES_KEY, org.sonar.server.qualitygate.Condition.Operator.GREATER_THAN, "0", null, false),
new org.sonar.server.qualitygate.Condition(CoreMetrics.CODE_SMELLS_KEY, org.sonar.server.qualitygate.Condition.Operator.GREATER_THAN, "0", null, false)));
new org.sonar.server.qualitygate.Condition(CoreMetrics.OPEN_ISSUES_KEY, org.sonar.server.qualitygate.Condition.Operator.GREATER_THAN, "0", null, false),
new org.sonar.server.qualitygate.Condition(CoreMetrics.REOPENED_ISSUES_KEY, org.sonar.server.qualitygate.Condition.Operator.GREATER_THAN, "0", null, false)));

private ShortLivingBranchQualityGate() {
// prevents instantiation
Expand Down
Expand Up @@ -4,13 +4,22 @@
"name": "feature/bar",
"isMain": false,
"type": "LONG",
"status": {
"qualityGateStatus": "OK"
},
"analysisDate": "2017-04-01T01:15:42+0100"
},
{
"name": "feature/foo",
"isMain": false,
"type": "SHORT",
"mergeBranch": "feature/bar",
"status": {
"qualityGateStatus": "OK",
"bugs": 1,
"vulnerabilities": 0,
"codeSmells": 0
},
"analysisDate": "2017-04-03T13:37:00+0100"
}
]
Expand Down
Expand Up @@ -28,7 +28,6 @@
import org.sonar.api.resources.ResourceTypes;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
Expand All @@ -47,7 +46,7 @@
import org.sonar.server.permission.index.PermissionIndexerTester;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.Common;
import org.sonarqube.ws.Common.BranchType;
import org.sonarqube.ws.MediaTypes;
import org.sonarqube.ws.ProjectBranches;
import org.sonarqube.ws.ProjectBranches.Branch;
Expand All @@ -66,6 +65,7 @@
import static org.sonar.api.rules.RuleType.VULNERABILITY;
import static org.sonar.api.utils.DateUtils.dateToLong;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
import static org.sonar.db.component.BranchType.LONG;
import static org.sonar.db.component.BranchType.SHORT;
Expand Down Expand Up @@ -111,39 +111,56 @@ public void test_definition() {
@Test
public void test_example() {
ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("sonarqube"));

ComponentDto longLivingBranch = db.components()
.insertProjectBranch(project, b -> b.setKey("feature/bar").setBranchType(LONG));
ComponentDto shortLivingBranch = db.components()
.insertProjectBranch(project, b -> b.setKey("feature/foo").setBranchType(SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
userSession.logIn().addProjectPermission(UserRole.USER, project);

db.getDbClient().snapshotDao().insert(db.getSession(),
newAnalysis(longLivingBranch).setLast(true).setCreatedAt(parseDateTime("2017-04-01T01:15:42+0100").getTime()));
db.measures().insertLiveMeasure(longLivingBranch, qualityGateStatus, m -> m.setData("OK"));

ComponentDto shortLivingBranch = db.components()
.insertProjectBranch(project, b -> b.setKey("feature/foo").setBranchType(SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
db.getDbClient().snapshotDao().insert(db.getSession(),
newAnalysis(shortLivingBranch).setLast(true).setCreatedAt(parseDateTime("2017-04-03T13:37:00+0100").getTime()));
db.commit();
db.measures().insertLiveMeasure(shortLivingBranch, qualityGateStatus, m -> m.setData("OK"));

RuleDefinitionDto rule = db.rules().insert();
db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(BUG).setResolution(null));

issueIndexer.indexOnStartup(emptySet());

userSession.logIn().addProjectPermission(USER, project);

String json = ws.newRequest()
.setParam("project", project.getDbKey())
.execute()
.getInput();

assertJson(json).isSimilarTo(ws.getDef().responseExampleAsString());
assertJson(ws.getDef().responseExampleAsString()).isSimilarTo(json);
}

@Test
public void test_with_SCAN_EXCUTION_permission() {
ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("sonarqube"));
ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setKey("feature/bar").setBranchType(LONG));
ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
b -> b.setKey("feature/foo").setBranchType(SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
userSession.logIn().addProjectPermission(SCAN_EXECUTION, project);

ComponentDto longLivingBranch = db.components()
.insertProjectBranch(project, b -> b.setKey("feature/bar").setBranchType(LONG));
db.getDbClient().snapshotDao().insert(db.getSession(),
newAnalysis(longLivingBranch).setLast(true).setCreatedAt(parseDateTime("2017-04-01T01:15:42+0100").getTime()));
db.measures().insertLiveMeasure(longLivingBranch, qualityGateStatus, m -> m.setData("OK"));

ComponentDto shortLivingBranch = db.components()
.insertProjectBranch(project, b -> b.setKey("feature/foo").setBranchType(SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
db.getDbClient().snapshotDao().insert(db.getSession(),
newAnalysis(shortLivingBranch).setLast(true).setCreatedAt(parseDateTime("2017-04-03T13:37:00+0100").getTime()));
db.commit();
db.measures().insertLiveMeasure(shortLivingBranch, qualityGateStatus, m -> m.setData("OK"));

RuleDefinitionDto rule = db.rules().insert();
db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(BUG).setResolution(null));
issueIndexer.indexOnStartup(emptySet());

userSession.logIn().addProjectPermission(SCAN_EXECUTION, project);

String json = ws.newRequest()
.setParam("project", project.getDbKey())
Expand All @@ -156,36 +173,36 @@ public void test_with_SCAN_EXCUTION_permission() {
@Test
public void main_branch() {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);

ListWsResponse response = ws.newRequest()
.setParam("project", project.getDbKey())
.executeProtobuf(ListWsResponse.class);

assertThat(response.getBranchesList())
.extracting(Branch::getName, Branch::getIsMain, Branch::getType)
.containsExactlyInAnyOrder(tuple("master", true, Common.BranchType.LONG));
.containsExactlyInAnyOrder(tuple("master", true, BranchType.LONG));
}

@Test
public void main_branch_with_specified_name() {
OrganizationDto organization = db.organizations().insert();
ComponentDto project = db.components().insertMainBranch(organization, "head");
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);

ListWsResponse response = ws.newRequest()
.setParam("project", project.getDbKey())
.executeProtobuf(ListWsResponse.class);

assertThat(response.getBranchesList())
.extracting(Branch::getName, Branch::getIsMain, Branch::getType)
.containsExactlyInAnyOrder(tuple("head", true, Common.BranchType.LONG));
.containsExactlyInAnyOrder(tuple("head", true, BranchType.LONG));
}

@Test
public void test_project_with_zero_branches() {
ComponentDto project = db.components().insertPrivateProject();
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);

String json = ws.newRequest()
.setParam("project", project.getDbKey())
Expand All @@ -200,7 +217,7 @@ public void test_project_with_branches() {
ComponentDto project = db.components().insertMainBranch();
db.components().insertProjectBranch(project, b -> b.setKey("feature/bar"));
db.components().insertProjectBranch(project, b -> b.setKey("feature/foo"));
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);

ListWsResponse response = ws.newRequest()
.setParam("project", project.getDbKey())
Expand All @@ -209,15 +226,15 @@ public void test_project_with_branches() {
assertThat(response.getBranchesList())
.extracting(Branch::getName, Branch::getType)
.containsExactlyInAnyOrder(
tuple("master", Common.BranchType.LONG),
tuple("feature/foo", Common.BranchType.LONG),
tuple("feature/bar", Common.BranchType.LONG));
tuple("master", BranchType.LONG),
tuple("feature/foo", BranchType.LONG),
tuple("feature/bar", BranchType.LONG));
}

@Test
public void short_living_branches() {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);
ComponentDto longLivingBranch = db.components().insertProjectBranch(project,
b -> b.setKey("long").setBranchType(LONG));
ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
Expand All @@ -232,16 +249,16 @@ public void short_living_branches() {
assertThat(response.getBranchesList())
.extracting(Branch::getName, Branch::getType, Branch::getMergeBranch)
.containsExactlyInAnyOrder(
tuple("master", Common.BranchType.LONG, ""),
tuple(longLivingBranch.getBranch(), Common.BranchType.LONG, ""),
tuple(shortLivingBranch.getBranch(), Common.BranchType.SHORT, longLivingBranch.getBranch()),
tuple(shortLivingBranchOnMaster.getBranch(), Common.BranchType.SHORT, "master"));
tuple("master", BranchType.LONG, ""),
tuple(longLivingBranch.getBranch(), BranchType.LONG, ""),
tuple(shortLivingBranch.getBranch(), BranchType.SHORT, longLivingBranch.getBranch()),
tuple(shortLivingBranchOnMaster.getBranch(), BranchType.SHORT, "master"));
}

@Test
public void mergeBranch_is_using_default_main_name_when_main_branch_has_no_name() {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);
ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
b -> b.setKey("short").setBranchType(SHORT).setMergeBranchUuid(project.uuid()));

Expand All @@ -251,13 +268,13 @@ public void mergeBranch_is_using_default_main_name_when_main_branch_has_no_name(

assertThat(response.getBranch())
.extracting(Branch::getName, Branch::getType, Branch::getMergeBranch)
.containsExactlyInAnyOrder(shortLivingBranch.getBranch(), Common.BranchType.SHORT, "master");
.containsExactlyInAnyOrder(shortLivingBranch.getBranch(), BranchType.SHORT, "master");
}

@Test
public void short_living_branch_on_removed_branch() {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);
ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
b -> b.setKey("short").setBranchType(SHORT).setMergeBranchUuid("unknown"));

Expand All @@ -268,14 +285,14 @@ public void short_living_branch_on_removed_branch() {
assertThat(response.getBranchesList())
.extracting(Branch::getName, Branch::getType, Branch::hasMergeBranch, Branch::getIsOrphan)
.containsExactlyInAnyOrder(
tuple("master", Common.BranchType.LONG, false, false),
tuple(shortLivingBranch.getBranch(), Common.BranchType.SHORT, false, true));
tuple("master", BranchType.LONG, false, false),
tuple(shortLivingBranch.getBranch(), BranchType.SHORT, false, true));
}

@Test
public void status_on_long_living_branch() {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(LONG));
db.measures().insertLiveMeasure(branch, qualityGateStatus, m -> m.setData("OK"));

Expand All @@ -291,7 +308,7 @@ public void status_on_long_living_branch() {
@Test
public void status_on_short_living_branches() {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);
ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(LONG));
ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
b -> b.setKey("short").setBranchType(SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
Expand Down Expand Up @@ -329,7 +346,7 @@ public void status_on_short_living_branches() {
@Test
public void status_on_short_living_branch_with_no_issue() {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);
ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(LONG));
db.components().insertProjectBranch(project, b -> b.setBranchType(SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
issueIndexer.indexOnStartup(emptySet());
Expand All @@ -339,7 +356,7 @@ public void status_on_short_living_branch_with_no_issue() {
.setParam("project", project.getKey())
.executeProtobuf(ListWsResponse.class);

assertThat(response.getBranchesList().stream().filter(b -> b.getType().equals(Common.BranchType.SHORT)).map(ProjectBranches.Branch::getStatus))
assertThat(response.getBranchesList().stream().filter(b -> b.getType().equals(BranchType.SHORT)).map(ProjectBranches.Branch::getStatus))
.extracting(Status::getBugs, Status::getVulnerabilities, Status::getCodeSmells)
.containsExactlyInAnyOrder(tuple(0L, 0L, 0L));
}
Expand All @@ -351,7 +368,7 @@ public void response_contains_date_of_last_analysis() {
Long lastAnalysisShortLivingBranch = dateToLong(parseDateTime("2017-04-03T00:00:00+0100"));

ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);
ComponentDto shortLivingBranch1 = db.components().insertProjectBranch(project, b -> b.setBranchType(SHORT).setMergeBranchUuid(project.uuid()));
ComponentDto longLivingBranch2 = db.components().insertProjectBranch(project, b -> b.setBranchType(LONG));
ComponentDto shortLivingBranch2 = db.components().insertProjectBranch(project, b -> b.setBranchType(SHORT).setMergeBranchUuid(longLivingBranch2.uuid()));
Expand All @@ -373,17 +390,17 @@ public void response_contains_date_of_last_analysis() {
.extracting(ProjectBranches.Branch::getType, ProjectBranches.Branch::hasAnalysisDate,
b -> "".equals(b.getAnalysisDate()) ? null : dateToLong(parseDateTime(b.getAnalysisDate())))
.containsExactlyInAnyOrder(
tuple(Common.BranchType.LONG, false, null),
tuple(Common.BranchType.SHORT, false, null),
tuple(Common.BranchType.LONG, true, lastAnalysisLongLivingBranch),
tuple(Common.BranchType.SHORT, true, lastAnalysisShortLivingBranch));
tuple(BranchType.LONG, false, null),
tuple(BranchType.SHORT, false, null),
tuple(BranchType.LONG, true, lastAnalysisLongLivingBranch),
tuple(BranchType.SHORT, true, lastAnalysisShortLivingBranch));
}

@Test
public void fail_when_using_branch_db_key() throws Exception {
OrganizationDto organization = db.organizations().insert();
ComponentDto project = db.components().insertMainBranch(organization);
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);
ComponentDto branch = db.components().insertProjectBranch(project);

expectedException.expect(NotFoundException.class);
Expand All @@ -406,7 +423,7 @@ public void fail_if_missing_project_parameter() {
public void fail_if_not_a_reference_on_project() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
userSession.logIn().addProjectPermission(UserRole.USER, project);
userSession.logIn().addProjectPermission(USER, project);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Invalid project key");
Expand Down
Expand Up @@ -127,6 +127,8 @@ public void findById_of_hardcoded_short_living_branch_returns_hardcoded_qp() {
MetricImpl bugsMetric = mockMetricInRepository(CoreMetrics.BUGS_KEY);
MetricImpl vulnerabilitiesMetric = mockMetricInRepository(CoreMetrics.VULNERABILITIES_KEY);
MetricImpl codeSmellsMetric = mockMetricInRepository(CoreMetrics.CODE_SMELLS_KEY);
MetricImpl openedIssueMetric = mockMetricInRepository(CoreMetrics.OPEN_ISSUES_KEY);
MetricImpl reOpenedIssueMetric = mockMetricInRepository(CoreMetrics.REOPENED_ISSUES_KEY);

Optional<QualityGate> res = underTest.findById(ShortLivingBranchQualityGate.ID);

Expand All @@ -137,9 +139,8 @@ public void findById_of_hardcoded_short_living_branch_returns_hardcoded_qp() {
assertThat(qualityGate.getConditions())
.extracting(Condition::getMetric, Condition::getOperator, Condition::getErrorThreshold, Condition::getWarningThreshold, Condition::hasPeriod)
.containsOnly(
tuple(bugsMetric, GREATER_THAN, "0", null, false),
tuple(vulnerabilitiesMetric, GREATER_THAN, "0", null, false),
tuple(codeSmellsMetric, GREATER_THAN, "0", null, false));
tuple(openedIssueMetric, GREATER_THAN, "0", null, false),
tuple(reOpenedIssueMetric, GREATER_THAN, "0", null, false));
}

private MetricImpl mockMetricInRepository(String metricKey) {
Expand Down

0 comments on commit 0edf8a3

Please sign in to comment.