From bb2b8450157e953ce61a8d2e288e824c341d80ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 13 Oct 2020 16:27:24 +0200 Subject: [PATCH] Use email util to compute fallback mail address --- .../repository/api/MergeCommandBuilder.java | 8 +- .../repository/api/ModifyCommandBuilder.java | 8 +- .../scm/repository/api/RepositoryService.java | 14 +- .../api/RepositoryServiceFactory.java | 170 +++++----- .../sonia/scm/repository/util/AuthorUtil.java | 13 +- .../api/ModifyCommandBuilderTest.java | 315 +++++++++++------- .../api/RepositoryServiceFactoryTest.java | 9 +- .../repository/api/RepositoryServiceTest.java | 16 +- 8 files changed, 315 insertions(+), 238 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java index 06a3b489a1..a751832d8f 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java @@ -30,6 +30,7 @@ import sonia.scm.repository.spi.MergeCommand; import sonia.scm.repository.spi.MergeCommandRequest; import sonia.scm.repository.spi.MergeConflictResult; import sonia.scm.repository.util.AuthorUtil; +import sonia.scm.user.EMail; import java.util.Set; @@ -75,11 +76,14 @@ import java.util.Set; */ public class MergeCommandBuilder { + private final EMail eMail; + private final MergeCommand mergeCommand; private final MergeCommandRequest request = new MergeCommandRequest(); - MergeCommandBuilder(MergeCommand mergeCommand) { + MergeCommandBuilder(MergeCommand mergeCommand, EMail eMail) { this.mergeCommand = mergeCommand; + this.eMail = eMail; } /** @@ -209,7 +213,7 @@ public class MergeCommandBuilder { * @return The result of the merge. */ public MergeCommandResult executeMerge() { - AuthorUtil.setAuthorIfNotAvailable(request); + AuthorUtil.setAuthorIfNotAvailable(request, eMail); Preconditions.checkArgument(request.isValid(), "revision to merge and target revision is required"); return mergeCommand.merge(request); } diff --git a/scm-core/src/main/java/sonia/scm/repository/api/ModifyCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/ModifyCommandBuilder.java index aeccb24b99..795de5d43d 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/ModifyCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/ModifyCommandBuilder.java @@ -35,6 +35,7 @@ import sonia.scm.repository.spi.ModifyCommand; import sonia.scm.repository.spi.ModifyCommandRequest; import sonia.scm.repository.util.AuthorUtil; import sonia.scm.repository.work.WorkdirProvider; +import sonia.scm.user.EMail; import sonia.scm.util.IOUtil; import java.io.File; @@ -72,14 +73,17 @@ public class ModifyCommandBuilder { private static final Logger LOG = LoggerFactory.getLogger(ModifyCommandBuilder.class); + private final EMail eMail; + private final ModifyCommand command; private final File workdir; private final ModifyCommandRequest request = new ModifyCommandRequest(); - ModifyCommandBuilder(ModifyCommand command, WorkdirProvider workdirProvider) { + ModifyCommandBuilder(ModifyCommand command, WorkdirProvider workdirProvider, EMail eMail) { this.command = command; this.workdir = workdirProvider.createNewWorkdir(); + this.eMail = eMail; } /** @@ -124,7 +128,7 @@ public class ModifyCommandBuilder { * @return The revision of the new commit. */ public String execute() { - AuthorUtil.setAuthorIfNotAvailable(request); + AuthorUtil.setAuthorIfNotAvailable(request, eMail); try { Preconditions.checkArgument(request.isValid(), "commit message and at least one request are required"); return command.execute(request); diff --git a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java index 15f84a4ecd..029dc6fa34 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java @@ -34,6 +34,7 @@ import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryPermissions; import sonia.scm.repository.spi.RepositoryServiceProvider; import sonia.scm.repository.work.WorkdirProvider; +import sonia.scm.user.EMail; import java.io.Closeable; import java.io.IOException; @@ -87,6 +88,7 @@ public final class RepositoryService implements Closeable { @SuppressWarnings("rawtypes") private final Set protocolProviders; private final WorkdirProvider workdirProvider; + private final EMail eMail; /** * Constructs a new {@link RepositoryService}. This constructor should only @@ -94,20 +96,22 @@ public final class RepositoryService implements Closeable { * @param cacheManager cache manager * @param provider implementation for {@link RepositoryServiceProvider} * @param repository the repository - * @param workdirProvider + * @param workdirProvider provider for workdirs + * @param eMail utility to compute email addresses if missing */ RepositoryService(CacheManager cacheManager, RepositoryServiceProvider provider, Repository repository, PreProcessorUtil preProcessorUtil, @SuppressWarnings("rawtypes") Set protocolProviders, - WorkdirProvider workdirProvider - ) { + WorkdirProvider workdirProvider, + EMail eMail) { this.cacheManager = cacheManager; this.provider = provider; this.repository = repository; this.preProcessorUtil = preProcessorUtil; this.protocolProviders = protocolProviders; this.workdirProvider = workdirProvider; + this.eMail = eMail; } /** @@ -397,7 +401,7 @@ public final class RepositoryService implements Closeable { LOG.debug("create merge command for repository {}", repository.getNamespaceAndName()); - return new MergeCommandBuilder(provider.getMergeCommand()); + return new MergeCommandBuilder(provider.getMergeCommand(), eMail); } /** @@ -418,7 +422,7 @@ public final class RepositoryService implements Closeable { LOG.debug("create modify command for repository {}", repository.getNamespaceAndName()); - return new ModifyCommandBuilder(provider.getModifyCommand(), workdirProvider); + return new ModifyCommandBuilder(provider.getModifyCommand(), workdirProvider, eMail); } /** diff --git a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java index b030d1cb0c..15cec9d210 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java @@ -40,9 +40,7 @@ import sonia.scm.HandlerEventType; import sonia.scm.NotFoundException; import sonia.scm.cache.Cache; import sonia.scm.cache.CacheManager; -import sonia.scm.config.ScmConfiguration; import sonia.scm.event.ScmEventBus; -import sonia.scm.repository.BranchCreatedEvent; import sonia.scm.repository.ClearRepositoryCacheEvent; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.PostReceiveRepositoryHookEvent; @@ -58,6 +56,7 @@ import sonia.scm.repository.work.WorkdirProvider; import sonia.scm.security.PublicKeyCreatedEvent; import sonia.scm.security.PublicKeyDeletedEvent; import sonia.scm.security.ScmSecurityException; +import sonia.scm.user.EMail; import java.util.Set; @@ -117,47 +116,9 @@ public final class RepositoryServiceFactory { //~--- constructors --------------------------------------------------------- - /** - * Constructs a new {@link RepositoryServiceFactory}. This constructor - * should not be called manually, it should only be used by the injection - * container. - * - * @param configuration configuration - * @param cacheManager cache manager - * @param repositoryManager manager for repositories - * @param resolvers a set of {@link RepositoryServiceResolver} - * @param preProcessorUtil helper object for pre processor handling - * @param protocolProviders - * @param workdirProvider - * @since 1.21 - */ - @Inject - public RepositoryServiceFactory(ScmConfiguration configuration, - CacheManager cacheManager, RepositoryManager repositoryManager, - Set resolvers, PreProcessorUtil preProcessorUtil, - @SuppressWarnings("rawtypes") Set protocolProviders, WorkdirProvider workdirProvider) { - this( - configuration, cacheManager, repositoryManager, resolvers, - preProcessorUtil, protocolProviders, workdirProvider, ScmEventBus.getInstance() - ); - } - - @VisibleForTesting - RepositoryServiceFactory(ScmConfiguration configuration, - CacheManager cacheManager, RepositoryManager repositoryManager, - Set resolvers, PreProcessorUtil preProcessorUtil, - Set protocolProviders, WorkdirProvider workdirProvider, - ScmEventBus eventBus) { - this.configuration = configuration; - this.cacheManager = cacheManager; - this.repositoryManager = repositoryManager; - this.resolvers = resolvers; - this.preProcessorUtil = preProcessorUtil; - this.protocolProviders = protocolProviders; - this.workdirProvider = workdirProvider; - - eventBus.register(new CacheClearHook(cacheManager)); - } + private final EMail eMail; + @SuppressWarnings("rawtypes") + private Set protocolProviders; //~--- methods -------------------------------------------------------------- @@ -216,47 +177,27 @@ public final class RepositoryServiceFactory { } /** - * Creates a new RepositoryService for the given repository. + * Constructs a new {@link RepositoryServiceFactory}. This constructor + * should not be called manually, it should only be used by the injection + * container. * - * @param repository the repository - * @return a implementation of RepositoryService - * for the given type of repository - * @throws RepositoryServiceNotFoundException if no repository service - * implementation for this kind of repository is available - * @throws NullPointerException if the repository is null - * @throws ScmSecurityException if current user has not read permissions - * for that repository + * @param cacheManager cache manager + * @param repositoryManager manager for repositories + * @param resolvers a set of {@link RepositoryServiceResolver} + * @param preProcessorUtil helper object for pre processor handling + * @param protocolProviders + * @param workdirProvider + * @since 1.21 */ - public RepositoryService create(Repository repository) { - Preconditions.checkNotNull(repository, "repository is required"); - - // check for read permissions of current user - RepositoryPermissions.read(repository).check(); - - RepositoryService service = null; - - for (RepositoryServiceResolver resolver : resolvers) { - RepositoryServiceProvider provider = resolver.resolve(repository); - - if (provider != null) { - if (logger.isDebugEnabled()) { - logger.debug( - "create new repository service for repository {} of type {}", - repository.getName(), repository.getType()); - } - - service = new RepositoryService(cacheManager, provider, repository, - preProcessorUtil, protocolProviders, workdirProvider); - - break; - } - } - - if (service == null) { - throw new RepositoryServiceNotFoundException(repository); - } - - return service; + @Inject + public RepositoryServiceFactory(CacheManager cacheManager, RepositoryManager repositoryManager, + Set resolvers, PreProcessorUtil preProcessorUtil, + @SuppressWarnings("rawtypes") Set protocolProviders, + WorkdirProvider workdirProvider, EMail eMail) { + this( + cacheManager, repositoryManager, resolvers, + preProcessorUtil, protocolProviders, workdirProvider, eMail, ScmEventBus.getInstance() + ); } //~--- inner classes -------------------------------------------------------- @@ -355,11 +296,6 @@ public final class RepositoryServiceFactory { */ private final CacheManager cacheManager; - /** - * scm-manager configuration - */ - private final ScmConfiguration configuration; - /** * pre processor util */ @@ -375,7 +311,65 @@ public final class RepositoryServiceFactory { */ private final Set resolvers; - private Set protocolProviders; + @VisibleForTesting + RepositoryServiceFactory(CacheManager cacheManager, RepositoryManager repositoryManager, + Set resolvers, PreProcessorUtil preProcessorUtil, + @SuppressWarnings("rawtypes") Set protocolProviders, + WorkdirProvider workdirProvider, EMail eMail, ScmEventBus eventBus) { + this.cacheManager = cacheManager; + this.repositoryManager = repositoryManager; + this.resolvers = resolvers; + this.preProcessorUtil = preProcessorUtil; + this.protocolProviders = protocolProviders; + this.workdirProvider = workdirProvider; + this.eMail = eMail; + + eventBus.register(new CacheClearHook(cacheManager)); + } private final WorkdirProvider workdirProvider; + + /** + * Creates a new RepositoryService for the given repository. + * + * @param repository the repository + * @return a implementation of RepositoryService + * for the given type of repository + * @throws RepositoryServiceNotFoundException if no repository service + * implementation for this kind of repository is available + * @throws NullPointerException if the repository is null + * @throws ScmSecurityException if current user has not read permissions + * for that repository + */ + public RepositoryService create(Repository repository) { + Preconditions.checkNotNull(repository, "repository is required"); + + // check for read permissions of current user + RepositoryPermissions.read(repository).check(); + + RepositoryService service = null; + + for (RepositoryServiceResolver resolver : resolvers) { + RepositoryServiceProvider provider = resolver.resolve(repository); + + if (provider != null) { + if (logger.isDebugEnabled()) { + logger.debug( + "create new repository service for repository {} of type {}", + repository.getName(), repository.getType()); + } + + service = new RepositoryService(cacheManager, provider, repository, + preProcessorUtil, protocolProviders, workdirProvider, eMail); + + break; + } + } + + if (service == null) { + throw new RepositoryServiceNotFoundException(repository); + } + + return service; + } } diff --git a/scm-core/src/main/java/sonia/scm/repository/util/AuthorUtil.java b/scm-core/src/main/java/sonia/scm/repository/util/AuthorUtil.java index fa2c598f03..5bc62fbecf 100644 --- a/scm-core/src/main/java/sonia/scm/repository/util/AuthorUtil.java +++ b/scm-core/src/main/java/sonia/scm/repository/util/AuthorUtil.java @@ -21,28 +21,29 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.util; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import sonia.scm.repository.Person; +import sonia.scm.user.EMail; import sonia.scm.user.User; public class AuthorUtil { - public static void setAuthorIfNotAvailable(CommandWithAuthor request) { + public static void setAuthorIfNotAvailable(CommandWithAuthor request, EMail eMail) { if (request.getAuthor() == null) { - request.setAuthor(createAuthorFromSubject()); + request.setAuthor(createAuthorFromSubject(eMail)); } } - private static Person createAuthorFromSubject() { + private static Person createAuthorFromSubject(EMail eMail) { Subject subject = SecurityUtils.getSubject(); User user = subject.getPrincipals().oneByType(User.class); String name = user.getDisplayName(); - String email = user.getMail(); - return new Person(name, email); + String mailAddress = eMail.createFallbackMailAddress(user); + return new Person(name, mailAddress); } public interface CommandWithAuthor { diff --git a/scm-core/src/test/java/sonia/scm/repository/api/ModifyCommandBuilderTest.java b/scm-core/src/test/java/sonia/scm/repository/api/ModifyCommandBuilderTest.java index 5583261e52..15a4b909f1 100644 --- a/scm-core/src/test/java/sonia/scm/repository/api/ModifyCommandBuilderTest.java +++ b/scm-core/src/test/java/sonia/scm/repository/api/ModifyCommandBuilderTest.java @@ -25,7 +25,12 @@ package sonia.scm.repository.api; import com.google.common.io.ByteSource; +import org.apache.shiro.subject.PrincipalCollection; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; @@ -34,10 +39,12 @@ import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.stubbing.Answer; -import sonia.scm.repository.Person; +import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.spi.ModifyCommand; import sonia.scm.repository.spi.ModifyCommandRequest; import sonia.scm.repository.work.WorkdirProvider; +import sonia.scm.user.EMail; +import sonia.scm.user.User; import java.io.ByteArrayInputStream; import java.io.File; @@ -50,15 +57,19 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ModifyCommandBuilderTest { + private static final ScmConfiguration SCM_CONFIGURATION = new ScmConfiguration(); + @Mock ModifyCommand command; @Mock @@ -73,7 +84,7 @@ class ModifyCommandBuilderTest { void initWorkdir(@TempDir Path temp) throws IOException { workdir = Files.createDirectory(temp.resolve("workdir")); lenient().when(workdirProvider.createNewWorkdir()).thenReturn(workdir.toFile()); - commandBuilder = new ModifyCommandBuilder(command, workdirProvider); + commandBuilder = new ModifyCommandBuilder(command, workdirProvider, new EMail(SCM_CONFIGURATION)); } @BeforeEach @@ -89,136 +100,27 @@ class ModifyCommandBuilderTest { ); } - @Test - void shouldReturnTargetRevisionFromCommit() { - String targetRevision = initCommand() - .deleteFile("toBeDeleted") - .execute(); - - assertThat(targetRevision).isEqualTo("target"); - } - - @Test - void shouldExecuteDelete() throws IOException { - initCommand() - .deleteFile("toBeDeleted") - .execute(); - - verify(worker).delete("toBeDeleted"); - } - - @Test - void shouldExecuteCreateWithByteSourceContent() throws IOException { - ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); - List contentCaptor = new ArrayList<>(); - doAnswer(new ExtractContent(contentCaptor)).when(worker).create(nameCaptor.capture(), any(), anyBoolean()); - - initCommand() - .createFile("toBeCreated").withData(ByteSource.wrap("content".getBytes())) - .execute(); - - assertThat(nameCaptor.getValue()).isEqualTo("toBeCreated"); - assertThat(contentCaptor).contains("content"); - } - - @Test - void shouldExecuteCreateWithInputStreamContent() throws IOException { - ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); - List contentCaptor = new ArrayList<>(); - doAnswer(new ExtractContent(contentCaptor)).when(worker).create(nameCaptor.capture(), any(), anyBoolean()); - - initCommand() - .createFile("toBeCreated").withData(new ByteArrayInputStream("content".getBytes())) - .execute(); - - assertThat(nameCaptor.getValue()).isEqualTo("toBeCreated"); - assertThat(contentCaptor).contains("content"); - } - - @Test - void shouldExecuteCreateWithOverwriteFalseAsDefault() throws IOException { - ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor overwriteCaptor = ArgumentCaptor.forClass(Boolean.class); - List contentCaptor = new ArrayList<>(); - doAnswer(new ExtractContent(contentCaptor)).when(worker).create(nameCaptor.capture(), any(), overwriteCaptor.capture()); - - initCommand() - .createFile("toBeCreated").withData(new ByteArrayInputStream("content".getBytes())) - .execute(); - - assertThat(nameCaptor.getValue()).isEqualTo("toBeCreated"); - assertThat(overwriteCaptor.getValue()).isFalse(); - assertThat(contentCaptor).contains("content"); - } - - @Test - void shouldExecuteCreateWithOverwriteIfSet() throws IOException { - ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor overwriteCaptor = ArgumentCaptor.forClass(Boolean.class); - List contentCaptor = new ArrayList<>(); - doAnswer(new ExtractContent(contentCaptor)).when(worker).create(nameCaptor.capture(), any(), overwriteCaptor.capture()); - - initCommand() - .createFile("toBeCreated").setOverwrite(true).withData(new ByteArrayInputStream("content".getBytes())) - .execute(); - - assertThat(nameCaptor.getValue()).isEqualTo("toBeCreated"); - assertThat(overwriteCaptor.getValue()).isTrue(); - assertThat(contentCaptor).contains("content"); - } - - @Test - void shouldExecuteCreateMultipleTimes() throws IOException { - ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); - List contentCaptor = new ArrayList<>(); - doAnswer(new ExtractContent(contentCaptor)).when(worker).create(nameCaptor.capture(), any(), anyBoolean()); - - initCommand() - .createFile("toBeCreated_1").withData(new ByteArrayInputStream("content_1".getBytes())) - .createFile("toBeCreated_2").withData(new ByteArrayInputStream("content_2".getBytes())) - .execute(); - - List createdNames = nameCaptor.getAllValues(); - assertThat(createdNames.get(0)).isEqualTo("toBeCreated_1"); - assertThat(createdNames.get(1)).isEqualTo("toBeCreated_2"); - assertThat(contentCaptor).contains("content_1", "content_2"); - } - - @Test - void shouldExecuteModify() throws IOException { - ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); - List contentCaptor = new ArrayList<>(); - doAnswer(new ExtractContent(contentCaptor)).when(worker).modify(nameCaptor.capture(), any()); - - initCommand() - .modifyFile("toBeModified").withData(ByteSource.wrap("content".getBytes())) - .execute(); - - assertThat(nameCaptor.getValue()).isEqualTo("toBeModified"); - assertThat(contentCaptor).contains("content"); - } - private ModifyCommandBuilder initCommand() { return commandBuilder .setBranch("branch") - .setCommitMessage("message") - .setAuthor(new Person()); + .setCommitMessage("message"); } - @Test - void shouldDeleteTemporaryFiles(@TempDir Path temp) throws IOException { - ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor fileCaptor = ArgumentCaptor.forClass(File.class); - doNothing().when(worker).modify(nameCaptor.capture(), fileCaptor.capture()); + private void mockLoggedInUser(User loggedInUser) { + Subject subject = mock(Subject.class); + ThreadContext.bind(subject); + PrincipalCollection principals = mock(PrincipalCollection.class); + when(subject.getPrincipals()).thenReturn(principals); + when(principals.oneByType(User.class)).thenReturn(loggedInUser); + } - initCommand() - .modifyFile("toBeModified").withData(ByteSource.wrap("content".getBytes())) - .execute(); - - assertThat(Files.list(temp)).isEmpty(); + @AfterEach + void unbindSubjec() { + ThreadContext.unbindSubject(); } private static class ExtractContent implements Answer { + private final List contentCaptor; public ExtractContent(List contentCaptor) { @@ -230,4 +132,171 @@ class ModifyCommandBuilderTest { return contentCaptor.add(Files.readAllLines(((File) invocation.getArgument(1)).toPath()).get(0)); } } + + @Nested + class WithUserWithMail { + + @BeforeEach + void initSubject() { + User loggedInUser = new User("dent", "Arthur", "dent@hitchhiker.com"); + mockLoggedInUser(loggedInUser); + } + + @Test + void shouldReturnTargetRevisionFromCommit() { + String targetRevision = initCommand() + .deleteFile("toBeDeleted") + .execute(); + + assertThat(targetRevision).isEqualTo("target"); + } + + @Test + void shouldExecuteDelete() throws IOException { + initCommand() + .deleteFile("toBeDeleted") + .execute(); + + verify(worker).delete("toBeDeleted"); + } + + @Test + void shouldExecuteCreateWithByteSourceContent() throws IOException { + ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); + List contentCaptor = new ArrayList<>(); + doAnswer(new ExtractContent(contentCaptor)).when(worker).create(nameCaptor.capture(), any(), anyBoolean()); + + initCommand() + .createFile("toBeCreated").withData(ByteSource.wrap("content".getBytes())) + .execute(); + + assertThat(nameCaptor.getValue()).isEqualTo("toBeCreated"); + assertThat(contentCaptor).contains("content"); + } + + @Test + void shouldExecuteCreateWithInputStreamContent() throws IOException { + ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); + List contentCaptor = new ArrayList<>(); + doAnswer(new ExtractContent(contentCaptor)).when(worker).create(nameCaptor.capture(), any(), anyBoolean()); + + initCommand() + .createFile("toBeCreated").withData(new ByteArrayInputStream("content".getBytes())) + .execute(); + + assertThat(nameCaptor.getValue()).isEqualTo("toBeCreated"); + assertThat(contentCaptor).contains("content"); + } + + @Test + void shouldExecuteCreateWithOverwriteFalseAsDefault() throws IOException { + ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor overwriteCaptor = ArgumentCaptor.forClass(Boolean.class); + List contentCaptor = new ArrayList<>(); + doAnswer(new ExtractContent(contentCaptor)).when(worker).create(nameCaptor.capture(), any(), overwriteCaptor.capture()); + + initCommand() + .createFile("toBeCreated").withData(new ByteArrayInputStream("content".getBytes())) + .execute(); + + assertThat(nameCaptor.getValue()).isEqualTo("toBeCreated"); + assertThat(overwriteCaptor.getValue()).isFalse(); + assertThat(contentCaptor).contains("content"); + } + + @Test + void shouldExecuteCreateWithOverwriteIfSet() throws IOException { + ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor overwriteCaptor = ArgumentCaptor.forClass(Boolean.class); + List contentCaptor = new ArrayList<>(); + doAnswer(new ExtractContent(contentCaptor)).when(worker).create(nameCaptor.capture(), any(), overwriteCaptor.capture()); + + initCommand() + .createFile("toBeCreated").setOverwrite(true).withData(new ByteArrayInputStream("content".getBytes())) + .execute(); + + assertThat(nameCaptor.getValue()).isEqualTo("toBeCreated"); + assertThat(overwriteCaptor.getValue()).isTrue(); + assertThat(contentCaptor).contains("content"); + } + + @Test + void shouldExecuteCreateMultipleTimes() throws IOException { + ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); + List contentCaptor = new ArrayList<>(); + doAnswer(new ExtractContent(contentCaptor)).when(worker).create(nameCaptor.capture(), any(), anyBoolean()); + + initCommand() + .createFile("toBeCreated_1").withData(new ByteArrayInputStream("content_1".getBytes())) + .createFile("toBeCreated_2").withData(new ByteArrayInputStream("content_2".getBytes())) + .execute(); + + List createdNames = nameCaptor.getAllValues(); + assertThat(createdNames.get(0)).isEqualTo("toBeCreated_1"); + assertThat(createdNames.get(1)).isEqualTo("toBeCreated_2"); + assertThat(contentCaptor).contains("content_1", "content_2"); + } + + @Test + void shouldExecuteModify() throws IOException { + ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); + List contentCaptor = new ArrayList<>(); + doAnswer(new ExtractContent(contentCaptor)).when(worker).modify(nameCaptor.capture(), any()); + + initCommand() + .modifyFile("toBeModified").withData(ByteSource.wrap("content".getBytes())) + .execute(); + + assertThat(nameCaptor.getValue()).isEqualTo("toBeModified"); + assertThat(contentCaptor).contains("content"); + } + + @Test + void shouldDeleteTemporaryFiles(@TempDir Path temp) throws IOException { + ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor fileCaptor = ArgumentCaptor.forClass(File.class); + doNothing().when(worker).modify(nameCaptor.capture(), fileCaptor.capture()); + + initCommand() + .modifyFile("toBeModified").withData(ByteSource.wrap("content".getBytes())) + .execute(); + + assertThat(Files.list(temp)).isEmpty(); + } + + @Test + void shouldUseMailFromUser() throws IOException { + initCommand() + .modifyFile("toBeModified").withData(ByteSource.wrap("content".getBytes())) + .execute(); + + verify(command).execute(argThat(modifyCommandRequest -> { + assertThat(modifyCommandRequest.getAuthor().getMail()).isEqualTo("dent@hitchhiker.com"); + return true; + })); + } + } + + @Nested + class WithUserWithoutMail { + + @BeforeEach + void initSubject() { + User loggedInUser = new User("dent", "Arthur", null); + mockLoggedInUser(loggedInUser); + } + + @Test + void shouldUseMailFromUser() throws IOException { + SCM_CONFIGURATION.setMailHost("heart-of-gold.local"); + initCommand() + .modifyFile("toBeModified").withData(ByteSource.wrap("content".getBytes())) + .execute(); + + verify(command).execute(argThat(modifyCommandRequest -> { + assertThat(modifyCommandRequest.getAuthor().getMail()).isEqualTo("dent@heart-of-gold.local"); + return true; + })); + } + } } diff --git a/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceFactoryTest.java b/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceFactoryTest.java index 492d23dec8..d77d0d072b 100644 --- a/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceFactoryTest.java +++ b/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceFactoryTest.java @@ -46,6 +46,7 @@ import sonia.scm.repository.RepositoryManager; import sonia.scm.repository.spi.RepositoryServiceProvider; import sonia.scm.repository.spi.RepositoryServiceResolver; import sonia.scm.repository.work.WorkdirProvider; +import sonia.scm.user.EMail; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -56,9 +57,6 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class RepositoryServiceFactoryTest { - @Mock - private ScmConfiguration configuration; - @Mock(answer = Answers.RETURNS_MOCKS) private CacheManager cacheManager; @@ -94,8 +92,9 @@ class RepositoryServiceFactoryTest { builder.add(repositoryServiceResolver); } return new RepositoryServiceFactory( - configuration, cacheManager, repositoryManager, builder.build(), - preProcessorUtil, ImmutableSet.of(), workdirProvider, eventBus + cacheManager, repositoryManager, builder.build(), + preProcessorUtil, ImmutableSet.of(), workdirProvider, + new EMail(new ScmConfiguration()), eventBus ); } diff --git a/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceTest.java b/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceTest.java index 901666cb87..8394e3c582 100644 --- a/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceTest.java +++ b/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceTest.java @@ -21,13 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.api; import org.junit.Test; +import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.Repository; import sonia.scm.repository.spi.HttpScmProtocol; import sonia.scm.repository.spi.RepositoryServiceProvider; +import sonia.scm.user.EMail; import javax.servlet.ServletConfig; import javax.servlet.http.HttpServletRequest; @@ -46,9 +48,11 @@ public class RepositoryServiceTest { private final RepositoryServiceProvider provider = mock(RepositoryServiceProvider.class); private final Repository repository = new Repository("", "git", "space", "repo"); + private final EMail eMail = new EMail(new ScmConfiguration()); + @Test public void shouldReturnMatchingProtocolsFromProvider() { - RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()), null); + RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()), null, eMail); Stream supportedProtocols = repositoryService.getSupportedProtocols(); assertThat(sizeOf(supportedProtocols.collect(Collectors.toList()))).isEqualTo(1); @@ -56,7 +60,7 @@ public class RepositoryServiceTest { @Test public void shouldFindKnownProtocol() { - RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()), null); + RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()), null, eMail); HttpScmProtocol protocol = repositoryService.getProtocol(HttpScmProtocol.class); @@ -65,11 +69,9 @@ public class RepositoryServiceTest { @Test public void shouldFailForUnknownProtocol() { - RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()), null); + RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()), null, eMail); - assertThrows(IllegalArgumentException.class, () -> { - repositoryService.getProtocol(UnknownScmProtocol.class); - }); + assertThrows(IllegalArgumentException.class, () -> repositoryService.getProtocol(UnknownScmProtocol.class)); } private static class DummyHttpProtocol extends HttpScmProtocol {