Fix unit test for import resource

This commit is contained in:
René Pfeuffer
2021-02-23 10:22:45 +01:00
parent 51ed267ada
commit b7fa1d77b2
7 changed files with 425 additions and 168 deletions

View File

@@ -49,7 +49,6 @@ import sonia.scm.importexport.FullScmRepositoryImporter;
import sonia.scm.importexport.RepositoryImportExportEncryption;
import sonia.scm.importexport.RepositoryImportLoggerFactory;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryPermissions;
import sonia.scm.repository.api.Command;
import sonia.scm.repository.api.ImportFailedException;
import sonia.scm.web.VndMediaType;
@@ -206,7 +205,6 @@ public class RepositoryImportResource {
@Pattern(regexp = "\\w{1,10}") @PathParam("type") String type,
MultipartFormDataInput input,
@QueryParam("compressed") @DefaultValue("false") boolean compressed) {
RepositoryPermissions.create().check();
Repository repository = doImportFromBundle(type, input, compressed);
return Response.created(URI.create(resourceLinks.repository().self(repository.getNamespace(), repository.getName()))).build();
@@ -256,7 +254,6 @@ public class RepositoryImportResource {
public Response importFullRepository(@Context UriInfo uriInfo,
@Pattern(regexp = "\\w{1,10}") @PathParam("type") String type,
MultipartFormDataInput input) {
RepositoryPermissions.create().check();
Repository createdRepository = importFullRepositoryFromInput(input);
return Response.created(URI.create(resourceLinks.repository().self(createdRepository.getNamespace(), createdRepository.getName()))).build();
}

View File

@@ -34,6 +34,7 @@ import sonia.scm.ContextEntry;
import sonia.scm.event.ScmEventBus;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.repository.RepositoryPermissions;
import sonia.scm.repository.api.ImportFailedException;
import javax.inject.Inject;
@@ -74,6 +75,7 @@ public class FullScmRepositoryImporter {
}
public Repository importFromStream(Repository repository, InputStream inputStream, String password) {
RepositoryPermissions.create().check();
RepositoryImportLogger logger = startLogger(repository);
try {
if (inputStream.available() > 0) {

View File

@@ -0,0 +1,84 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.api.v2.resources;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jboss.resteasy.mock.MockHttpRequest;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.util.Map;
import java.util.UUID;
class MultiPartRequestBuilder {
/**
* This method is a slightly adapted copy of Lin Zaho's gist at https://gist.github.com/lin-zhao/9985191
*/
static void multipartRequest(MockHttpRequest request, Map<String, InputStream> files, RepositoryDto repository) throws IOException {
String boundary = UUID.randomUUID().toString();
request.contentType("multipart/form-data; boundary=" + boundary);
//Make sure this is deleted in afterTest()
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (OutputStreamWriter formWriter = new OutputStreamWriter(buffer)) {
formWriter.append("--").append(boundary);
for (Map.Entry<String, InputStream> entry : files.entrySet()) {
formWriter.append("\n");
formWriter.append(String.format("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"",
entry.getKey(), entry.getKey())).append("\n");
formWriter.append("Content-Type: application/octet-stream").append("\n\n");
InputStream stream = entry.getValue();
int b = stream.read();
while (b >= 0) {
formWriter.write(b);
b = stream.read();
}
stream.close();
formWriter.append("\n").append("--").append(boundary);
}
if (repository != null) {
formWriter.append("\n");
formWriter.append("Content-Disposition: form-data; name=\"repository\"").append("\n\n");
StringWriter repositoryWriter = new StringWriter();
new JsonFactory().createGenerator(repositoryWriter).setCodec(new ObjectMapper()).writeObject(repository);
formWriter.append(repositoryWriter.getBuffer().toString()).append("\n");
formWriter.append("--").append(boundary);
}
formWriter.append("--");
formWriter.flush();
}
request.setInputStream(new ByteArrayInputStream(buffer.toByteArray()));
}
}

View File

@@ -0,0 +1,304 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.api.v2.resources;
import com.google.common.io.Resources;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.api.v2.resources.RepositoryImportResource.RepositoryImportFromFileDto;
import sonia.scm.importexport.FromBundleImporter;
import sonia.scm.importexport.FromUrlImporter;
import sonia.scm.importexport.FullScmRepositoryImporter;
import sonia.scm.importexport.RepositoryImportExportEncryption;
import sonia.scm.importexport.RepositoryImportLoggerFactory;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryTestData;
import sonia.scm.web.RestDispatcher;
import sonia.scm.web.VndMediaType;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.singletonMap;
import static javax.servlet.http.HttpServletResponse.SC_CREATED;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SuppressWarnings("UnstableApiUsage")
@RunWith(MockitoJUnitRunner.class)
public class RepositoryImportResourceTest extends RepositoryTestBase {
private final RestDispatcher dispatcher = new RestDispatcher();
@Mock
private FullScmRepositoryImporter fullScmRepositoryImporter;
@Mock
private FromUrlImporter fromUrlImporter;
@Mock
private FromBundleImporter fromBundleImporter;
@Mock
private RepositoryImportLoggerFactory importLoggerFactory;
@Mock
private RepositoryImportExportEncryption repositoryImportExportEncryption;
@Captor
private ArgumentCaptor<FromUrlImporter.RepositoryImportParameters> parametersCaptor;
@Captor
private ArgumentCaptor<Repository> repositoryCaptor;
private final URI baseUri = URI.create("/");
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
@InjectMocks
private RepositoryDtoToRepositoryMapperImpl dtoToRepositoryMapper;
private final MockHttpResponse response = new MockHttpResponse();
@Before
public void prepareEnvironment() {
super.repositoryImportResource = new RepositoryImportResource(dtoToRepositoryMapper, resourceLinks, fullScmRepositoryImporter, new RepositoryImportDtoToRepositoryImportParametersMapperImpl(), repositoryImportExportEncryption, fromUrlImporter, fromBundleImporter, importLoggerFactory);
dispatcher.addSingletonResource(getRepositoryRootResource());
}
@Test
public void shouldImportRepositoryFromUrl() throws Exception {
when(fromUrlImporter.importFromUrl(parametersCaptor.capture(), repositoryCaptor.capture()))
.thenReturn(RepositoryTestData.createHeartOfGold());
URL url = Resources.getResource("sonia/scm/api/v2/import-repo.json");
byte[] importRequest = Resources.toByteArray(url);
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "import/git/url")
.contentType(VndMediaType.REPOSITORY)
.content(importRequest);
dispatcher.invoke(request, response);
assertThat(response.getStatus()).isEqualTo(SC_CREATED);
assertThat(response.getOutputHeaders().get("Location")).asString().contains("/v2/repositories/hitchhiker/HeartOfGold");
assertThat(parametersCaptor.getValue().getImportUrl()).isEqualTo("https://scm-manager-org/scm/repo/secret/puzzle42");
assertThat(parametersCaptor.getValue().getUsername()).isNull();
assertThat(parametersCaptor.getValue().getPassword()).isNull();
assertThat(repositoryCaptor.getValue().getName()).isEqualTo("HeartOfGold");
assertThat(repositoryCaptor.getValue().getNamespace()).isEqualTo("hitchhiker");
}
@Test
public void shouldImportRepositoryFromUrlWithCredentials() throws Exception {
when(fromUrlImporter.importFromUrl(parametersCaptor.capture(), repositoryCaptor.capture()))
.thenReturn(RepositoryTestData.createHeartOfGold());
URL url = Resources.getResource("sonia/scm/api/v2/import-repo-with-credentials.json");
byte[] importRequest = Resources.toByteArray(url);
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "import/git/url")
.contentType(VndMediaType.REPOSITORY)
.content(importRequest);
dispatcher.invoke(request, response);
assertThat(response.getStatus()).isEqualTo(SC_CREATED);
assertThat(parametersCaptor.getValue().getUsername()).isEqualTo("trillian");
assertThat(parametersCaptor.getValue().getPassword()).isEqualTo("secret");
}
@Test
public void shouldFailOnImportFromUrlWithDifferentTypes() throws Exception {
URL url = Resources.getResource("sonia/scm/api/v2/import-repo.json");
byte[] importRequest = Resources.toByteArray(url);
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "import/svn/url")
.contentType(VndMediaType.REPOSITORY)
.content(importRequest);
dispatcher.invoke(request, response);
assertThat(response.getStatus()).isNotEqualTo(SC_CREATED);
verify(fromUrlImporter, never()).importFromUrl(any(), any());
}
@Nested
class WithCorrectBundle {
@BeforeEach
void mockImporter() {
when(
fromBundleImporter.importFromBundle(
eq(false),
argThat(argument -> streamHasContent(argument, "svn-dump")),
argThat(repository -> repository.getName().equals("HeartOfGold"))
)
).thenReturn(RepositoryTestData.createHeartOfGold());
}
@Test
public void shouldImportRepositoryFromBundle() throws Exception {
RepositoryImportFromFileDto importDto = createBasicImportDto();
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "import/svn/bundle");
MultiPartRequestBuilder.multipartRequest(request, singletonMap("bundle", new ByteArrayInputStream("svn-dump".getBytes(UTF_8))), importDto);
dispatcher.invoke(request, response);
assertThat(response.getStatus()).isEqualTo(SC_CREATED);
assertThat(response.getOutputHeaders().get("Location")).asString().contains("/v2/repositories/hitchhiker/HeartOfGold");
verify(repositoryImportExportEncryption, never()).decrypt(any(), any());
}
@Test
public void shouldImportRepositoryFromEncryptedBundle() throws Exception {
when(repositoryImportExportEncryption.decrypt(any(), eq("hgt2g")))
.thenAnswer(invocation -> invocation.getArgument(0));
RepositoryImportFromFileDto importDto = createBasicImportDto();
importDto.setPassword("hgt2g");
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "import/svn/bundle");
MultiPartRequestBuilder.multipartRequest(request, singletonMap("bundle", new ByteArrayInputStream("svn-dump".getBytes(UTF_8))), importDto);
dispatcher.invoke(request, response);
assertThat(response.getStatus()).isEqualTo(SC_CREATED);
assertThat(response.getOutputHeaders().get("Location")).asString().contains("/v2/repositories/hitchhiker/HeartOfGold");
}
private RepositoryImportFromFileDto createBasicImportDto() {
RepositoryImportFromFileDto importDto = new RepositoryImportFromFileDto();
importDto.setName("HeartOfGold");
importDto.setNamespace("hitchhiker");
importDto.setType("svn");
return importDto;
}
}
@Test
public void shouldFailOnImportFromBundleWithDifferentTypes() throws Exception {
RepositoryDto repositoryDto = new RepositoryDto();
repositoryDto.setName("HeartOfGold");
repositoryDto.setNamespace("hitchhiker");
repositoryDto.setType("svn");
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "import/git/bundle");
MultiPartRequestBuilder.multipartRequest(request, Collections.singletonMap("bundle", new ByteArrayInputStream("svn-dump".getBytes(StandardCharsets.UTF_8))), repositoryDto);
dispatcher.invoke(request, response);
assertThat(response.getStatus()).isNotEqualTo(SC_CREATED);
verify(fromBundleImporter, never()).importFromBundle(any(Boolean.class), any(InputStream.class), any(Repository.class));
}
@Test
public void shouldImportFullRepository() throws Exception {
when(
fullScmRepositoryImporter.importFromStream(
argThat(repository -> repository.getName().equals("HeartOfGold")),
argThat(argument -> streamHasContent(argument, "svn-dump")),
isNull()
)
).thenReturn(RepositoryTestData.createHeartOfGold());
RepositoryDto repositoryDto = new RepositoryDto();
repositoryDto.setName("HeartOfGold");
repositoryDto.setNamespace("hitchhiker");
repositoryDto.setType("svn");
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "import/svn/full");
MultiPartRequestBuilder.multipartRequest(request, singletonMap("bundle", new ByteArrayInputStream("svn-dump".getBytes(UTF_8))), repositoryDto);
dispatcher.invoke(request, response);
assertThat(response.getStatus()).isEqualTo(SC_CREATED);
assertThat(response.getOutputHeaders().get("Location")).asString().contains("/v2/repositories/hitchhiker/HeartOfGold");
}
@Test
public void shouldFindImportLog() throws Exception {
doAnswer(
invocation -> {
invocation.getArgument(1, OutputStream.class).write("some log".getBytes(UTF_8));
return null;
}
).when(importLoggerFactory).getLog(eq("42"), any(OutputStream.class));
MockHttpRequest request = MockHttpRequest
.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "import/log/42");
dispatcher.invoke(request, response);
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentAsString()).isEqualTo("some log");
}
private boolean streamHasContent(InputStream argument, String expectedContent) {
try {
byte[] data = new byte[expectedContent.length()];
argument.read(data);
return new String(data).equals(expectedContent);
} catch (IOException e) {
return false;
}
}
}

View File

@@ -24,8 +24,6 @@
package sonia.scm.api.v2.resources;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.collect.ImmutableSet;
@@ -37,14 +35,15 @@ import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.NotFoundException;
import sonia.scm.PageResult;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.event.ScmEventBus;
import sonia.scm.importexport.ExportFileExtensionResolver;
import sonia.scm.importexport.ExportService;
import sonia.scm.importexport.ExportStatus;
@@ -59,14 +58,11 @@ import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.NamespaceStrategy;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryHandler;
import sonia.scm.repository.RepositoryImportEvent;
import sonia.scm.repository.RepositoryInitializer;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.repository.RepositoryTestData;
import sonia.scm.repository.RepositoryType;
import sonia.scm.repository.api.BundleCommandBuilder;
import sonia.scm.repository.api.Command;
import sonia.scm.repository.api.ImportFailedException;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.user.User;
@@ -76,21 +72,14 @@ import sonia.scm.web.VndMediaType;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.time.Instant;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Predicate;
import static java.util.Collections.singletonList;
@@ -98,7 +87,6 @@ import static java.util.stream.Stream.of;
import static javax.servlet.http.HttpServletResponse.SC_ACCEPTED;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
import static javax.servlet.http.HttpServletResponse.SC_CREATED;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
import static javax.servlet.http.HttpServletResponse.SC_OK;
@@ -115,7 +103,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.openMocks;
@SubjectAware(
username = "trillian",
@@ -123,6 +110,7 @@ import static org.mockito.MockitoAnnotations.openMocks;
configuration = "classpath:sonia/scm/repository/shiro.ini"
)
@SuppressWarnings("UnstableApiUsage")
@RunWith(MockitoJUnitRunner.class)
public class RepositoryRootResourceTest extends RepositoryTestBase {
private static final String REALM = "AdminRealm";
@@ -151,8 +139,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
@Mock
private Set<NamespaceStrategy> strategies;
@Mock
private ScmEventBus eventBus;
@Mock
private FullScmRepositoryExporter fullScmRepositoryExporter;
@Mock
private RepositoryExportInformationToDtoMapper exportInformationToDtoMapper;
@@ -182,9 +168,10 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
@InjectMocks
private RepositoryDtoToRepositoryMapperImpl dtoToRepositoryMapper;
private final MockHttpResponse response = new MockHttpResponse();
@Before
public void prepareEnvironment() throws IOException {
openMocks(this);
super.repositoryToDtoMapper = repositoryToDtoMapper;
super.dtoToRepositoryMapper = dtoToRepositoryMapper;
super.manager = repositoryManager;
@@ -194,8 +181,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
super.repositoryExportResource = new RepositoryExportResource(repositoryManager, serviceFactory, fullScmRepositoryExporter, repositoryImportExportEncryption, exportService, exportInformationToDtoMapper, fileExtensionResolver, resourceLinks);
dispatcher.addSingletonResource(getRepositoryRootResource());
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
when(scmPathInfoStore.get()).thenReturn(uriInfo);
when(uriInfo.getApiRestUri()).thenReturn(URI.create("/x/y"));
doReturn(ImmutableSet.of(new CustomNamespaceStrategy()).iterator()).when(strategies).iterator();
SimplePrincipalCollection trillian = new SimplePrincipalCollection("trillian", REALM);
trillian.add(new User("trillian"), REALM);
@@ -213,7 +198,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
createRepository("space", "repo");
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/other");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -226,7 +210,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
when(configuration.getNamespaceStrategy()).thenReturn("CustomNamespaceStrategy");
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -241,7 +224,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
when(configuration.getNamespaceStrategy()).thenReturn("CustomNamespaceStrategy");
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -256,7 +238,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
when(configuration.getNamespaceStrategy()).thenReturn("CustomNamespaceStrategy");
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "?q=Rep");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -273,7 +254,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
when(configuration.getNamespaceStrategy()).thenReturn("CustomNamespaceStrategy");
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -290,7 +270,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
when(configuration.getNamespaceStrategy()).thenReturn("CustomNamespaceStrategy");
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space?q=Rep");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -310,7 +289,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
.put("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo")
.contentType(VndMediaType.REPOSITORY)
.content(repository);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -328,7 +306,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
.put("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo")
.contentType(VndMediaType.REPOSITORY)
.content(repository);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -347,7 +324,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
.put("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo")
.contentType(VndMediaType.REPOSITORY)
.content(repository);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -367,7 +343,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
.put("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "wrong/repo")
.contentType(VndMediaType.REPOSITORY)
.content(repository);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -380,7 +355,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
createRepository("space", "repo");
MockHttpRequest request = MockHttpRequest.delete("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -403,7 +377,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2)
.contentType(VndMediaType.REPOSITORY)
.content(repositoryJson);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -424,7 +397,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "?initialize=true")
.contentType(VndMediaType.REPOSITORY)
.content(repositoryJson);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -454,7 +426,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2)
.contentType(VndMediaType.REPOSITORY)
.content(repositoryJson);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -473,7 +444,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
when(configuration.getNamespaceStrategy()).thenReturn("CustomNamespaceStrategy");
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -495,7 +465,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/rename")
.contentType(VndMediaType.REPOSITORY)
.content(repository);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -503,82 +472,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
verify(repositoryManager).rename(repository1, "space", "x");
}
@Test
public void shouldImportRepositoryFromUrl() throws URISyntaxException, IOException {
ArgumentCaptor<RepositoryImportEvent> captor = ArgumentCaptor.forClass(RepositoryImportEvent.class);
when(manager.getHandler("git")).thenReturn(repositoryHandler);
when(repositoryHandler.getType()).thenReturn(new RepositoryType("git", "git", ImmutableSet.of(Command.PULL)));
when(manager.create(any(Repository.class), any())).thenReturn(RepositoryTestData.create42Puzzle());
URL url = Resources.getResource("sonia/scm/api/v2/import-repo.json");
byte[] importRequest = Resources.toByteArray(url);
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "import/git/url")
.contentType(VndMediaType.REPOSITORY)
.content(importRequest);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(SC_CREATED, response.getStatus());
verify(eventBus).post(captor.capture());
assertThat(captor.getValue().isFailed()).isFalse();
}
@Test
public void shouldFailOnImportRepositoryFromUrl() throws URISyntaxException, IOException {
ArgumentCaptor<RepositoryImportEvent> captor = ArgumentCaptor.forClass(RepositoryImportEvent.class);
when(manager.getHandler("git")).thenReturn(repositoryHandler);
when(repositoryHandler.getType()).thenReturn(new RepositoryType("git", "git", ImmutableSet.of(Command.PULL)));
doThrow(ImportFailedException.class).when(manager).create(any(Repository.class), any());
URL url = Resources.getResource("sonia/scm/api/v2/import-repo.json");
byte[] importRequest = Resources.toByteArray(url);
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "import/git/url")
.contentType(VndMediaType.REPOSITORY)
.content(importRequest);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(500, response.getStatus());
verify(eventBus).post(captor.capture());
assertThat(captor.getValue().isFailed()).isTrue();
}
@Test
public void shouldThrowFailedEventOnImportRepositoryFromBundle() throws IOException, URISyntaxException {
when(manager.getHandler("svn")).thenReturn(repositoryHandler);
when(repositoryHandler.getType()).thenReturn(new RepositoryType("svn", "svn", ImmutableSet.of(Command.UNBUNDLE)));
doThrow(ImportFailedException.class).when(repositoryManager).create(any(), any());
RepositoryDto repositoryDto = new RepositoryDto();
repositoryDto.setName("HeartOfGold");
repositoryDto.setNamespace("hitchhiker");
repositoryDto.setType("svn");
URL dumpUrl = Resources.getResource("sonia/scm/api/v2/svn.dump");
byte[] svnDump = Resources.toByteArray(dumpUrl);
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "import/svn/bundle");
MockHttpResponse response = new MockHttpResponse();
multipartRequest(request, Collections.singletonMap("bundle", new ByteArrayInputStream(svnDump)), repositoryDto);
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
ArgumentCaptor<RepositoryImportEvent> event = ArgumentCaptor.forClass(RepositoryImportEvent.class);
verify(eventBus).post(event.capture());
assertTrue(event.getValue().isFailed());
}
@Test
public void shouldMarkRepositoryAsArchived() throws Exception {
String namespace = "space";
@@ -589,7 +482,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/archive")
.content(new byte[]{});
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -608,7 +500,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/unarchive")
.content(new byte[]{});
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -630,7 +521,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
MockHttpRequest request = MockHttpRequest
.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/export/svn");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -653,7 +543,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
MockHttpRequest request = MockHttpRequest
.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/export/svn?compressed=true");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -670,12 +559,8 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
when(manager.get(new NamespaceAndName(namespace, name))).thenReturn(repository);
mockRepositoryHandler(ImmutableSet.of(Command.BUNDLE));
BundleCommandBuilder bundleCommandBuilder = mock(BundleCommandBuilder.class);
when(service.getBundleCommand()).thenReturn(bundleCommandBuilder);
MockHttpRequest request = MockHttpRequest
.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/export/full");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
@@ -891,7 +776,6 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
when(repositoryType.getSupportedCommands()).thenReturn(cmds);
}
private PageResult<Repository> createSingletonPageResult(Repository repository) {
return new PageResult<>(singletonList(repository), 0);
}
@@ -919,48 +803,4 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
when(repositoryManager.get(id)).thenReturn(repository);
return repository;
}
/**
* This method is a slightly adapted copy of Lin Zaho's gist at https://gist.github.com/lin-zhao/9985191
*/
private void multipartRequest(MockHttpRequest request, Map<String, InputStream> files, RepositoryDto repository) throws IOException {
String boundary = UUID.randomUUID().toString();
request.contentType("multipart/form-data; boundary=" + boundary);
//Make sure this is deleted in afterTest()
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (OutputStreamWriter formWriter = new OutputStreamWriter(buffer)) {
formWriter.append("--").append(boundary);
for (Map.Entry<String, InputStream> entry : files.entrySet()) {
formWriter.append("\n");
formWriter.append(String.format("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"",
entry.getKey(), entry.getKey())).append("\n");
formWriter.append("Content-Type: application/octet-stream").append("\n\n");
InputStream stream = entry.getValue();
int b = stream.read();
while (b >= 0) {
formWriter.write(b);
b = stream.read();
}
stream.close();
formWriter.append("\n").append("--").append(boundary);
}
if (repository != null) {
formWriter.append("\n");
formWriter.append("Content-Disposition: form-data; name=\"repository\"").append("\n\n");
StringWriter repositoryWriter = new StringWriter();
new JsonFactory().createGenerator(repositoryWriter).setCodec(new ObjectMapper()).writeObject(repository);
formWriter.append(repositoryWriter.getBuffer().toString()).append("\n");
formWriter.append("--").append(boundary);
}
formWriter.append("--");
formWriter.flush();
}
request.setInputStream(new ByteArrayInputStream(buffer.toByteArray()));
}
}

View File

@@ -25,6 +25,9 @@
package sonia.scm.importexport;
import com.google.common.io.Resources;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -64,6 +67,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -98,6 +102,8 @@ class FullScmRepositoryImporterTest {
private RepositoryImportLogger logger;
@Mock
private RepositoryImportLoggerFactory loggerFactory;
@Mock
private Subject subject;
@InjectMocks
private EnvironmentCheckStep environmentCheckStep;
@@ -140,6 +146,16 @@ class FullScmRepositoryImporterTest {
lenient().when(loggerFactory.createLogger()).thenReturn(logger);
}
@BeforeEach
void initSubject() {
ThreadContext.bind(subject);
}
@BeforeEach
void cleanupSubject() {
ThreadContext.unbindSubject();
}
@Test
void shouldNotImportRepositoryIfFileNotExists(@TempDir Path temp) throws IOException {
Path emptyFile = temp.resolve("empty");
@@ -162,6 +178,18 @@ class FullScmRepositoryImporterTest {
);
}
@Test
void shouldNotImportRepositoryWithoutPermission() throws IOException {
doThrow(AuthorizationException.class).when(subject).checkPermission("repository:create");
InputStream stream = Resources.getResource("sonia/scm/repository/import/scm-import.tar.gz").openStream();
assertThrows(AuthorizationException.class, () -> fullImporter.importFromStream(REPOSITORY, stream, null));
verify(storeImporter, never()).importFromTarArchive(any(Repository.class), any(InputStream.class), any(RepositoryImportLogger.class));
verify(repositoryManager, never()).modify(any());
}
@Nested
class WithValidEnvironment {
@@ -214,6 +242,7 @@ class FullScmRepositoryImporterTest {
assertThat(repository).isEqualTo(REPOSITORY);
verify(storeImporter).importFromTarArchive(eq(REPOSITORY), any(InputStream.class), eq(logger));
verify(repositoryManager).create(REPOSITORY);
verify(repositoryManager).modify(REPOSITORY);
verify(unbundleCommandBuilder).unbundle((InputStream) argThat(argument -> argument.getClass().equals(NoneClosingInputStream.class)));
verify(workdirProvider, never()).createNewWorkdir(REPOSITORY.getId());

View File

@@ -1,6 +1,7 @@
{
"namespace": "hitchhiker",
"name": "HeartOfGold",
"type": "git",
"importUrl": "https://scm-manager-org/scm/repo/secret/puzzle42",
"username": "trillian",
"password": "secret"