diff --git a/pom.xml b/pom.xml
index e480d1527b..f8bb7c5727 100644
--- a/pom.xml
+++ b/pom.xml
@@ -142,6 +142,11 @@
junit-vintage-engine
+
+ org.junit-pioneer
+ junit-pioneer
+
+
org.hamcrest
hamcrest-core
@@ -159,6 +164,11 @@
mockito-core
+
+ org.mockito
+ mockito-junit-jupiter
+
+
org.assertj
assertj-core
@@ -325,6 +335,13 @@
test
+
+ org.junit-pioneer
+ junit-pioneer
+ 0.3.0
+ test
+
+
org.hamcrest
hamcrest-core
@@ -346,6 +363,13 @@
test
+
+ org.mockito
+ mockito-junit-jupiter
+ ${mockito.version}
+ test
+
+
org.assertj
assertj-core
diff --git a/scm-core/src/main/java/sonia/scm/BasicContextProvider.java b/scm-core/src/main/java/sonia/scm/BasicContextProvider.java
index f6507fc453..6954c03832 100644
--- a/scm-core/src/main/java/sonia/scm/BasicContextProvider.java
+++ b/scm-core/src/main/java/sonia/scm/BasicContextProvider.java
@@ -35,6 +35,7 @@ package sonia.scm;
//~--- non-JDK imports --------------------------------------------------------
+import com.google.common.annotations.VisibleForTesting;
import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------
@@ -43,6 +44,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Path;
import java.util.Locale;
import java.util.Properties;
@@ -105,8 +107,26 @@ public class BasicContextProvider implements SCMContextProvider
}
}
+ @VisibleForTesting
+ BasicContextProvider(File baseDirectory, String version, Stage stage) {
+ this.baseDirectory = baseDirectory;
+ this.version = version;
+ this.stage = stage;
+ }
+
//~--- methods --------------------------------------------------------------
+
+ @Override
+ public Path resolve(Path path) {
+ if (path.isAbsolute()) {
+ return path;
+ }
+
+ return baseDirectory.toPath().resolve(path);
+ }
+
+
/**
* {@inheritDoc}
*/
diff --git a/scm-core/src/main/java/sonia/scm/SCMContextProvider.java b/scm-core/src/main/java/sonia/scm/SCMContextProvider.java
index 18328403fe..93918770c8 100644
--- a/scm-core/src/main/java/sonia/scm/SCMContextProvider.java
+++ b/scm-core/src/main/java/sonia/scm/SCMContextProvider.java
@@ -37,6 +37,7 @@ package sonia.scm;
import java.io.Closeable;
import java.io.File;
+import java.nio.file.Path;
/**
* The main class for retrieving the home and the version of the SCM-Manager.
@@ -65,6 +66,17 @@ public interface SCMContextProvider extends Closeable
*/
public File getBaseDirectory();
+ /**
+ * Resolves the given path against the base directory.
+ *
+ * @param path path to resolve
+ *
+ * @return absolute resolved path
+ *
+ * @since 2.0.0
+ */
+ Path resolve(Path path);
+
/**
* Returns the current stage of SCM-Manager.
*
diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java
index 17e5d3e24f..9941a4253b 100644
--- a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java
+++ b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java
@@ -168,6 +168,6 @@ public abstract class AbstractSimpleRepositoryHandler) invocationOnMock -> invocationOnMock.getArgument(0));
+ }
+
+ private RepositoryLocationResolver createResolver(RepositoryDAO pathBasedRepositoryDAO) {
+ return new RepositoryLocationResolver(contextProvider, pathBasedRepositoryDAO, initialRepositoryLocationResolver);
+ }
+
+ @Test
+ void shouldReturnPathFromDao() {
+ Path repositoryPath = Paths.get("repos", "42");
+ when(pathBasedRepositoryDAO.getPath("42")).thenReturn(repositoryPath);
+
+ RepositoryLocationResolver resolver = createResolver(pathBasedRepositoryDAO);
+ Path path = resolver.getPath("42");
+
+ assertThat(path).isSameAs(repositoryPath);
+ }
+
+ @Test
+ void shouldReturnInitialPathIfDaoIsNotPathBased() {
+ Path repositoryPath = Paths.get("r", "42");
+ when(initialRepositoryLocationResolver.getPath("42")).thenReturn(repositoryPath);
+
+ RepositoryLocationResolver resolver = createResolver(repositoryDAO);
+ Path path = resolver.getPath("42");
+
+ assertThat(path).isSameAs(repositoryPath);
+ }
+
+}
diff --git a/scm-core/src/test/java/sonia/scm/xml/IndentXMLStreamWriterTest.java b/scm-core/src/test/java/sonia/scm/xml/IndentXMLStreamWriterTest.java
index ecfdd06a0f..16c4278793 100644
--- a/scm-core/src/test/java/sonia/scm/xml/IndentXMLStreamWriterTest.java
+++ b/scm-core/src/test/java/sonia/scm/xml/IndentXMLStreamWriterTest.java
@@ -89,7 +89,7 @@ public class IndentXMLStreamWriterTest
StringBuilder buffer = new StringBuilder("");
buffer.append(IndentXMLStreamWriter.LINE_SEPARATOR);
buffer.append("").append(IndentXMLStreamWriter.LINE_SEPARATOR);
- buffer.append(" Hello");
+ buffer.append(" Hello");
buffer.append(IndentXMLStreamWriter.LINE_SEPARATOR);
buffer.append("").append(IndentXMLStreamWriter.LINE_SEPARATOR);
diff --git a/scm-dao-xml/src/main/java/sonia/scm/repository/xml/MetadataStore.java b/scm-dao-xml/src/main/java/sonia/scm/repository/xml/MetadataStore.java
new file mode 100644
index 0000000000..1f5f0e81b6
--- /dev/null
+++ b/scm-dao-xml/src/main/java/sonia/scm/repository/xml/MetadataStore.java
@@ -0,0 +1,50 @@
+package sonia.scm.repository.xml;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import sonia.scm.ContextEntry;
+import sonia.scm.repository.InternalRepositoryException;
+import sonia.scm.repository.Repository;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import java.nio.file.Path;
+
+class MetadataStore {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MetadataStore.class);
+
+ private final JAXBContext jaxbContext;
+
+ MetadataStore() {
+ try {
+ jaxbContext = JAXBContext.newInstance(Repository.class);
+ } catch (JAXBException ex) {
+ throw new IllegalStateException("failed to create jaxb context for repository", ex);
+ }
+ }
+
+ Repository read(Path path) {
+ LOG.trace("read repository metadata from {}", path);
+ try {
+ return (Repository) jaxbContext.createUnmarshaller().unmarshal(path.toFile());
+ } catch (JAXBException ex) {
+ throw new InternalRepositoryException(
+ ContextEntry.ContextBuilder.entity(Path.class, path.toString()).build(), "failed read repository metadata", ex
+ );
+ }
+ }
+
+ void write(Path path, Repository repository) {
+ LOG.trace("write repository metadata of {} to {}", repository.getNamespaceAndName(), path);
+ try {
+ Marshaller marshaller = jaxbContext.createMarshaller();
+ marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
+ marshaller.marshal(repository, path.toFile());
+ } catch (JAXBException ex) {
+ throw new InternalRepositoryException(repository, "failed write repository metadata", ex);
+ }
+ }
+
+}
diff --git a/scm-dao-xml/src/main/java/sonia/scm/repository/xml/PathDatabase.java b/scm-dao-xml/src/main/java/sonia/scm/repository/xml/PathDatabase.java
new file mode 100644
index 0000000000..bddcdec570
--- /dev/null
+++ b/scm-dao-xml/src/main/java/sonia/scm/repository/xml/PathDatabase.java
@@ -0,0 +1,145 @@
+package sonia.scm.repository.xml;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import sonia.scm.ContextEntry;
+import sonia.scm.repository.InternalRepositoryException;
+import sonia.scm.xml.IndentXMLStreamWriter;
+import sonia.scm.xml.XmlStreams;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+
+class PathDatabase {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PathDatabase.class);
+
+ private static final String ENCODING = "UTF-8";
+ private static final String VERSION = "1.0";
+
+ private static final String ELEMENT_REPOSITORIES = "repositories";
+ private static final String ATTRIBUTE_CREATION_TIME = "creation-time";
+ private static final String ATTRIBUTE_LAST_MODIFIED = "last-modified";
+
+ private static final String ELEMENT_REPOSITORY = "repository";
+ private static final String ATTRIBUTE_ID = "id";
+
+ private final Path storePath;
+
+ PathDatabase(Path storePath){
+ this.storePath = storePath;
+ }
+
+ void write(Long creationTime, Long lastModified, Map pathDatabase) {
+ ensureParentDirectoryExists();
+ LOG.trace("write repository path database to {}", storePath);
+
+ try (IndentXMLStreamWriter writer = XmlStreams.createWriter(storePath)) {
+ writer.writeStartDocument(ENCODING, VERSION);
+
+ writeRepositoriesStart(writer, creationTime, lastModified);
+ for (Map.Entry e : pathDatabase.entrySet()) {
+ writeRepository(writer, e.getKey(), e.getValue());
+ }
+ writer.writeEndElement();
+
+ writer.writeEndDocument();
+ } catch (XMLStreamException | IOException ex) {
+ throw new InternalRepositoryException(
+ ContextEntry.ContextBuilder.entity(Path.class, storePath.toString()).build(),
+ "failed to write repository path database",
+ ex
+ );
+ }
+ }
+
+ private void ensureParentDirectoryExists() {
+ Path parent = storePath.getParent();
+ if (!Files.exists(parent)) {
+ try {
+ Files.createDirectories(parent);
+ } catch (IOException ex) {
+ throw new InternalRepositoryException(
+ ContextEntry.ContextBuilder.entity(Path.class, parent.toString()).build(),
+ "failed to create parent directory",
+ ex
+ );
+ }
+ }
+ }
+
+ private void writeRepositoriesStart(XMLStreamWriter writer, Long creationTime, Long lastModified) throws XMLStreamException {
+ writer.writeStartElement(ELEMENT_REPOSITORIES);
+ writer.writeAttribute(ATTRIBUTE_CREATION_TIME, String.valueOf(creationTime));
+ writer.writeAttribute(ATTRIBUTE_LAST_MODIFIED, String.valueOf(lastModified));
+ }
+
+ private void writeRepository(XMLStreamWriter writer, String id, Path value) throws XMLStreamException {
+ writer.writeStartElement(ELEMENT_REPOSITORY);
+ writer.writeAttribute(ATTRIBUTE_ID, id);
+ writer.writeCharacters(value.toString());
+ writer.writeEndElement();
+ }
+
+ void read(OnRepositories onRepositories, OnRepository onRepository) {
+ LOG.trace("read repository path database from {}", storePath);
+ XMLStreamReader reader = null;
+ try {
+ reader = XmlStreams.createReader(storePath);
+
+ while (reader.hasNext()) {
+ int eventType = reader.next();
+
+ if (eventType == XMLStreamReader.START_ELEMENT) {
+ String element = reader.getLocalName();
+ if (ELEMENT_REPOSITORIES.equals(element)) {
+ readRepositories(reader, onRepositories);
+ } else if (ELEMENT_REPOSITORY.equals(element)) {
+ readRepository(reader, onRepository);
+ }
+ }
+ }
+ } catch (XMLStreamException | IOException ex) {
+ throw new InternalRepositoryException(
+ ContextEntry.ContextBuilder.entity(Path.class, storePath.toString()).build(),
+ "failed to read repository path database",
+ ex
+ );
+ } finally {
+ XmlStreams.close(reader);
+ }
+ }
+
+ private void readRepository(XMLStreamReader reader, OnRepository onRepository) throws XMLStreamException {
+ String id = reader.getAttributeValue(null, ATTRIBUTE_ID);
+ Path path = Paths.get(reader.getElementText());
+ onRepository.handle(id, path);
+ }
+
+ private void readRepositories(XMLStreamReader reader, OnRepositories onRepositories) {
+ String creationTime = reader.getAttributeValue(null, ATTRIBUTE_CREATION_TIME);
+ String lastModified = reader.getAttributeValue(null, ATTRIBUTE_LAST_MODIFIED);
+ onRepositories.handle(Long.parseLong(creationTime), Long.parseLong(lastModified));
+ }
+
+ @FunctionalInterface
+ interface OnRepositories {
+
+ void handle(Long creationTime, Long lastModified);
+
+ }
+
+ @FunctionalInterface
+ interface OnRepository {
+
+ void handle(String id, Path path);
+
+ }
+
+}
diff --git a/scm-dao-xml/src/main/java/sonia/scm/repository/xml/XmlRepositoryDAO.java b/scm-dao-xml/src/main/java/sonia/scm/repository/xml/XmlRepositoryDAO.java
index c83de49525..de51ebdef7 100644
--- a/scm-dao-xml/src/main/java/sonia/scm/repository/xml/XmlRepositoryDAO.java
+++ b/scm-dao-xml/src/main/java/sonia/scm/repository/xml/XmlRepositoryDAO.java
@@ -33,156 +33,229 @@ package sonia.scm.repository.xml;
//~--- non-JDK imports --------------------------------------------------------
-import com.google.inject.Inject;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
import com.google.inject.Singleton;
-import sonia.scm.ContextEntry;
import sonia.scm.SCMContextProvider;
import sonia.scm.io.FileSystem;
import sonia.scm.repository.InitialRepositoryLocationResolver;
-import sonia.scm.repository.InitialRepositoryLocationResolver.InitialRepositoryLocation;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.PathBasedRepositoryDAO;
import sonia.scm.repository.Repository;
-import sonia.scm.store.ConfigurationStoreFactory;
-import sonia.scm.xml.AbstractXmlDAO;
+import sonia.scm.store.StoreConstants;
+import javax.inject.Inject;
import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.time.Clock;
import java.util.Collection;
-import java.util.Optional;
+import java.util.LinkedHashMap;
+import java.util.Map;
/**
* @author Sebastian Sdorra
*/
@Singleton
-public class XmlRepositoryDAO
- extends AbstractXmlDAO
- implements PathBasedRepositoryDAO {
+public class XmlRepositoryDAO implements PathBasedRepositoryDAO {
- public static final String STORE_NAME = "repositories";
+ private static final String STORE_NAME = "repositories";
+
+ private final PathDatabase pathDatabase;
+ private final MetadataStore metadataStore = new MetadataStore();
- private InitialRepositoryLocationResolver initialRepositoryLocationResolver;
- private final FileSystem fileSystem;
private final SCMContextProvider context;
+ private final InitialRepositoryLocationResolver locationResolver;
+ private final FileSystem fileSystem;
- //~--- constructors ---------------------------------------------------------
+ @VisibleForTesting
+ Clock clock = Clock.systemUTC();
+
+ private Long creationTime;
+ private Long lastModified;
+
+ private Map pathById;
+ private Map byId;
+ private Map byNamespaceAndName;
- /**
- * Constructs ...
- * @param storeFactory
- * @param fileSystem
- * @param context
- */
@Inject
- public XmlRepositoryDAO(ConfigurationStoreFactory storeFactory, InitialRepositoryLocationResolver initialRepositoryLocationResolver, FileSystem fileSystem, SCMContextProvider context) {
- super(storeFactory.getStore(XmlRepositoryDatabase.class, STORE_NAME));
- this.initialRepositoryLocationResolver = initialRepositoryLocationResolver;
- this.fileSystem = fileSystem;
+ public XmlRepositoryDAO(SCMContextProvider context, InitialRepositoryLocationResolver locationResolver, FileSystem fileSystem) {
this.context = context;
+ this.locationResolver = locationResolver;
+ this.fileSystem = fileSystem;
+
+ this.creationTime = clock.millis();
+
+ this.pathById = new LinkedHashMap<>();
+ this.byId = new LinkedHashMap<>();
+ this.byNamespaceAndName = new LinkedHashMap<>();
+
+ pathDatabase = new PathDatabase(createStorePath());
+ read();
}
- //~--- methods --------------------------------------------------------------
+ private void read() {
+ Path storePath = createStorePath();
- @Override
- public boolean contains(NamespaceAndName namespaceAndName) {
- return db.contains(namespaceAndName);
+ if (!Files.exists(storePath)) {
+ return;
+ }
+
+ pathDatabase.read(this::loadDates, this::loadRepository);
}
- //~--- get methods ----------------------------------------------------------
-
- @Override
- public Repository get(NamespaceAndName namespaceAndName) {
- return db.get(namespaceAndName);
+ private void loadDates(Long creationTime, Long lastModified) {
+ this.creationTime = creationTime;
+ this.lastModified = lastModified;
}
- //~--- methods --------------------------------------------------------------
+ private void loadRepository(String id, Path repositoryPath) {
+ Path metadataPath = createMetadataPath(context.resolve(repositoryPath));
+ Repository repository = metadataStore.read(metadataPath);
+
+ byId.put(id, repository);
+ byNamespaceAndName.put(repository.getNamespaceAndName(), repository);
+ pathById.put(id, repositoryPath);
+ }
+
+ @VisibleForTesting
+ Path createStorePath() {
+ return context.getBaseDirectory()
+ .toPath()
+ .resolve(StoreConstants.CONFIG_DIRECTORY_NAME)
+ .resolve(STORE_NAME.concat(StoreConstants.FILE_EXTENSION));
+ }
+
+
+ @VisibleForTesting
+ Path createMetadataPath(Path repositoryPath) {
+ return repositoryPath.resolve(StoreConstants.REPOSITORY_METADATA.concat(StoreConstants.FILE_EXTENSION));
+ }
@Override
- public void modify(Repository repository) {
- RepositoryPath repositoryPath = findExistingRepositoryPath(repository.getId()).orElseThrow(() -> new InternalRepositoryException(repository, "path object for repository not found"));
- repositoryPath.setRepository(repository);
- repositoryPath.setToBeSynchronized(true);
- storeDB();
+ public String getType() {
+ return "xml";
+ }
+
+ @Override
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ @Override
+ public Long getLastModified() {
+ return lastModified;
}
@Override
public void add(Repository repository) {
- InitialRepositoryLocation initialLocation = initialRepositoryLocationResolver.getRelativeRepositoryPath(repository.getId());
+ Repository clone = repository.clone();
+
+ Path repositoryPath = locationResolver.getPath(repository.getId());
+ Path resolvedPath = context.resolve(repositoryPath);
+
try {
- fileSystem.create(initialLocation.getAbsolutePath());
+ fileSystem.create(resolvedPath.toFile());
+
+ Path metadataPath = createMetadataPath(resolvedPath);
+ metadataStore.write(metadataPath, repository);
+
+ synchronized (this) {
+ pathById.put(repository.getId(), repositoryPath);
+
+ byId.put(repository.getId(), clone);
+ byNamespaceAndName.put(repository.getNamespaceAndName(), clone);
+
+ writePathDatabase();
+ }
+
} catch (IOException e) {
- throw new InternalRepositoryException(repository, "could not create directory for repository data: " + initialLocation.getAbsolutePath(), e);
- }
- RepositoryPath repositoryPath = new RepositoryPath(initialLocation.getRelativePath(), repository.getId(), repository.clone());
- repositoryPath.setToBeSynchronized(true);
- synchronized (store) {
- db.add(repositoryPath);
- storeDB();
+ throw new InternalRepositoryException(repository, "failed to create filesystem", e);
}
}
+ private void writePathDatabase() {
+ lastModified = clock.millis();
+ pathDatabase.write(creationTime, lastModified, pathById);
+ }
+
+ @Override
+ public boolean contains(Repository repository) {
+ return byId.containsKey(repository.getId());
+ }
+
+ @Override
+ public boolean contains(NamespaceAndName namespaceAndName) {
+ return byNamespaceAndName.containsKey(namespaceAndName);
+ }
+
+ @Override
+ public boolean contains(String id) {
+ return byId.containsKey(id);
+ }
+
+ @Override
+ public Repository get(NamespaceAndName namespaceAndName) {
+ return byNamespaceAndName.get(namespaceAndName);
+ }
+
@Override
public Repository get(String id) {
- RepositoryPath repositoryPath = db.get(id);
- if (repositoryPath != null) {
- return repositoryPath.getRepository();
- }
- return null;
+ return byId.get(id);
}
@Override
public Collection getAll() {
- return db.getRepositories();
+ return ImmutableList.copyOf(byNamespaceAndName.values());
}
- /**
- * Method description
- *
- * @param repository
- * @return
- */
@Override
- protected Repository clone(Repository repository) {
- return repository.clone();
+ public void modify(Repository repository) {
+ Repository clone = repository.clone();
+
+ synchronized (this) {
+ // remove old namespaceAndName from map, in case of rename
+ Repository prev = byId.put(clone.getId(), clone);
+ if (prev != null) {
+ byNamespaceAndName.remove(prev.getNamespaceAndName());
+ }
+ byNamespaceAndName.put(clone.getNamespaceAndName(), clone);
+
+ writePathDatabase();
+ }
+
+ Path repositoryPath = context.resolve(getPath(repository.getId()));
+ Path metadataPath = createMetadataPath(repositoryPath);
+ metadataStore.write(metadataPath, clone);
}
@Override
public void delete(Repository repository) {
- Path directory = getPath(repository.getId());
- super.delete(repository);
- try {
- fileSystem.destroy(directory.toFile());
- } catch (IOException e) {
- throw new InternalRepositoryException(repository, "could not delete repository directory", e);
- }
- }
+ Path path;
+ synchronized (this) {
+ Repository prev = byId.remove(repository.getId());
+ if (prev != null) {
+ byNamespaceAndName.remove(prev.getNamespaceAndName());
+ }
- /**
- * Method description
- *
- * @return
- */
- @Override
- protected XmlRepositoryDatabase createNewDatabase() {
- return new XmlRepositoryDatabase();
+ path = pathById.remove(repository.getId());
+
+ writePathDatabase();
+ }
+
+ path = context.resolve(path);
+
+ try {
+ fileSystem.destroy(path.toFile());
+ } catch (IOException e) {
+ throw new InternalRepositoryException(repository, "failed to destroy filesystem", e);
+ }
}
@Override
public Path getPath(String repositoryId) {
- return context
- .getBaseDirectory()
- .toPath()
- .resolve(
- findExistingRepositoryPath(repositoryId)
- .map(RepositoryPath::getPath)
- .orElseThrow(() -> new InternalRepositoryException(ContextEntry.ContextBuilder.entity("repository", repositoryId), "could not find base directory for repository")));
- }
-
- private Optional findExistingRepositoryPath(String repositoryId) {
- return db.values().stream()
- .filter(repoPath -> repoPath.getId().equals(repositoryId))
- .findAny();
+ return pathById.get(repositoryId);
}
}
diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java
index 6a9098b545..40cf03c8a8 100644
--- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java
+++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java
@@ -35,32 +35,14 @@ package sonia.scm.store;
//~--- non-JDK imports --------------------------------------------------------
-import com.google.common.base.Charsets;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Maps;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
import sonia.scm.security.KeyGenerator;
import sonia.scm.xml.IndentXMLStreamWriter;
-
-//~--- JDK imports ------------------------------------------------------------
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.Writer;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Map.Entry;
+import sonia.scm.xml.XmlStreams;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
@@ -68,11 +50,14 @@ import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLOutputFactory;
-import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
-import javax.xml.stream.XMLStreamWriter;
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+
+//~--- JDK imports ------------------------------------------------------------
/**
*
@@ -255,74 +240,6 @@ public class JAXBConfigurationEntryStore implements ConfigurationEntryStore implements ConfigurationEntryStore implements ConfigurationEntryStore implements ConfigurationEntryStore implements ConfigurationEntryStore store;
- @Mock
- private XmlRepositoryDatabase db;
@Mock
private SCMContextProvider context;
- @Rule
- public TemporaryFolder temporaryFolder = new TemporaryFolder();
+ @Mock
+ private InitialRepositoryLocationResolver locationResolver;
- private final FileSystem fileSystem = new DefaultFileSystem();
+ private FileSystem fileSystem = new DefaultFileSystem();
- @Before
- public void init() throws IOException {
- when(storeFactory.getStore(XmlRepositoryDatabase.class, STORE_NAME)).thenReturn(store);
- when(store.get()).thenReturn(db);
- when(context.getBaseDirectory()).thenReturn(temporaryFolder.newFolder());
+ private XmlRepositoryDAO dao;
+
+ private Path baseDirectory;
+
+ private AtomicLong atomicClock;
+
+ @BeforeEach
+ void createDAO(@TempDirectory.TempDir Path baseDirectory) {
+ this.baseDirectory = baseDirectory;
+ this.atomicClock = new AtomicLong();
+
+ when(locationResolver.getPath("42")).thenReturn(Paths.get("repos", "42"));
+ when(locationResolver.getPath("42+1")).thenReturn(Paths.get("repos", "puzzle"));
+
+ when(context.getBaseDirectory()).thenReturn(baseDirectory.toFile());
+ when(context.resolve(any(Path.class))).then(ic -> {
+ Path path = ic.getArgument(0);
+ return baseDirectory.resolve(path);
+ });
+
+ dao = createDAO();
+ }
+
+ private XmlRepositoryDAO createDAO() {
+ XmlRepositoryDAO dao = new XmlRepositoryDAO(context, locationResolver, fileSystem);
+
+ Clock clock = mock(Clock.class);
+ when(clock.millis()).then(ic -> atomicClock.incrementAndGet());
+ dao.clock = clock;
+
+ return dao;
}
@Test
- public void addShouldCreateNewRepositoryPathWithRelativePath() {
- InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver(context);
- XmlRepositoryDAO dao = new XmlRepositoryDAO(storeFactory, initialRepositoryLocationResolver, fileSystem, context);
-
- dao.add(new Repository("id", null, null, null));
-
- verify(db).add(argThat(repositoryPath -> {
- assertThat(repositoryPath.getId()).isEqualTo("id");
- assertThat(repositoryPath.getPath()).isEqualTo(InitialRepositoryLocationResolver.DEFAULT_REPOSITORY_PATH + "/id");
- return true;
- }));
- verify(store).set(db);
+ void shouldReturnXmlType() {
+ assertThat(dao.getType()).isEqualTo("xml");
}
@Test
- public void modifyShouldStoreChangedRepository() {
- Repository oldRepository = new Repository("id", "old", null, null);
- RepositoryPath repositoryPath = new RepositoryPath("/path", "id", oldRepository);
- when(db.values()).thenReturn(asList(repositoryPath));
-
- XmlRepositoryDAO dao = new XmlRepositoryDAO(storeFactory, new InitialRepositoryLocationResolver(context), fileSystem, context);
-
- Repository newRepository = new Repository("id", "new", null, null);
- dao.modify(newRepository);
-
- assertThat(repositoryPath.getRepository()).isSameAs(newRepository);
- verify(store).set(db);
+ void shouldReturnCreationTimeAfterCreation() {
+ long now = System.currentTimeMillis();
+ assertThat(dao.getCreationTime()).isBetween(now - 200, now + 200);
}
@Test
- public void shouldGetPathInBaseDirectoryForRelativePath() {
- Repository existingRepository = new Repository("id", "old", null, null);
- RepositoryPath repositoryPath = new RepositoryPath("path", "id", existingRepository);
- when(db.values()).thenReturn(asList(repositoryPath));
-
- XmlRepositoryDAO dao = new XmlRepositoryDAO(storeFactory, new InitialRepositoryLocationResolver(context), fileSystem, context);
-
- Path path = dao.getPath(existingRepository.getId());
-
- assertThat(path.toString()).isEqualTo(context.getBaseDirectory().getPath() + "/path");
+ void shouldNotReturnLastModifiedAfterCreation() {
+ assertThat(dao.getLastModified()).isNull();
}
@Test
- public void shouldGetPathInBaseDirectoryForAbsolutePath() {
- Repository existingRepository = new Repository("id", "old", null, null);
- RepositoryPath repositoryPath = new RepositoryPath("/tmp/path", "id", existingRepository);
- when(db.values()).thenReturn(asList(repositoryPath));
+ void shouldReturnTrueForEachContainsMethod() {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
- XmlRepositoryDAO dao = new XmlRepositoryDAO(storeFactory, new InitialRepositoryLocationResolver(context), fileSystem, context);
+ assertThat(dao.contains(heartOfGold)).isTrue();
+ assertThat(dao.contains(heartOfGold.getId())).isTrue();
+ assertThat(dao.contains(heartOfGold.getNamespaceAndName())).isTrue();
+ }
- Path path = dao.getPath(existingRepository.getId());
+ private Repository createHeartOfGold() {
+ Repository heartOfGold = RepositoryTestData.createHeartOfGold();
+ heartOfGold.setId("42");
+ return heartOfGold;
+ }
- assertThat(path.toString()).isEqualTo("/tmp/path");
+ @Test
+ void shouldReturnFalseForEachContainsMethod() {
+ Repository heartOfGold = createHeartOfGold();
+
+ assertThat(dao.contains(heartOfGold)).isFalse();
+ assertThat(dao.contains(heartOfGold.getId())).isFalse();
+ assertThat(dao.contains(heartOfGold.getNamespaceAndName())).isFalse();
+ }
+
+ @Test
+ void shouldReturnNullForEachGetMethod() {
+ assertThat(dao.get("42")).isNull();
+ assertThat(dao.get(new NamespaceAndName("hitchhiker","HeartOfGold"))).isNull();
+ }
+
+ @Test
+ void shouldReturnRepository() {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ assertThat(dao.get("42")).isEqualTo(heartOfGold);
+ assertThat(dao.get(new NamespaceAndName("hitchhiker","HeartOfGold"))).isEqualTo(heartOfGold);
+ }
+
+ @Test
+ void shouldNotReturnTheSameInstance() {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ Repository repository = dao.get("42");
+ assertThat(repository).isNotSameAs(heartOfGold);
+ }
+
+ @Test
+ void shouldReturnAllRepositories() {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ Repository puzzle = createPuzzle();
+ dao.add(puzzle);
+
+ Collection repositories = dao.getAll();
+ assertThat(repositories).containsExactlyInAnyOrder(heartOfGold, puzzle);
+ }
+
+ private Repository createPuzzle() {
+ Repository puzzle = RepositoryTestData.create42Puzzle();
+ puzzle.setId("42+1");
+ return puzzle;
+ }
+
+ @Test
+ void shouldModifyRepository() {
+ Repository heartOfGold = createHeartOfGold();
+ heartOfGold.setDescription("HeartOfGold");
+ dao.add(heartOfGold);
+ assertThat(dao.get("42").getDescription()).isEqualTo("HeartOfGold");
+
+ heartOfGold = createHeartOfGold();
+ heartOfGold.setDescription("Heart of Gold");
+ dao.modify(heartOfGold);
+
+ assertThat(dao.get("42").getDescription()).isEqualTo("Heart of Gold");
+ }
+
+ @Test
+ void shouldRemoveRepository() {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+ assertThat(dao.contains("42")).isTrue();
+
+ dao.delete(heartOfGold);
+ assertThat(dao.contains("42")).isFalse();
+ assertThat(dao.contains(new NamespaceAndName("hitchhiker", "HeartOfGold"))).isFalse();
+ }
+
+ @Test
+ void shouldUpdateLastModifiedAfterEachWriteOperation() {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ Long firstLastModified = dao.getLastModified();
+ assertThat(firstLastModified).isNotNull();
+
+ Repository puzzle = createPuzzle();
+ dao.add(puzzle);
+
+ Long lastModifiedAdded = dao.getLastModified();
+ assertThat(lastModifiedAdded).isGreaterThan(firstLastModified);
+
+ heartOfGold.setDescription("Heart of Gold");
+ dao.modify(heartOfGold);
+
+ Long lastModifiedModified = dao.getLastModified();
+ assertThat(lastModifiedModified).isGreaterThan(lastModifiedAdded);
+
+ dao.delete(puzzle);
+
+ Long lastModifiedRemoved = dao.getLastModified();
+ assertThat(lastModifiedRemoved).isGreaterThan(lastModifiedModified);
+ }
+
+ @Test
+ void shouldRenameTheRepository() {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ heartOfGold.setNamespace("hg2tg");
+ heartOfGold.setName("hog");
+
+ dao.modify(heartOfGold);
+
+ Repository repository = dao.get("42");
+ assertThat(repository.getNamespace()).isEqualTo("hg2tg");
+ assertThat(repository.getName()).isEqualTo("hog");
+
+ assertThat(dao.contains(new NamespaceAndName("hg2tg", "hog"))).isTrue();
+ assertThat(dao.contains(new NamespaceAndName("hitchhiker", "HeartOfGold"))).isFalse();
+ }
+
+ @Test
+ void shouldDeleteRepositoryEvenWithChangedNamespace() {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ heartOfGold.setNamespace("hg2tg");
+ heartOfGold.setName("hog");
+
+ dao.delete(heartOfGold);
+
+ assertThat(dao.contains(new NamespaceAndName("hitchhiker", "HeartOfGold"))).isFalse();
+ }
+
+ @Test
+ void shouldReturnThePathForTheRepository() {
+ Path repositoryPath = Paths.get("r", "42");
+ when(locationResolver.getPath("42")).thenReturn(repositoryPath);
+
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ Path path = dao.getPath("42");
+ assertThat(path).isEqualTo(repositoryPath);
+ }
+
+ @Test
+ void shouldCreateTheDirectoryForTheRepository() {
+ Path repositoryPath = Paths.get("r", "42");
+ when(locationResolver.getPath("42")).thenReturn(repositoryPath);
+
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ Path path = getAbsolutePathFromDao("42");
+ assertThat(path).isDirectory();
+ }
+
+ @Test
+ void shouldRemoveRepositoryDirectoryAfterDeletion() {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ Path path = getAbsolutePathFromDao(heartOfGold.getId());
+ assertThat(path).isDirectory();
+
+ dao.delete(heartOfGold);
+ assertThat(path).doesNotExist();
+ }
+
+ private Path getAbsolutePathFromDao(String id) {
+ return context.resolve(dao.getPath(id));
+ }
+
+ @Test
+ void shouldCreateRepositoryPathDatabase() throws IOException {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ Path storePath = dao.createStorePath();
+ assertThat(storePath).isRegularFile();
+
+ String content = content(storePath);
+
+ assertThat(content).contains(heartOfGold.getId());
+ assertThat(content).contains(dao.getPath(heartOfGold.getId()).toString());
+ }
+
+ private String content(Path storePath) throws IOException {
+ return new String(Files.readAllBytes(storePath), Charsets.UTF_8);
+ }
+
+ @Test
+ void shouldStoreRepositoryMetadataAfterAdd() throws IOException {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ Path repositoryDirectory = getAbsolutePathFromDao(heartOfGold.getId());
+ Path metadataPath = dao.createMetadataPath(repositoryDirectory);
+
+ assertThat(metadataPath).isRegularFile();
+
+ String content = content(metadataPath);
+ assertThat(content).contains(heartOfGold.getName());
+ assertThat(content).contains(heartOfGold.getNamespace());
+ assertThat(content).contains(heartOfGold.getDescription());
+ }
+
+ @Test
+ void shouldUpdateRepositoryMetadataAfterModify() throws IOException {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ heartOfGold.setDescription("Awesome Spaceship");
+ dao.modify(heartOfGold);
+
+ Path repositoryDirectory = getAbsolutePathFromDao(heartOfGold.getId());
+ Path metadataPath = dao.createMetadataPath(repositoryDirectory);
+
+ String content = content(metadataPath);
+ assertThat(content).contains("Awesome Spaceship");
+ }
+
+ @Test
+ void shouldReadPathDatabaseAndMetadataOfRepositories() {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ // reload data
+ dao = createDAO();
+
+ heartOfGold = dao.get("42");
+ assertThat(heartOfGold.getName()).isEqualTo("HeartOfGold");
+
+ Path path = getAbsolutePathFromDao(heartOfGold.getId());
+ assertThat(path).isDirectory();
+ }
+
+ @Test
+ void shouldReadCreationTimeAndLastModifedDateFromDatabase() {
+ Repository heartOfGold = createHeartOfGold();
+ dao.add(heartOfGold);
+
+ Long creationTime = dao.getCreationTime();
+ Long lastModified = dao.getLastModified();
+
+ // reload data
+ dao = createDAO();
+
+ assertThat(dao.getCreationTime()).isEqualTo(creationTime);
+ assertThat(dao.getLastModified()).isEqualTo(lastModified);
}
}
diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java
index bfff7d3bf1..d3eca1d6e0 100644
--- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java
+++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java
@@ -62,8 +62,6 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
@Mock
private GitWorkdirFactory gitWorkdirFactory;
- RepositoryLocationResolver repositoryLocationResolver;
-
@Override
protected void checkDirectory(File directory) {
@@ -86,10 +84,10 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
@Override
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
+ RepositoryLocationResolver locationResolver,
File directory) {
- repositoryLocationResolver = new RepositoryLocationResolver(repoDao, new InitialRepositoryLocationResolver(contextProvider));
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory,
- scheduler, repositoryLocationResolver, gitWorkdirFactory);
+ scheduler, locationResolver, gitWorkdirFactory);
repositoryHandler.init(contextProvider);
GitConfig config = new GitConfig();
@@ -103,7 +101,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
@Test
public void getDirectory() {
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory,
- scheduler, repositoryLocationResolver, gitWorkdirFactory);
+ scheduler, locationResolver, gitWorkdirFactory);
GitConfig config = new GitConfig();
config.setDisabled(false);
config.setGcExpression("gc exp");
diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java
index b8b9646a90..7a13c06eb2 100644
--- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java
+++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java
@@ -50,7 +50,7 @@ import static org.junit.Assert.assertTrue;
/**
* @author Sebastian Sdorra
*/
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(MockitoJUnitRunner.Silent.class)
public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
@Mock
@@ -59,8 +59,6 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
@Mock
private com.google.inject.Provider provider;
- private RepositoryLocationResolver repositoryLocationResolver;
-
@Override
protected void checkDirectory(File directory) {
File hgDirectory = new File(directory, ".hg");
@@ -70,11 +68,8 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
}
@Override
- protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
- File directory) {
- repositoryLocationResolver = new RepositoryLocationResolver(repoDao, new InitialRepositoryLocationResolver(contextProvider));
- HgRepositoryHandler handler = new HgRepositoryHandler(factory,
- new HgContextProvider(), repositoryLocationResolver);
+ protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) {
+ HgRepositoryHandler handler = new HgRepositoryHandler(factory, new HgContextProvider(), locationResolver);
handler.init(contextProvider);
HgTestUtil.checkForSkip(handler);
@@ -84,8 +79,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
@Test
public void getDirectory() {
- HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory,
- provider, repositoryLocationResolver);
+ HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, provider, locationResolver);
HgConfig hgConfig = new HgConfig();
hgConfig.setHgBinary("hg");
diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java
index d2344816ef..68f7e18a76 100644
--- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java
+++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java
@@ -103,7 +103,7 @@ public final class HgTestUtil
PathBasedRepositoryDAO repoDao = mock(PathBasedRepositoryDAO.class);
- RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(repoDao, new InitialRepositoryLocationResolver(context));
+ RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(context, repoDao, new InitialRepositoryLocationResolver());
HgRepositoryHandler handler =
new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver);
Path repoDir = directory.toPath();
diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/TempSCMContextProvider.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/TempSCMContextProvider.java
index bc6794e5a5..0a0064ad44 100644
--- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/TempSCMContextProvider.java
+++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/TempSCMContextProvider.java
@@ -41,6 +41,7 @@ import sonia.scm.Stage;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Path;
/**
*
@@ -136,6 +137,11 @@ public class TempSCMContextProvider implements SCMContextProvider
this.baseDirectory = baseDirectory;
}
+ @Override
+ public Path resolve(Path path) {
+ return baseDirectory.toPath().resolve(path);
+ }
+
//~--- fields ---------------------------------------------------------------
/** Field description */
diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java
index bfc8bbc428..7b11d1bb7f 100644
--- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java
+++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java
@@ -67,15 +67,10 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
@Mock
private com.google.inject.Provider repositoryManagerProvider;
- @Mock
- private RepositoryDAO repositoryDAO;
-
private HookContextFactory hookContextFactory = new HookContextFactory(mock(PreProcessorUtil.class));
private HookEventFacade facade = new HookEventFacade(repositoryManagerProvider, hookContextFactory);
- private RepositoryLocationResolver repositoryLocationResolver;
-
@Override
protected void checkDirectory(File directory) {
File format = new File(directory, "format");
@@ -91,9 +86,9 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
@Override
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
+ RepositoryLocationResolver locationResolver,
File directory) {
- repositoryLocationResolver = new RepositoryLocationResolver(repoDao, new InitialRepositoryLocationResolver(contextProvider));
- SvnRepositoryHandler handler = new SvnRepositoryHandler(factory, null, repositoryLocationResolver);
+ SvnRepositoryHandler handler = new SvnRepositoryHandler(factory, null, locationResolver);
handler.init(contextProvider);
@@ -109,7 +104,7 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
public void getDirectory() {
when(factory.getStore(any(), any())).thenReturn(store);
SvnRepositoryHandler repositoryHandler = new SvnRepositoryHandler(factory,
- facade, repositoryLocationResolver);
+ facade, locationResolver);
SvnConfig svnConfig = new SvnConfig();
repositoryHandler.setConfig(svnConfig);
diff --git a/scm-test/src/main/java/sonia/scm/repository/RepositoryTestData.java b/scm-test/src/main/java/sonia/scm/repository/RepositoryTestData.java
index b81c39ca00..5dbd672b98 100644
--- a/scm-test/src/main/java/sonia/scm/repository/RepositoryTestData.java
+++ b/scm-test/src/main/java/sonia/scm/repository/RepositoryTestData.java
@@ -45,6 +45,7 @@ public final class RepositoryTestData {
.type(type)
.contact("douglas.adams@hitchhiker.com")
.name("42Puzzle")
+ .namespace("hitchhiker")
.description("The 42 Puzzle")
.build();
}
@@ -59,6 +60,7 @@ public final class RepositoryTestData {
.type(type)
.contact("zaphod.beeblebrox@hitchhiker.com")
.name("happyVerticalPeopleTransporter")
+ .namespace("hitchhiker")
.description("Happy Vertical People Transporter")
.build();
}
@@ -72,6 +74,7 @@ public final class RepositoryTestData {
.type(type)
.contact("zaphod.beeblebrox@hitchhiker.com")
.name("HeartOfGold")
+ .namespace("hitchhiker")
.description(
"Heart of Gold is the first prototype ship to successfully utilise the revolutionary Infinite Improbability Drive")
.build();
@@ -87,6 +90,7 @@ public final class RepositoryTestData {
.type(type)
.contact("douglas.adams@hitchhiker.com")
.name("RestaurantAtTheEndOfTheUniverse")
+ .namespace("hitchhiker")
.description("The Restaurant at the End of the Universe")
.build();
}
diff --git a/scm-test/src/main/java/sonia/scm/repository/SimpleRepositoryHandlerTestBase.java b/scm-test/src/main/java/sonia/scm/repository/SimpleRepositoryHandlerTestBase.java
index 706117b2c7..37f7266984 100644
--- a/scm-test/src/main/java/sonia/scm/repository/SimpleRepositoryHandlerTestBase.java
+++ b/scm-test/src/main/java/sonia/scm/repository/SimpleRepositoryHandlerTestBase.java
@@ -45,6 +45,7 @@ import java.nio.file.Path;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -63,7 +64,7 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase {
protected abstract void checkDirectory(File directory);
protected abstract RepositoryHandler createRepositoryHandler(
- ConfigurationStoreFactory factory, File directory) throws IOException, RepositoryPathNotFoundException;
+ ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) throws IOException, RepositoryPathNotFoundException;
@Test
public void testCreate() {
@@ -75,7 +76,15 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase {
InMemoryConfigurationStoreFactory storeFactory = new InMemoryConfigurationStoreFactory();
baseDirectory = new File(contextProvider.getBaseDirectory(), "repositories");
IOUtil.mkdirs(baseDirectory);
- handler = createRepositoryHandler(storeFactory, baseDirectory);
+
+ locationResolver = mock(RepositoryLocationResolver.class);
+
+ when(locationResolver.getPath(anyString())).then(ic -> {
+ String id = ic.getArgument(0);
+ return baseDirectory.toPath().resolve(id);
+ });
+
+ handler = createRepositoryHandler(storeFactory, locationResolver, baseDirectory);
}
@Override
@@ -105,6 +114,7 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase {
}
protected File baseDirectory;
+ protected RepositoryLocationResolver locationResolver;
private RepositoryHandler handler;
}
diff --git a/scm-test/src/main/java/sonia/scm/util/MockUtil.java b/scm-test/src/main/java/sonia/scm/util/MockUtil.java
index 756b2632be..76bf4ae24d 100644
--- a/scm-test/src/main/java/sonia/scm/util/MockUtil.java
+++ b/scm-test/src/main/java/sonia/scm/util/MockUtil.java
@@ -55,6 +55,7 @@ import static org.mockito.Mockito.*;
import java.io.File;
+import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
@@ -213,6 +214,10 @@ public final class MockUtil
SCMContextProvider provider = mock(SCMContextProvider.class);
when(provider.getBaseDirectory()).thenReturn(directory);
+ when(provider.resolve(any(Path.class))).then(ic -> {
+ Path p = ic.getArgument(0);
+ return directory.toPath().resolve(p);
+ });
return provider;
}
diff --git a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java
index bb7c861d33..141a7f8527 100644
--- a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java
@@ -434,9 +434,9 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase {
DefaultFileSystem fileSystem = new DefaultFileSystem();
Set handlerSet = new HashSet<>();
ConfigurationStoreFactory factory = new JAXBConfigurationStoreFactory(contextProvider);
- InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver(contextProvider);
- XmlRepositoryDAO repositoryDAO = new XmlRepositoryDAO(factory, initialRepositoryLocationResolver, fileSystem, contextProvider);
- RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(repositoryDAO, initialRepositoryLocationResolver);
+ InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver();
+ XmlRepositoryDAO repositoryDAO = new XmlRepositoryDAO(contextProvider, initialRepositoryLocationResolver, fileSystem);
+ RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(contextProvider, repositoryDAO, initialRepositoryLocationResolver);
handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver));
handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver) {
@Override