Do not rely on magic numbers for parsing the URL
Refactor PullReplicationFilter to use regular expressions when projectName is extracted
Add unit tests for PullReplicationFilter to cover main functionality
Bug: Issue 345363
Change-Id: I5c762df2d79a7b06aaed49c09d5f47f2d4b19dda
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilter.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilter.java
index 0a9b266..d2bca2b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilter.java
@@ -25,7 +25,6 @@
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
-import com.google.common.base.Splitter;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.api.projects.HeadInput;
@@ -38,7 +37,6 @@
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.httpd.AllRequestFilter;
import com.google.gerrit.httpd.restapi.RestApiServlet;
import com.google.gerrit.json.OutputFormat;
@@ -61,9 +59,11 @@
import java.io.EOFException;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
@@ -74,6 +74,10 @@
public class PullReplicationFilter extends AllRequestFilter {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private static final Pattern projectNameInGerritUrl = Pattern.compile(".*/projects/([^/]+)/.*");
+ private static final Pattern projectNameInitProjectUrl =
+ Pattern.compile(".*/init-project/([^/]+.git)");
+
private FetchAction fetchAction;
private ApplyObjectAction applyObjectAction;
private ApplyObjectsAction applyObjectsAction;
@@ -180,6 +184,9 @@
} catch (InitProjectException | ResourceNotFoundException e) {
RestApiServlet.replyError(
httpRequest, httpResponse, SC_INTERNAL_SERVER_ERROR, e.getMessage(), e.caching(), e);
+ } catch (NoSuchElementException e) {
+ RestApiServlet.replyError(
+ httpRequest, httpResponse, SC_BAD_REQUEST, "Project name not present in the url", e);
} catch (Exception e) {
throw new ServletException(e);
}
@@ -188,8 +195,8 @@
private void doInitProject(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws RestApiException, IOException, PermissionBackendException {
- String path = httpRequest.getRequestURI();
- String projectName = Url.decode(path.substring(path.lastIndexOf('/') + 1));
+ IdString id = getInitProjectName(httpRequest).get();
+ String projectName = id.get();
if (projectInitializationAction.initProject(projectName)) {
setResponse(
httpResponse, HttpServletResponse.SC_CREATED, "Project " + projectName + " initialized");
@@ -202,7 +209,7 @@
private Response<Map<String, Object>> doApplyObject(HttpServletRequest httpRequest)
throws RestApiException, IOException, PermissionBackendException {
RevisionInput input = readJson(httpRequest, TypeLiteral.get(RevisionInput.class));
- IdString id = getProjectName(httpRequest);
+ IdString id = getProjectName(httpRequest).get();
ProjectResource projectResource = projectsCollection.parse(TopLevelResource.INSTANCE, id);
return (Response<Map<String, Object>>) applyObjectAction.apply(projectResource, input);
@@ -212,7 +219,7 @@
private Response<Map<String, Object>> doApplyObjects(HttpServletRequest httpRequest)
throws RestApiException, IOException, PermissionBackendException {
RevisionsInput input = readJson(httpRequest, TypeLiteral.get(RevisionsInput.class));
- IdString id = getProjectName(httpRequest);
+ IdString id = getProjectName(httpRequest).get();
ProjectResource projectResource = projectsCollection.parse(TopLevelResource.INSTANCE, id);
return (Response<Map<String, Object>>) applyObjectsAction.apply(projectResource, input);
@@ -221,16 +228,16 @@
@SuppressWarnings("unchecked")
private Response<String> doUpdateHEAD(HttpServletRequest httpRequest) throws Exception {
HeadInput input = readJson(httpRequest, TypeLiteral.get(HeadInput.class));
- ProjectResource projectResource =
- projectsCollection.parse(TopLevelResource.INSTANCE, getProjectName(httpRequest));
+ IdString id = getProjectName(httpRequest).get();
+ ProjectResource projectResource = projectsCollection.parse(TopLevelResource.INSTANCE, id);
return (Response<String>) updateHEADAction.apply(projectResource, input);
}
@SuppressWarnings("unchecked")
private Response<String> doDeleteProject(HttpServletRequest httpRequest) throws Exception {
- ProjectResource projectResource =
- projectsCollection.parse(TopLevelResource.INSTANCE, getProjectName(httpRequest));
+ IdString id = getProjectName(httpRequest).get();
+ ProjectResource projectResource = projectsCollection.parse(TopLevelResource.INSTANCE, id);
return (Response<String>)
projectDeletionAction.apply(projectResource, new ProjectDeletionAction.DeleteInput());
}
@@ -239,7 +246,7 @@
private Response<Map<String, Object>> doFetch(HttpServletRequest httpRequest)
throws IOException, RestApiException, PermissionBackendException {
Input input = readJson(httpRequest, TypeLiteral.get(Input.class));
- IdString id = getProjectName(httpRequest);
+ IdString id = getProjectName(httpRequest).get();
ProjectResource projectResource = projectsCollection.parse(TopLevelResource.INSTANCE, id);
return (Response<Map<String, Object>>) fetchAction.apply(projectResource, input);
@@ -296,42 +303,48 @@
* @param req
* @return project name
*/
- private IdString getProjectName(HttpServletRequest req) {
- String path = req.getRequestURI();
+ private Optional<IdString> getInitProjectName(HttpServletRequest req) {
+ return extractProjectName(req, projectNameInitProjectUrl);
+ }
- List<IdString> out = new ArrayList<>();
- for (String p : Splitter.on('/').split(path)) {
- out.add(IdString.fromUrl(p));
+ private Optional<IdString> getProjectName(HttpServletRequest req) {
+ return extractProjectName(req, projectNameInGerritUrl);
+ }
+
+ private Optional<IdString> extractProjectName(HttpServletRequest req, Pattern urlPattern) {
+ String path = req.getRequestURI();
+ Matcher projectGroupMatcher = urlPattern.matcher(path);
+
+ if (projectGroupMatcher.find()) {
+ return Optional.of(IdString.fromUrl(projectGroupMatcher.group(1)));
}
- if (!out.isEmpty() && out.get(out.size() - 1).isEmpty()) {
- out.remove(out.size() - 1);
- }
- return out.get(3);
+
+ return Optional.empty();
}
private boolean isApplyObjectAction(HttpServletRequest httpRequest) {
- return httpRequest.getRequestURI().endsWith("pull-replication~apply-object");
+ return httpRequest.getRequestURI().endsWith(String.format("/%s~apply-object", pluginName));
}
private boolean isApplyObjectsAction(HttpServletRequest httpRequest) {
- return httpRequest.getRequestURI().endsWith("pull-replication~apply-objects");
+ return httpRequest.getRequestURI().endsWith(String.format("/%s~apply-objects", pluginName));
}
private boolean isFetchAction(HttpServletRequest httpRequest) {
- return httpRequest.getRequestURI().endsWith("pull-replication~fetch");
+ return httpRequest.getRequestURI().endsWith(String.format("/%s~fetch", pluginName));
}
private boolean isInitProjectAction(HttpServletRequest httpRequest) {
- return httpRequest.getRequestURI().contains("pull-replication/init-project/");
+ return httpRequest.getRequestURI().contains(String.format("/%s/init-project/", pluginName));
}
private boolean isUpdateHEADAction(HttpServletRequest httpRequest) {
- return httpRequest.getRequestURI().matches("(/a)?/projects/[^/]+/HEAD")
+ return httpRequest.getRequestURI().matches(".*/projects/[^/]+/HEAD")
&& "PUT".equals(httpRequest.getMethod());
}
private boolean isDeleteProjectAction(HttpServletRequest httpRequest) {
- return httpRequest.getRequestURI().endsWith(String.format("%s~delete-project", pluginName))
+ return httpRequest.getRequestURI().endsWith(String.format("/%s~delete-project", pluginName))
&& "DELETE".equals(httpRequest.getMethod());
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilterTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilterTest.java
new file mode 100644
index 0000000..da2d4a5
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/PullReplicationFilterTest.java
@@ -0,0 +1,358 @@
+package com.googlesource.gerrit.plugins.replication.pull.api;
+
+import static com.google.common.net.HttpHeaders.ACCEPT;
+import static com.google.gerrit.httpd.restapi.RestApiServlet.SC_UNPROCESSABLE_ENTITY;
+import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.internal.verification.VerificationModeFactory.atLeastOnce;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
+
+import com.google.common.net.MediaType;
+import com.google.gerrit.extensions.restapi.*;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.restapi.project.ProjectsCollection;
+import com.google.inject.Provider;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PullReplicationFilterTest {
+
+ @Mock HttpServletRequest request;
+ @Mock HttpServletResponse response;
+ @Mock FilterChain filterChain;
+ @Mock private FetchAction fetchAction;
+ @Mock private ApplyObjectAction applyObjectAction;
+ @Mock private ApplyObjectsAction applyObjectsAction;
+ @Mock private ProjectInitializationAction projectInitializationAction;
+ @Mock private UpdateHeadAction updateHEADAction;
+ @Mock private ProjectDeletionAction projectDeletionAction;
+ @Mock private ProjectsCollection projectsCollection;
+ @Mock private CurrentUser currentUser;
+ @Mock private Provider<CurrentUser> userProvider;
+ @Mock private ProjectResource projectResource;
+ @Mock private ServletOutputStream outputStream;
+ @Mock private PrintWriter printWriter;
+ private final String PLUGIN_NAME = "pull-replication";
+ private final String PROJECT_NAME = "some-project";
+ private final String PROJECT_NAME_GIT = "some-project.git";
+ private final String FETCH_URI =
+ String.format("any-prefix/projects/%s/%s~fetch", PROJECT_NAME, PLUGIN_NAME);
+ private final String APPLY_OBJECT_URI =
+ String.format("any-prefix/projects/%s/%s~apply-object", PROJECT_NAME, PLUGIN_NAME);
+ private final String APPLY_OBJECTS_URI =
+ String.format("any-prefix/projects/%s/%s~apply-objects", PROJECT_NAME, PLUGIN_NAME);
+ private final String HEAD_URI = String.format("any-prefix/projects/%s/HEAD", PROJECT_NAME);
+ private final String DELETE_PROJECT_URI =
+ String.format("any-prefix/projects/%s/%s~delete-project", PROJECT_NAME, PLUGIN_NAME);
+ private final String INIT_PROJECT_URI =
+ String.format("any-prefix/%s/init-project/%s", PLUGIN_NAME, PROJECT_NAME_GIT);
+
+ private final Response OK_RESPONSE = Response.ok();
+
+ private PullReplicationFilter createPullReplicationFilter() {
+ return new PullReplicationFilter(
+ fetchAction,
+ applyObjectAction,
+ applyObjectsAction,
+ projectInitializationAction,
+ updateHEADAction,
+ projectDeletionAction,
+ projectsCollection,
+ userProvider,
+ PLUGIN_NAME);
+ }
+
+ private void defineBehaviours(byte[] payload, String uri) throws Exception {
+ when(request.getRequestURI()).thenReturn(uri);
+ when(userProvider.get()).thenReturn(currentUser);
+ when(currentUser.isIdentifiedUser()).thenReturn(true);
+ InputStream is = new ByteArrayInputStream(payload);
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
+ when(request.getReader()).thenReturn(bufferedReader);
+ when(projectsCollection.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(PROJECT_NAME)))
+ .thenReturn(projectResource);
+ when(response.getWriter()).thenReturn(printWriter);
+ }
+
+ private void verifyBehaviours() throws Exception {
+ verify(request, atLeastOnce()).getRequestURI();
+ verify(userProvider).get();
+ verify(currentUser).isIdentifiedUser();
+ verify(request).getReader();
+ verify(projectsCollection).parse(TopLevelResource.INSTANCE, IdString.fromDecoded(PROJECT_NAME));
+ verify(response).getWriter();
+ verify(response).setContentType("application/json");
+ verify(response).setStatus(HttpServletResponse.SC_OK);
+ }
+
+ @Test
+ public void shouldFilterFetchAction() throws Exception {
+ byte[] payloadFetch =
+ ("{"
+ + "\"label\":\"Replication\", "
+ + "\"ref_name\": \"refs/heads/master\", "
+ + "\"async\":false"
+ + "}")
+ .getBytes(StandardCharsets.UTF_8);
+
+ defineBehaviours(payloadFetch, FETCH_URI);
+ when(fetchAction.apply(any(), any())).thenReturn(OK_RESPONSE);
+
+ PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verifyBehaviours();
+ verify(fetchAction).apply(eq(projectResource), any());
+ }
+
+ @Test
+ public void shouldFilterApplyObjectAction() throws Exception {
+
+ byte[] payloadApplyObject =
+ ("{\"label\":\"Replication\",\"ref_name\":\"refs/heads/master\","
+ + "\"revision_data\":{"
+ + "\"commit_object\":{\"type\":1,\"content\":\"some-content\"},"
+ + "\"tree_object\":{\"type\":2,\"content\":\"some-content\"},"
+ + "\"blobs\":[]}"
+ + "}")
+ .getBytes(StandardCharsets.UTF_8);
+
+ defineBehaviours(payloadApplyObject, APPLY_OBJECT_URI);
+
+ when(applyObjectAction.apply(any(), any())).thenReturn(OK_RESPONSE);
+
+ PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verifyBehaviours();
+ verify(applyObjectAction).apply(eq(projectResource), any());
+ }
+
+ @Test
+ public void shouldFilterApplyObjectsAction() throws Exception {
+
+ byte[] payloadApplyObjects =
+ ("{\"label\":\"Replication\",\"ref_name\":\"refs/heads/master\","
+ + "\"revisions_data\":[{"
+ + "\"commit_object\":{\"type\":1,\"content\":\"some-content\"},"
+ + "\"tree_object\":{\"type\":2,\"content\":\"some-content\"},"
+ + "\"blobs\":[]}]}")
+ .getBytes(StandardCharsets.UTF_8);
+
+ defineBehaviours(payloadApplyObjects, APPLY_OBJECTS_URI);
+
+ when(applyObjectsAction.apply(any(), any())).thenReturn(OK_RESPONSE);
+
+ PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verifyBehaviours();
+ verify(applyObjectsAction).apply(eq(projectResource), any());
+ }
+
+ @Test
+ public void shouldFilterProjectInitializationAction() throws Exception {
+
+ when(request.getRequestURI()).thenReturn(INIT_PROJECT_URI);
+ when(request.getHeader(ACCEPT)).thenReturn(MediaType.PLAIN_TEXT_UTF_8.toString());
+ when(userProvider.get()).thenReturn(currentUser);
+ when(currentUser.isIdentifiedUser()).thenReturn(true);
+ when(projectInitializationAction.initProject(PROJECT_NAME_GIT)).thenReturn(true);
+ when(response.getWriter()).thenReturn(printWriter);
+
+ final PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verify(request, times(5)).getRequestURI();
+ verify(userProvider).get();
+ verify(currentUser).isIdentifiedUser();
+ verify(projectInitializationAction).initProject(eq(PROJECT_NAME_GIT));
+ verify(response).getWriter();
+ }
+
+ @Test
+ public void shouldFilterUpdateHEADAction() throws Exception {
+
+ byte[] payloadUpdateHead = "{\"ref\":\"some-ref\"}".getBytes(StandardCharsets.UTF_8);
+ defineBehaviours(payloadUpdateHead, HEAD_URI);
+ when(request.getMethod()).thenReturn("PUT");
+ when(updateHEADAction.apply(any(), any())).thenReturn(OK_RESPONSE);
+
+ final PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verifyBehaviours();
+ verify(updateHEADAction).apply(eq(projectResource), any());
+ }
+
+ @Test
+ public void shouldFilterProjectDeletionAction() throws Exception {
+ when(request.getRequestURI()).thenReturn(DELETE_PROJECT_URI);
+ when(request.getMethod()).thenReturn("DELETE");
+ when(userProvider.get()).thenReturn(currentUser);
+ when(currentUser.isIdentifiedUser()).thenReturn(true);
+ when(projectsCollection.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(PROJECT_NAME)))
+ .thenReturn(projectResource);
+ when(projectDeletionAction.apply(any(), any())).thenReturn(OK_RESPONSE);
+ when(response.getWriter()).thenReturn(printWriter);
+
+ final PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verify(request, times(7)).getRequestURI();
+ verify(userProvider).get();
+ verify(currentUser).isIdentifiedUser();
+ verify(projectsCollection).parse(TopLevelResource.INSTANCE, IdString.fromDecoded(PROJECT_NAME));
+ verify(projectDeletionAction).apply(eq(projectResource), any());
+ verify(response).getWriter();
+ verify(response).setContentType("application/json");
+ verify(response).setStatus(OK_RESPONSE.statusCode());
+ }
+
+ @Test
+ public void shouldGoNextInChainWhenUriDoesNotMatch() throws Exception {
+ when(request.getRequestURI()).thenReturn("any-url");
+ final PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+ verify(filterChain).doFilter(request, response);
+ }
+
+ @Test
+ public void shouldBe404WhenJsonIsMalformed() throws Exception {
+ byte[] payloadMalformedJson = "some-json-malformed".getBytes(StandardCharsets.UTF_8);
+ InputStream is = new ByteArrayInputStream(payloadMalformedJson);
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
+ when(request.getRequestURI()).thenReturn(FETCH_URI);
+ when(request.getReader()).thenReturn(bufferedReader);
+ when(userProvider.get()).thenReturn(currentUser);
+ when(currentUser.isIdentifiedUser()).thenReturn(true);
+ when(response.getOutputStream()).thenReturn(outputStream);
+
+ PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verify(response).setStatus(HttpServletResponse.SC_BAD_REQUEST);
+ }
+
+ @Test
+ public void shouldBe500WhenProjectCannotBeInitiated() throws Exception {
+ when(request.getRequestURI()).thenReturn(INIT_PROJECT_URI);
+ when(request.getHeader(ACCEPT)).thenReturn(MediaType.PLAIN_TEXT_UTF_8.toString());
+ when(userProvider.get()).thenReturn(currentUser);
+ when(currentUser.isIdentifiedUser()).thenReturn(true);
+ when(projectInitializationAction.initProject(PROJECT_NAME_GIT)).thenReturn(false);
+ when(response.getOutputStream()).thenReturn(outputStream);
+
+ final PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ @Test
+ public void shouldBe500WhenResourceNotFound() throws Exception {
+ when(request.getRequestURI()).thenReturn(DELETE_PROJECT_URI);
+ when(request.getMethod()).thenReturn("DELETE");
+ when(userProvider.get()).thenReturn(currentUser);
+ when(currentUser.isIdentifiedUser()).thenReturn(true);
+ when(projectsCollection.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(PROJECT_NAME)))
+ .thenReturn(projectResource);
+ when(projectDeletionAction.apply(any(), any()))
+ .thenThrow(new ResourceNotFoundException("resource not found"));
+ when(response.getOutputStream()).thenReturn(outputStream);
+
+ final PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ @Test
+ public void shouldBe403WhenUserIsNotAuthorised() throws Exception {
+ byte[] payloadFetchAction =
+ ("{"
+ + "\"label\":\"Replication\", "
+ + "\"ref_name\": \"refs/heads/master\", "
+ + "\"async\":false"
+ + "}")
+ .getBytes(StandardCharsets.UTF_8);
+
+ defineBehaviours(payloadFetchAction, FETCH_URI);
+ when(fetchAction.apply(any(), any()))
+ .thenThrow(new AuthException("The user is not authorised"));
+ when(response.getOutputStream()).thenReturn(outputStream);
+
+ PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verify(response).setStatus(HttpServletResponse.SC_FORBIDDEN);
+ }
+
+ @Test
+ public void shouldBe422WhenEntityCannotBeProcessed() throws Exception {
+ byte[] payloadFetchAction =
+ ("{"
+ + "\"label\":\"Replication\", "
+ + "\"ref_name\": \"refs/heads/master\", "
+ + "\"async\":false"
+ + "}")
+ .getBytes(StandardCharsets.UTF_8);
+
+ defineBehaviours(payloadFetchAction, FETCH_URI);
+ when(fetchAction.apply(any(), any()))
+ .thenThrow(new UnprocessableEntityException("Entity cannot be processed"));
+ when(response.getOutputStream()).thenReturn(outputStream);
+
+ PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verify(response).setStatus(SC_UNPROCESSABLE_ENTITY);
+ }
+
+ @Test
+ public void shouldBe409WhenThereIsResourceConflict() throws Exception {
+ when(request.getRequestURI()).thenReturn(DELETE_PROJECT_URI);
+ when(request.getMethod()).thenReturn("DELETE");
+ when(userProvider.get()).thenReturn(currentUser);
+ when(currentUser.isIdentifiedUser()).thenReturn(true);
+ when(projectsCollection.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(PROJECT_NAME)))
+ .thenReturn(projectResource);
+
+ when(projectDeletionAction.apply(any(), any()))
+ .thenThrow(new ResourceConflictException("Resource conflict"));
+ when(response.getOutputStream()).thenReturn(outputStream);
+
+ PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verify(response).setStatus(SC_CONFLICT);
+ }
+
+ @Test
+ public void shouldBe400WhenProjectNameIsNotPresentInURL() throws Exception {
+ when(request.getRequestURI())
+ .thenReturn(String.format("any-prefix/projects/%s~delete-project", PLUGIN_NAME));
+ when(request.getMethod()).thenReturn("DELETE");
+ when(userProvider.get()).thenReturn(currentUser);
+ when(currentUser.isIdentifiedUser()).thenReturn(true);
+ when(response.getOutputStream()).thenReturn(outputStream);
+
+ PullReplicationFilter pullReplicationFilter = createPullReplicationFilter();
+ pullReplicationFilter.doFilter(request, response, filterChain);
+
+ verify(response).setStatus(HttpServletResponse.SC_BAD_REQUEST);
+ }
+}