create RepositoryInitializer which can be used to create new files in the initial commit on a new repository

This commit is contained in:
Eduard Heimbuch
2020-01-20 15:19:51 +01:00
parent af866f2ae8
commit f44d17d640
10 changed files with 494 additions and 13 deletions

View File

@@ -18,6 +18,7 @@ import org.mockito.Mock;
import sonia.scm.PageResult;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryInitializer;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
@@ -76,6 +77,8 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
private ScmPathInfoStore scmPathInfoStore;
@Mock
private ScmPathInfo uriInfo;
@Mock
private RepositoryInitializer repositoryInitializer;
@Captor
private ArgumentCaptor<Predicate<Repository>> filterCaptor;
@@ -95,7 +98,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
super.dtoToRepositoryMapper = dtoToRepositoryMapper;
super.manager = repositoryManager;
RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper = new RepositoryCollectionToDtoMapper(repositoryToDtoMapper, resourceLinks);
super.repositoryCollectionResource = Providers.of(new RepositoryCollectionResource(repositoryManager, repositoryCollectionToDtoMapper, dtoToRepositoryMapper, resourceLinks));
super.repositoryCollectionResource = Providers.of(new RepositoryCollectionResource(repositoryManager, repositoryCollectionToDtoMapper, dtoToRepositoryMapper, resourceLinks, repositoryInitializer));
dispatcher.addSingletonResource(getRepositoryRootResource());
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
when(scmPathInfoStore.get()).thenReturn(uriInfo);
@@ -288,6 +291,32 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
assertEquals(HttpServletResponse.SC_CREATED, response.getStatus());
assertEquals("/v2/repositories/otherspace/repo", response.getOutputHeaders().get("Location").get(0).toString());
verify(repositoryManager).create(any(Repository.class));
verify(repositoryInitializer, never()).initialize(any(Repository.class));
}
@Test
public void shouldCreateNewRepositoryAndInitialize() throws Exception {
when(repositoryManager.create(any())).thenAnswer(invocation -> invocation.getArgument(0));
URL url = Resources.getResource("sonia/scm/api/v2/repository-test-update.json");
byte[] repositoryJson = Resources.toByteArray(url);
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "?initialize=true")
.contentType(VndMediaType.REPOSITORY)
.content(repositoryJson);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(HttpServletResponse.SC_CREATED, response.getStatus());
ArgumentCaptor<Repository> captor = ArgumentCaptor.forClass(Repository.class);
verify(repositoryInitializer).initialize(captor.capture());
Repository repository = captor.getValue();
assertEquals("space", repository.getNamespace());
assertEquals("repo", repository.getName());
}
@Test

View File

@@ -0,0 +1,52 @@
package sonia.scm.repository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ReadmeRepositoryContentInitializerTest {
@Mock
private RepositoryContentInitializer.InitializerContext context;
@Mock
private RepositoryContentInitializer.CreateFile createFile;
private Repository repository;
private ReadmeRepositoryContentInitializer initializer = new ReadmeRepositoryContentInitializer();
@BeforeEach
void setUpContext() {
repository = RepositoryTestData.createHeartOfGold("hg");
when(context.getRepository()).thenReturn(repository);
when(context.create("README.md")).thenReturn(createFile);
}
@Test
void shouldCreateReadme() throws IOException {
initializer.initialize(context);
verify(createFile).from("# HeartOfGold\n\n" + repository.getDescription());
}
@Test
void shouldCreateReadmeWithoutDescription() throws IOException {
repository.setDescription(null);
initializer.initialize(context);
verify(createFile).from("# HeartOfGold");
}
}

View File

@@ -0,0 +1,181 @@
package sonia.scm.repository;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteSource;
import com.google.common.io.ByteStreams;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.Priority;
import sonia.scm.repository.api.ModifyCommandBuilder;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class RepositoryInitializerTest {
@Mock
private RepositoryServiceFactory repositoryServiceFactory;
@Mock
private RepositoryService repositoryService;
@Mock(answer = Answers.RETURNS_SELF)
private ModifyCommandBuilder modifyCommand;
private final Repository repository = RepositoryTestData.createHeartOfGold("git");
@BeforeEach
void setUpModifyCommand() {
when(repositoryServiceFactory.create(repository)).thenReturn(repositoryService);
when(repositoryService.getModifyCommand()).thenReturn(modifyCommand);
}
@Test
void shouldCallRepositoryContentInitializer() throws IOException {
ModifyCommandBuilder.WithOverwriteFlagContentLoader readmeContentLoader = mockContentLoader("README.md");
ModifyCommandBuilder.WithOverwriteFlagContentLoader licenseContentLoader = mockContentLoader("LICENSE.txt");
Set<RepositoryContentInitializer> repositoryContentInitializers = ImmutableSet.of(
new ReadmeContentInitializer(),
new LicenseContentInitializer()
);
RepositoryInitializer initializer = new RepositoryInitializer(repositoryServiceFactory, repositoryContentInitializers);
initializer.initialize(repository);
verifyFileCreation(readmeContentLoader, "# HeartOfGold");
verifyFileCreation(licenseContentLoader, "MIT");
verify(modifyCommand).setCommitMessage("initialize repository");
verify(modifyCommand).execute();
verify(repositoryService).close();
}
@Test
void shouldCallRepositoryContentInitializerWithInputStream() throws IOException {
ModifyCommandBuilder.WithOverwriteFlagContentLoader contentLoader = mockContentLoader("awesome.txt");
Set<RepositoryContentInitializer> repositoryContentInitializers = ImmutableSet.of(
new StreamingContentInitializer()
);
RepositoryInitializer initializer = new RepositoryInitializer(repositoryServiceFactory, repositoryContentInitializers);
initializer.initialize(repository);
verifyFileCreationWithStream(contentLoader, "awesome");
verify(modifyCommand).setCommitMessage("initialize repository");
verify(modifyCommand).execute();
verify(repositoryService).close();
}
@Test
void shouldRespectPriorityOrder() throws IOException {
ModifyCommandBuilder.WithOverwriteFlagContentLoader contentLoader = mock(ModifyCommandBuilder.WithOverwriteFlagContentLoader.class);
when(contentLoader.setOverwrite(true)).thenReturn(contentLoader);
when(modifyCommand.createFile(anyString())).thenReturn(contentLoader);
AtomicReference<String> reference = new AtomicReference<>();
when(contentLoader.withData(any(ByteSource.class))).thenAnswer(ic -> {
ByteSource byteSource = ic.getArgument(0);
reference.set(byteSource.asCharSource(StandardCharsets.UTF_8).read());
return modifyCommand;
});
Set<RepositoryContentInitializer> repositoryContentInitializers = ImmutableSet.of(
new LicenseContentInitializer(),
new ReadmeContentInitializer()
);
RepositoryInitializer initializer = new RepositoryInitializer(repositoryServiceFactory, repositoryContentInitializers);
initializer.initialize(repository);
assertThat(reference.get()).isEqualTo("MIT");
}
@Test
void shouldCloseRepositoryServiceOnException() throws IOException {
ModifyCommandBuilder.WithOverwriteFlagContentLoader contentLoader = mockContentLoader("README.md");
doThrow(new IOException("epic fail")).when(contentLoader).withData(any(ByteSource.class));
RepositoryInitializer initializer = new RepositoryInitializer(repositoryServiceFactory, ImmutableSet.of(new ReadmeContentInitializer()));
assertThrows(InternalRepositoryException.class, () -> initializer.initialize(repository));
verify(repositoryService).close();
}
private ModifyCommandBuilder.WithOverwriteFlagContentLoader mockContentLoader(String path) {
ModifyCommandBuilder.WithOverwriteFlagContentLoader contentLoader = mock(ModifyCommandBuilder.WithOverwriteFlagContentLoader.class);
doReturn(contentLoader).when(modifyCommand).createFile(path);
when(contentLoader.setOverwrite(true)).thenReturn(contentLoader);
return contentLoader;
}
private void verifyFileCreation(ModifyCommandBuilder.WithOverwriteFlagContentLoader contentLoader, String expectedContent) throws IOException {
ArgumentCaptor<ByteSource> captor = ArgumentCaptor.forClass(ByteSource.class);
verify(contentLoader).withData(captor.capture());
String content = captor.getValue().asCharSource(StandardCharsets.UTF_8).read();
assertThat(content).isEqualTo(expectedContent);
}
private void verifyFileCreationWithStream(ModifyCommandBuilder.WithOverwriteFlagContentLoader contentLoader, String expectedContent) throws IOException {
ArgumentCaptor<InputStream> captor = ArgumentCaptor.forClass(InputStream.class);
verify(contentLoader).withData(captor.capture());
byte[] bytes = ByteStreams.toByteArray(captor.getValue());
assertThat(new String(bytes, StandardCharsets.UTF_8)).isEqualTo(expectedContent);
}
@Priority(1)
private static class ReadmeContentInitializer implements RepositoryContentInitializer {
@Override
public void initialize(InitializerContext context) throws IOException {
Repository repository = context.getRepository();
context.create("README.md").from("# " + repository.getName());
}
}
@Priority(2)
private static class LicenseContentInitializer implements RepositoryContentInitializer {
@Override
public void initialize(InitializerContext context) throws IOException {
context.create("LICENSE.txt").from("MIT");
}
}
private static class StreamingContentInitializer implements RepositoryContentInitializer {
@Override
public void initialize(InitializerContext context) throws IOException {
context.create("awesome.txt").from(new ByteArrayInputStream("awesome".getBytes(StandardCharsets.UTF_8)));
}
}
}