Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support pull request chaining for GitHub #896

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.sonar.scanner.bootstrap.ScannerWsClient;
import org.sonar.scanner.protocol.GsonHelper;
import org.sonar.scanner.scan.branch.BranchInfo;
import org.sonar.scanner.scan.branch.BranchType;
import org.sonar.scanner.scan.branch.ProjectBranches;
import org.sonar.scanner.scan.branch.ProjectBranchesLoader;
import org.sonar.server.branch.ws.ProjectBranchesParameters;
Expand All @@ -48,6 +49,8 @@ public class CommunityProjectBranchesLoader implements ProjectBranchesLoader {
String.format("/%s/%s?%s=", ProjectBranchesParameters.CONTROLLER, ProjectBranchesParameters.ACTION_LIST,
ProjectBranchesParameters.PARAM_PROJECT);

private static final String PROJECT_PULL_REQUESTS_URL = "/api/project_pull_requests/list?project=";

private final ScannerWsClient scannerWsClient;
private final Gson gson;

Expand All @@ -59,24 +62,54 @@ public CommunityProjectBranchesLoader(ScannerWsClient scannerWsClient) {

@Override
public ProjectBranches load(String projectKey) {
List<BranchInfo> branches;

try {
GetRequest branchesGetRequest =
new GetRequest(PROJECT_BRANCHES_URL + URLEncoder.encode(projectKey, StandardCharsets.UTF_8.name()));
new GetRequest(PROJECT_BRANCHES_URL + URLEncoder.encode(projectKey, StandardCharsets.UTF_8));
try (WsResponse branchesResponse = scannerWsClient
.call(branchesGetRequest); Reader reader = branchesResponse
.contentReader()) {
BranchesResponse parsedResponse = gson.fromJson(reader, BranchesResponse.class);
return new ProjectBranches(parsedResponse.getBranches());
branches = new ArrayList<>(parsedResponse.getBranches());
}
} catch (IOException e) {
throw MessageException.of("Could not load branches from server", e);
} catch (HttpException e) {
if (404 == e.code()) {
return new ProjectBranches(new ArrayList<>());
branches = new ArrayList<>();
} else {
throw MessageException.of("Could not load branches from server", e);
}
}

try {
GetRequest pullRequestsGetRequest =
new GetRequest(PROJECT_PULL_REQUESTS_URL + URLEncoder.encode(projectKey, StandardCharsets.UTF_8));
try (WsResponse pullRequestsResponse = scannerWsClient
.call(pullRequestsGetRequest); Reader reader = pullRequestsResponse
.contentReader()) {
PullRequestsResponse parsedResponse = gson.fromJson(reader, PullRequestsResponse.class);
for (PullRequestsResponse.PullRequest pullRequest : parsedResponse.getPullRequests()) {
branches.add(
new BranchInfo(
pullRequest.getBranch(),
BranchType.PULL_REQUEST,
false,
pullRequest.getTarget()
)
);
}
}
} catch (IOException e) {
throw MessageException.of("Could not load pull requests from server", e);
} catch (HttpException e) {
if (e.code() != 404) {
throw MessageException.of("Could not load pull requests from server", e);
}
}

return new ProjectBranches(branches);
}

/*package*/ static class BranchesResponse {
Expand All @@ -93,4 +126,36 @@ public ProjectBranches load(String projectKey) {
}
}

/*package*/ static class PullRequestsResponse {

private final List<PullRequest> pullRequests;

/*package*/ PullRequestsResponse(List<PullRequest> pullRequests) {
super();
this.pullRequests = pullRequests;
}

/*package*/ List<PullRequest> getPullRequests() {
return pullRequests;
}

/*package*/ static class PullRequest {
private final String branch;
private final String target;

/*package*/ PullRequest(String branch, String target) {
this.branch = branch;
this.target = target;
}

/*package*/ String getBranch() {
return branch;
}

/*package*/ String getTarget() {
return target;
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package com.github.mc1arke.sonarqube.plugin.scanner;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
Expand All @@ -27,6 +28,7 @@
import org.sonar.scanner.scan.branch.BranchInfo;
import org.sonar.scanner.scan.branch.BranchType;
import org.sonar.scanner.scan.branch.ProjectBranches;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.HttpException;
import org.sonarqube.ws.client.WsResponse;

Expand All @@ -35,14 +37,18 @@
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;

/**
Expand All @@ -51,21 +57,32 @@
public class CommunityRepositoryBranchesLoaderTest {

private final ScannerWsClient scannerWsClient = mock(ScannerWsClient.class);
private final Map<String, WsResponse> scannerWsResponses = new HashMap<>();
private final ExpectedException expectedException = ExpectedException.none();

@Rule
public ExpectedException expectedException() {
return expectedException;
}

@Before
public void setUp() {
when(scannerWsClient.call(any())).thenAnswer(invocation -> {
GetRequest request = invocation.getArgument(0);
return scannerWsResponses.get(request.getPath());
});
}

@Test
public void testEmptyBranchesOnEmptyServerResponse() {
WsResponse mockResponse = mock(WsResponse.class);
when(scannerWsClient.call(any())).thenReturn(mockResponse);

StringReader stringReader = new StringReader(
GsonHelper.create().toJson(new CommunityProjectBranchesLoader.BranchesResponse(new ArrayList<>())));
when(mockResponse.contentReader()).thenReturn(stringReader);
mockResponse(
"/api/project_branches/list?project=projectKey",
new CommunityProjectBranchesLoader.BranchesResponse(new ArrayList<>())
);
mockResponse(
"/api/project_pull_requests/list?project=projectKey",
new CommunityProjectBranchesLoader.PullRequestsResponse(new ArrayList<>())
);

CommunityProjectBranchesLoader testCase = new CommunityProjectBranchesLoader(scannerWsClient);
ProjectBranches response = testCase.load("projectKey");
Expand All @@ -74,17 +91,18 @@ public void testEmptyBranchesOnEmptyServerResponse() {

@Test
public void testAllBranchesFromNonEmptyServerResponse() {
WsResponse mockResponse = mock(WsResponse.class);
when(scannerWsClient.call(any())).thenReturn(mockResponse);

List<BranchInfo> infos = new ArrayList<>();
for (int i = 0; i < 10; i++) {
infos.add(new BranchInfo("key" + i, BranchType.BRANCH, i == 1, "target" + i));
}

StringReader stringReader = new StringReader(
GsonHelper.create().toJson(new CommunityProjectBranchesLoader.BranchesResponse(infos)));
when(mockResponse.contentReader()).thenReturn(stringReader);
mockResponse(
"/api/project_branches/list?project=key",
new CommunityProjectBranchesLoader.BranchesResponse(infos)
);
mockResponse(
"/api/project_pull_requests/list?project=key",
new CommunityProjectBranchesLoader.PullRequestsResponse(new ArrayList<>())
);

CommunityProjectBranchesLoader testCase = new CommunityProjectBranchesLoader(scannerWsClient);
ProjectBranches response = testCase.load("key");
Expand All @@ -100,40 +118,90 @@ public void testAllBranchesFromNonEmptyServerResponse() {
}

@Test
public void testMessageExceptionOnIOException() {
WsResponse mockResponse = mock(WsResponse.class);
when(scannerWsClient.call(any())).thenReturn(mockResponse);
public void testAllBranchesAndPullRequestsFromNonEmptyServerResponse() {
mockResponse(
"/api/project_branches/list?project=key",
new CommunityProjectBranchesLoader.BranchesResponse(
List.of(
new BranchInfo("main", BranchType.BRANCH, true, null),
new BranchInfo("feature-1", BranchType.BRANCH, false, null),
new BranchInfo("feature-2", BranchType.BRANCH, false, null)
)
)
);
mockResponse(
"/api/project_pull_requests/list?project=key",
new CommunityProjectBranchesLoader.PullRequestsResponse(
List.of(
new CommunityProjectBranchesLoader.PullRequestsResponse.PullRequest("pr-1", "main"),
new CommunityProjectBranchesLoader.PullRequestsResponse.PullRequest("pr-2", "main"),
new CommunityProjectBranchesLoader.PullRequestsResponse.PullRequest("pr-3", "pr-2")
)
)
);

Reader mockReader = new BufferedReader(new StringReader(
CommunityProjectBranchesLoader testCase = new CommunityProjectBranchesLoader(scannerWsClient);
ProjectBranches response = testCase.load("key");
assertThat(response.isEmpty()).isFalse();
assertThat(response.defaultBranchName()).isEqualTo("main");
assertThat(response.get("main"))
.usingRecursiveComparison()
.isEqualTo(new BranchInfo("main", BranchType.BRANCH, true, null));
assertThat(response.get("feature-1"))
.usingRecursiveComparison()
.isEqualTo(new BranchInfo("feature-1", BranchType.BRANCH, false, null));
assertThat(response.get("feature-2"))
.usingRecursiveComparison()
.isEqualTo(new BranchInfo("feature-2", BranchType.BRANCH, false, null));
assertThat(response.get("pr-1"))
.usingRecursiveComparison()
.isEqualTo(new BranchInfo("pr-1", BranchType.PULL_REQUEST, false, "main"));
assertThat(response.get("pr-2"))
.usingRecursiveComparison()
.isEqualTo(new BranchInfo("pr-2", BranchType.PULL_REQUEST, false, "main"));
assertThat(response.get("pr-3"))
.usingRecursiveComparison()
.isEqualTo(new BranchInfo("pr-3", BranchType.PULL_REQUEST, false, "pr-2"));
}

@Test
public void testMessageExceptionOnIOException() {
mockResponseWithReader(
"/api/project_branches/list?project=project",
new BufferedReader(new StringReader(
GsonHelper.create().toJson(new CommunityProjectBranchesLoader.BranchesResponse(new ArrayList<>())))) {
public void close() throws IOException {
throw new IOException("Dummy IO Exception");
public void close() throws IOException {
throw new IOException("Dummy IO Exception");
}
}
};
when(mockResponse.contentReader()).thenReturn(mockReader);
);
mockResponse(
"/api/project_pull_requests/list?project=project",
new CommunityProjectBranchesLoader.PullRequestsResponse(new ArrayList<>())
);

expectedException.expectMessage("Could not load branches from server");
expectedException.expect(MessageException.class);

CommunityProjectBranchesLoader testCase = new CommunityProjectBranchesLoader(scannerWsClient);
testCase.load("project");


}


@Test
public void testErrorOnNon404HttpResponse() {
WsResponse mockResponse = mock(WsResponse.class);
when(scannerWsClient.call(any())).thenReturn(mockResponse);

Reader mockReader = new BufferedReader(new StringReader(
mockResponseWithReader(
"/api/project_branches/list?project=project",
new BufferedReader(new StringReader(
GsonHelper.create().toJson(new CommunityProjectBranchesLoader.BranchesResponse(new ArrayList<>())))) {
public void close() {
throw new HttpException("url", 12, "content");
public void close() throws IOException {
throw new HttpException("url", 12, "content");
}
}
};
when(mockResponse.contentReader()).thenReturn(mockReader);
);
mockResponse(
"/api/project_pull_requests/list?project=project",
new CommunityProjectBranchesLoader.PullRequestsResponse(new ArrayList<>())
);

expectedException.expectMessage("Could not load branches from server");
expectedException.expect(MessageException.class);
Expand All @@ -142,12 +210,23 @@ public void close() {
testCase.load("project");
}


@Test
public void testEmptyListOn404HttpResponse() {
reset(scannerWsClient);
when(scannerWsClient.call(any())).thenThrow(new HttpException("url", 404, "content"));

CommunityProjectBranchesLoader testCase = new CommunityProjectBranchesLoader(scannerWsClient);
assertTrue(testCase.load("project").isEmpty());
}

private void mockResponse(String requestPath, Object response) {
mockResponseWithReader(requestPath, new StringReader(GsonHelper.create().toJson(response)));
}

private void mockResponseWithReader(String requestPath, Reader responseReader) {
WsResponse mockResponse = mock(WsResponse.class);
when(mockResponse.contentReader()).thenReturn(responseReader);
scannerWsResponses.put(requestPath, mockResponse);
}

}
Loading