diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java index 3e85b4f3c2..9ac3b78b20 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java @@ -40,6 +40,8 @@ import com.google.inject.servlet.ServletModule; import org.eclipse.jgit.transport.ScmTransportProtocol; import sonia.scm.plugin.ext.Extension; +import sonia.scm.web.lfs.LfsBlobStoreFactory; +import sonia.scm.web.lfs.LfsStoreRemoveListener; /** * @@ -68,6 +70,9 @@ public class GitServletModule extends ServletModule bind(GitRepositoryResolver.class); bind(GitReceivePackFactory.class); bind(ScmTransportProtocol.class); + + bind(LfsBlobStoreFactory.class); + bind(LfsStoreRemoveListener.class); // serlvelts and filters filter(PATTERN_GIT).through(GitBasicAuthenticationFilter.class); diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java index 5dcef505c1..e8e62b52cc 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java @@ -168,13 +168,13 @@ public class ScmGitServlet extends GitServlet private void handleRequest(HttpServletRequest request, HttpServletResponse response, Repository repository) throws ServletException, IOException { logger.trace("handle git repository at {}", repository.getName()); if (isLfsBatchApiRequest(request, repository.getName())) { - + HttpServlet servlet = lfsServletFactory.createProtocolServletFor(repository, request); logger.trace("handle lfs batch api request"); - handleGitLfsRequest(request, response, repository); + handleGitLfsRequest(servlet, request, response, repository); } else if (isLfsFileTransferRequest(request, repository.getName())) { - + HttpServlet servlet = lfsServletFactory.createFileLfsServletFor(repository, request); logger.trace("handle lfs file transfer request"); - handleGitLfsRequest(request, response, repository); + handleGitLfsRequest(servlet, request, response, repository); } else if (isRegularGitAPIRequest(request)) { logger.trace("handle regular git request"); // continue with the regular git Backend @@ -189,8 +189,7 @@ public class ScmGitServlet extends GitServlet return REGEX_GITHTTPBACKEND.matcher(HttpUtil.getStrippedURI(request)).matches(); } - private void handleGitLfsRequest(HttpServletRequest request, HttpServletResponse response, Repository repository) throws ServletException, IOException { - HttpServlet servlet = lfsServletFactory.createProtocolServletFor(repository, request); + private void handleGitLfsRequest(HttpServlet servlet, HttpServletRequest request, HttpServletResponse response, Repository repository) throws ServletException, IOException { if (repositoryRequestListenerUtil.callListeners(request, response, repository)) { servlet.service(request, response); } else if (logger.isDebugEnabled()) { diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/LfsBlobStoreFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/LfsBlobStoreFactory.java new file mode 100644 index 0000000000..eebd6b8f2b --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/LfsBlobStoreFactory.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + +package sonia.scm.web.lfs; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import sonia.scm.repository.Repository; +import sonia.scm.store.BlobStore; +import sonia.scm.store.BlobStoreFactory; + +/** + * Creates {@link BlobStore} objects to store lfs objects. + * + * @author Sebastian Sdorra + * @since 1.54 + */ +@Singleton +public class LfsBlobStoreFactory { + + private static final String GIT_LFS_REPOSITORY_POSTFIX = "-git-lfs"; + + private final BlobStoreFactory blobStoreFactory; + + /** + * Create a new instance. + * + * @param blobStoreFactory blob store factory + */ + @Inject + public LfsBlobStoreFactory(BlobStoreFactory blobStoreFactory) { + this.blobStoreFactory = blobStoreFactory; + } + + /** + * Provides a {@link BlobStore} corresponding to the SCM Repository. + *
+ * git-lfs repositories should generally carry the same name as their regular SCM repository counterparts. However, + * we have decided to store them under their IDs instead of their names, since the names might change and provide + * other drawbacks, as well. + *
+ * These repositories will have {@linkplain #GIT_LFS_REPOSITORY_POSTFIX} appended to their IDs. + * + * @param repository The SCM Repository to provide a LFS {@link BlobStore} for. + * + * @return blob store for the corresponding scm repository + */ + public BlobStore getLfsBlobStore(Repository repository) { + return blobStoreFactory.getBlobStore(repository.getId() + GIT_LFS_REPOSITORY_POSTFIX); + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/LfsStoreRemoveListener.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/LfsStoreRemoveListener.java new file mode 100644 index 0000000000..4d86c38db6 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/LfsStoreRemoveListener.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + +package sonia.scm.web.lfs; + +import com.google.common.eventbus.Subscribe; +import com.google.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.EagerSingleton; +import sonia.scm.HandlerEvent; +import sonia.scm.plugin.ext.Extension; +import sonia.scm.repository.GitRepositoryHandler; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryEvent; +import sonia.scm.store.Blob; +import sonia.scm.store.BlobStore; + +/** + * Listener which removes all lfs objects from a blob store, whenever its corresponding git repository gets deleted. + * + * @author Sebastian Sdorra + * @since 1.54 + */ +@Extension +@EagerSingleton +public class LfsStoreRemoveListener { + + private static final Logger LOG = LoggerFactory.getLogger(LfsBlobStoreFactory.class); + + private final LfsBlobStoreFactory lfsBlobStoreFactory; + + @Inject + public LfsStoreRemoveListener(LfsBlobStoreFactory lfsBlobStoreFactory) { + this.lfsBlobStoreFactory = lfsBlobStoreFactory; + } + + /** + * Remove all object from the blob store, if the event is an delete event and the repository is a git repository. + * + * @param event repository event + */ + @Subscribe + public void handleRepositoryEvent(RepositoryEvent event) { + if ( isDeleteEvent(event) && isGitRepositoryEvent(event) ) { + removeLfsStore(event.getItem()); + } + } + + private boolean isDeleteEvent(RepositoryEvent event) { + return HandlerEvent.DELETE == event.getEventType(); + } + + private boolean isGitRepositoryEvent(RepositoryEvent event) { + return event.getItem() != null + && event.getItem().getType().equals(GitRepositoryHandler.TYPE_NAME); + } + + private void removeLfsStore(Repository repository) { + LOG.debug("remove all blobs from store, because corresponding git repository {} was removed", repository.getName()); + BlobStore blobStore = lfsBlobStoreFactory.getLfsBlobStore(repository); + for ( Blob blob : blobStore.getAll() ) { + LOG.trace("remove blob {}, because repository {} was removed", blob.getId(), repository.getName()); + blobStore.remove(blob); + } + } + +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/servlet/LfsServletFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/servlet/LfsServletFactory.java index cbe4d1b99c..2ca10559a2 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/servlet/LfsServletFactory.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/lfs/servlet/LfsServletFactory.java @@ -8,13 +8,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.repository.Repository; import sonia.scm.store.BlobStore; -import sonia.scm.store.BlobStoreFactory; import sonia.scm.util.HttpUtil; import sonia.scm.web.lfs.ScmBlobLfsRepository; import javax.inject.Inject; +import javax.inject.Singleton; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; +import sonia.scm.web.lfs.LfsBlobStoreFactory; /** * This factory class is a helper class to provide the {@link LfsProtocolServlet} and the {@link FileLfsServlet} @@ -23,18 +24,16 @@ import javax.servlet.http.HttpServletRequest; * @since 1.54 * Created by omilke on 11.05.2017. */ +@Singleton public class LfsServletFactory { - private static final String GIT_LFS_REPOSITORY_POSTFIX = "-git-lfs"; - private static final Logger logger = LoggerFactory.getLogger(LfsServletFactory.class); - private final BlobStoreFactory blobStoreFactory; + private final LfsBlobStoreFactory lfsBlobStoreFactory; @Inject - public LfsServletFactory(BlobStoreFactory blobStoreFactory) { - - this.blobStoreFactory = blobStoreFactory; + public LfsServletFactory(LfsBlobStoreFactory lfsBlobStoreFactory) { + this.lfsBlobStoreFactory = lfsBlobStoreFactory; } /** @@ -45,8 +44,7 @@ public class LfsServletFactory { * @return The {@link LfsProtocolServlet} to provide the LFS Batch API for a SCM Repository. */ public LfsProtocolServlet createProtocolServletFor(Repository repository, HttpServletRequest request) { - - BlobStore blobStore = getBlobStore(repository); + BlobStore blobStore = lfsBlobStoreFactory.getLfsBlobStore(repository); String baseUri = buildBaseUri(repository, request); LargeFileRepository largeFileRepository = new ScmBlobLfsRepository(blobStore, baseUri); @@ -61,8 +59,7 @@ public class LfsServletFactory { * @return The {@link FileLfsServlet} to provide the LFS Upload / Download API for a SCM Repository. */ public HttpServlet createFileLfsServletFor(Repository repository, HttpServletRequest request) { - - return new ScmFileTransferServlet(getBlobStore(repository)); + return new ScmFileTransferServlet(lfsBlobStoreFactory.getLfsBlobStore(repository)); } /** @@ -73,26 +70,7 @@ public class LfsServletFactory { */ @VisibleForTesting static String buildBaseUri(Repository repository, HttpServletRequest request) { - return String.format("%s/git/%s.git/info/lfs/objects/", HttpUtil.getCompleteUrl(request), repository.getName()); } - /** - * Provides a {@link BlobStore} corresponding to the SCM Repository. - *
- * git-lfs repositories should generally carry the same name as their regular SCM repository counterparts. However, - * we have decided to store them under their IDs instead of their names, since the names might change and provide - * other drawbacks, as well. - *
- * These repositories will have {@linkplain #GIT_LFS_REPOSITORY_POSTFIX} appended to their IDs.
- *
- * @param repository The SCM Repository to provide a LFS {@link BlobStore} for.
- */
- @VisibleForTesting
- BlobStore getBlobStore(Repository repository) {
-
- return blobStoreFactory.getBlobStore(repository.getId() + GIT_LFS_REPOSITORY_POSTFIX);
- }
-
-
}
diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/lfs/LfsBlobStoreFactoryTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/lfs/LfsBlobStoreFactoryTest.java
new file mode 100644
index 0000000000..2eb8968405
--- /dev/null
+++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/lfs/LfsBlobStoreFactoryTest.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2010, Sebastian Sdorra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+package sonia.scm.web.lfs;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import static org.mockito.Matchers.matches;
+import org.mockito.Mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import org.mockito.runners.MockitoJUnitRunner;
+import sonia.scm.repository.Repository;
+import sonia.scm.store.BlobStoreFactory;
+
+/**
+ * Unit tests for {@link LfsBlobStoreFactory}.
+ *
+ * @author Sebastian Sdorra
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class LfsBlobStoreFactoryTest {
+
+ @Mock
+ private BlobStoreFactory blobStoreFactory;
+
+ @InjectMocks
+ private LfsBlobStoreFactory lfsBlobStoreFactory;
+
+ @Test
+ public void getBlobStore() throws Exception {
+ lfsBlobStoreFactory.getLfsBlobStore(new Repository("the-id", "GIT", "the-name"));
+
+ // just make sure the right parameter is passed, as properly validating the return value is nearly impossible with
+ // the return value (and should not be part of this test)
+ verify(blobStoreFactory).getBlobStore(matches("the-id-git-lfs"));
+
+ // make sure there have been no further usages of the factory
+ verifyNoMoreInteractions(blobStoreFactory);
+ }
+
+}
diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/lfs/LfsStoreRemoveListenerTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/lfs/LfsStoreRemoveListenerTest.java
new file mode 100644
index 0000000000..36c380e193
--- /dev/null
+++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/lfs/LfsStoreRemoveListenerTest.java
@@ -0,0 +1,122 @@
+/**
+ * Copyright (c) 2010, Sebastian Sdorra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+package sonia.scm.web.lfs;
+
+import com.google.common.collect.Lists;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import static org.mockito.Mockito.*;
+import org.mockito.runners.MockitoJUnitRunner;
+import sonia.scm.HandlerEvent;
+import sonia.scm.repository.Repository;
+import sonia.scm.repository.RepositoryEvent;
+import sonia.scm.repository.RepositoryTestData;
+import sonia.scm.store.Blob;
+import sonia.scm.store.BlobStore;
+
+/**
+ * Unit tests for {@link LfsStoreRemoveListener}.
+ *
+ * @author Sebastian Sdorra
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class LfsStoreRemoveListenerTest {
+
+ @Mock
+ private LfsBlobStoreFactory lfsBlobStoreFactory;
+
+ @Mock
+ private BlobStore blobStore;
+
+ @InjectMocks
+ private LfsStoreRemoveListener lfsStoreRemoveListener;
+
+ @Test
+ public void testHandleRepositoryEventWithNonDeleteEvents() {
+ lfsStoreRemoveListener.handleRepositoryEvent(event(HandlerEvent.BEFORE_CREATE));
+ lfsStoreRemoveListener.handleRepositoryEvent(event(HandlerEvent.CREATE));
+
+ lfsStoreRemoveListener.handleRepositoryEvent(event(HandlerEvent.BEFORE_MODIFY));
+ lfsStoreRemoveListener.handleRepositoryEvent(event(HandlerEvent.MODIFY));
+
+ lfsStoreRemoveListener.handleRepositoryEvent(event(HandlerEvent.BEFORE_DELETE));
+
+ verifyZeroInteractions(lfsBlobStoreFactory);
+ }
+
+ @Test
+ public void testHandleRepositoryEventWithNonGitRepositories() {
+ lfsStoreRemoveListener.handleRepositoryEvent(event(HandlerEvent.DELETE, "svn"));
+ lfsStoreRemoveListener.handleRepositoryEvent(event(HandlerEvent.DELETE, "hg"));
+ lfsStoreRemoveListener.handleRepositoryEvent(event(HandlerEvent.DELETE, "dummy"));
+
+ verifyZeroInteractions(lfsBlobStoreFactory);
+ }
+
+ @Test
+ public void testHandleRepositoryEvent() {
+ Repository heartOfGold = RepositoryTestData.createHeartOfGold("git");
+
+ when(lfsBlobStoreFactory.getLfsBlobStore(heartOfGold)).thenReturn(blobStore);
+ Blob blobA = mockBlob("a");
+ Blob blobB = mockBlob("b");
+ List