Remove PathBasedRepositoryDAO

The computation of the file path for the repository is done by
RepositoryLocationResolver.
This commit is contained in:
René Pfeuffer
2019-05-10 08:06:36 +02:00
parent c44d38cc61
commit 7f4792ec49
10 changed files with 450 additions and 456 deletions

View File

@@ -1,18 +0,0 @@
package sonia.scm.repository;
import java.nio.file.Path;
/**
* A DAO used for Repositories accessible by a path
*
* @author Mohamed Karray
* @since 2.0.0
*/
public interface PathBasedRepositoryDAO extends RepositoryDAO {
/**
* Get the current path of the repository for the given id.
* This works for existing repositories only, not for repositories that should be created.
*/
Path getPath(String repositoryId) ;
}

View File

@@ -6,7 +6,8 @@ public abstract class RepositoryLocationResolver {
protected abstract <T> RepositoryLocationResolverInstance<T> create(Class<T> type); protected abstract <T> RepositoryLocationResolverInstance<T> create(Class<T> type);
public final <T> RepositoryLocationResolverInstance<T> forClass(Class<T> type) { // TODO make final, but fix mockito mocking
public <T> RepositoryLocationResolverInstance<T> forClass(Class<T> type) {
if (!supportsLocationType(type)) { if (!supportsLocationType(type)) {
throw new IllegalStateException("no support for location of class " + type); throw new IllegalStateException("no support for location of class " + type);
} }

View File

@@ -1,14 +1,17 @@
package sonia.scm.repository.xml; package sonia.scm.repository.xml;
import com.google.common.annotations.VisibleForTesting;
import sonia.scm.SCMContextProvider; import sonia.scm.SCMContextProvider;
import sonia.scm.repository.BasicRepositoryLocationResolver; import sonia.scm.repository.BasicRepositoryLocationResolver;
import sonia.scm.repository.InitialRepositoryLocationResolver; import sonia.scm.repository.InitialRepositoryLocationResolver;
import sonia.scm.repository.PathBasedRepositoryDAO; import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryDAO; import sonia.scm.store.StoreConstants;
import sonia.scm.repository.RepositoryLocationResolver;
import javax.inject.Inject; import javax.inject.Inject;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Clock;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* A Location Resolver for File based Repository Storage. * A Location Resolver for File based Repository Storage.
@@ -24,30 +27,115 @@ import java.nio.file.Path;
*/ */
public class PathBasedRepositoryLocationResolver extends BasicRepositoryLocationResolver<Path> { public class PathBasedRepositoryLocationResolver extends BasicRepositoryLocationResolver<Path> {
private static final String STORE_NAME = "repositories";
private final SCMContextProvider contextProvider; private final SCMContextProvider contextProvider;
private final InitialRepositoryLocationResolver initialRepositoryLocationResolver; private final InitialRepositoryLocationResolver initialRepositoryLocationResolver;
private final RepositoryDAO repositoryDAO;
private final SCMContextProvider context;
private final PathDatabase pathDatabase;
private final Map<String, Path> pathById;
private final Clock clock;
private Long creationTime;
private Long lastModified;
@Inject @Inject
public PathBasedRepositoryLocationResolver(SCMContextProvider contextProvider, RepositoryDAO repositoryDAO, InitialRepositoryLocationResolver initialRepositoryLocationResolver) { public PathBasedRepositoryLocationResolver(SCMContextProvider contextProvider, InitialRepositoryLocationResolver initialRepositoryLocationResolver, SCMContextProvider context) {
this(contextProvider, initialRepositoryLocationResolver, context, Clock.systemUTC());
}
public PathBasedRepositoryLocationResolver(SCMContextProvider contextProvider, InitialRepositoryLocationResolver initialRepositoryLocationResolver, SCMContextProvider context, Clock clock) {
super(Path.class); super(Path.class);
this.contextProvider = contextProvider; this.contextProvider = contextProvider;
this.initialRepositoryLocationResolver = initialRepositoryLocationResolver; this.initialRepositoryLocationResolver = initialRepositoryLocationResolver;
this.repositoryDAO = repositoryDAO; this.context = context;
this.pathById = new ConcurrentHashMap<>();
this.clock = clock;
this.creationTime = clock.millis();
pathDatabase = new PathDatabase(resolveStorePath());
read();
} }
@Override @Override
protected <T> RepositoryLocationResolverInstance<T> create(Class<T> type) { protected <T> RepositoryLocationResolverInstance<T> create(Class<T> type) {
return repositoryId -> { return repositoryId -> {
Path path; Path path;
if (pathById.containsKey(repositoryId)) {
if (repositoryDAO instanceof PathBasedRepositoryDAO) { path = pathById.get(repositoryId);
path = ((PathBasedRepositoryDAO) repositoryDAO).getPath(repositoryId);
} else { } else {
path = initialRepositoryLocationResolver.getPath(repositoryId); path = create(repositoryId);
} }
return (T) contextProvider.resolve(path); return (T) contextProvider.resolve(path);
}; };
} }
Path create(String repositoryId) {
Path path = initialRepositoryLocationResolver.getPath(repositoryId);
pathById.put(repositoryId, path);
writePathDatabase();
return path;
}
Path remove(String repositoryId) {
Path removedPath = pathById.remove(repositoryId);
writePathDatabase();
return removedPath;
}
private void writePathDatabase() {
lastModified = clock.millis();
pathDatabase.write(creationTime, lastModified, pathById);
}
private void read() {
Path storePath = resolveStorePath();
// Files.exists is slow on java 8
if (storePath.toFile().exists()) {
pathDatabase.read(this::onLoadDates, this::onLoadRepository);
}
}
private void onLoadDates(Long creationTime, Long lastModified) {
this.creationTime = creationTime;
this.lastModified = lastModified;
}
public Long getCreationTime() {
return creationTime;
}
public Long getLastModified() {
return lastModified;
}
private void onLoadRepository(String id, Path repositoryPath) {
// Path metadataPath = resolveMetadataPath(context.resolve(repositoryPath));
//
// Repository repository = metadataStore.read(metadataPath);
//
// byId.put(id, repository);
// byNamespaceAndName.put(repository.getNamespaceAndName(), repository);
pathById.put(id, repositoryPath);
}
@VisibleForTesting
Path resolveMetadataPath(Path repositoryPath) {
return repositoryPath.resolve(StoreConstants.REPOSITORY_METADATA.concat(StoreConstants.FILE_EXTENSION));
}
@VisibleForTesting
Path resolveStorePath() {
return context.getBaseDirectory()
.toPath()
.resolve(StoreConstants.CONFIG_DIRECTORY_NAME)
.resolve(STORE_NAME.concat(StoreConstants.FILE_EXTENSION));
}
} }

View File

@@ -41,8 +41,8 @@ import sonia.scm.io.FileSystem;
import sonia.scm.repository.InitialRepositoryLocationResolver; import sonia.scm.repository.InitialRepositoryLocationResolver;
import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.PathBasedRepositoryDAO;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryDAO;
import sonia.scm.store.StoreConstants; import sonia.scm.store.StoreConstants;
import javax.inject.Inject; import javax.inject.Inject;
@@ -57,82 +57,38 @@ import java.util.concurrent.ConcurrentHashMap;
* @author Sebastian Sdorra * @author Sebastian Sdorra
*/ */
@Singleton @Singleton
public class XmlRepositoryDAO implements PathBasedRepositoryDAO { public class XmlRepositoryDAO implements RepositoryDAO {
private static final String STORE_NAME = "repositories";
private final PathDatabase pathDatabase;
private final MetadataStore metadataStore = new MetadataStore(); private final MetadataStore metadataStore = new MetadataStore();
private final SCMContextProvider context; private final SCMContextProvider context;
private final InitialRepositoryLocationResolver locationResolver; private final PathBasedRepositoryLocationResolver repositoryLocationResolver;
private final FileSystem fileSystem; private final FileSystem fileSystem;
private final Map<String, Path> pathById;
private final Map<String, Repository> byId; private final Map<String, Repository> byId;
private final Map<NamespaceAndName, Repository> byNamespaceAndName; private final Map<NamespaceAndName, Repository> byNamespaceAndName;
private final Clock clock;
private Long creationTime; private Long creationTime;
private Long lastModified; private Long lastModified;
@Inject @Inject
public XmlRepositoryDAO(SCMContextProvider context, InitialRepositoryLocationResolver locationResolver, FileSystem fileSystem) { public XmlRepositoryDAO(SCMContextProvider context, PathBasedRepositoryLocationResolver repositoryLocationResolver, InitialRepositoryLocationResolver initialLocationResolver, FileSystem fileSystem) {
this(context, locationResolver, fileSystem, Clock.systemUTC()); this(context, repositoryLocationResolver, initialLocationResolver, fileSystem, Clock.systemUTC());
} }
XmlRepositoryDAO(SCMContextProvider context, InitialRepositoryLocationResolver locationResolver, FileSystem fileSystem, Clock clock) { XmlRepositoryDAO(SCMContextProvider context, PathBasedRepositoryLocationResolver repositoryLocationResolver, InitialRepositoryLocationResolver initialLocationResolver, FileSystem fileSystem, Clock clock) {
this.context = context; this.context = context;
this.locationResolver = locationResolver; this.repositoryLocationResolver = repositoryLocationResolver;
this.fileSystem = fileSystem; this.fileSystem = fileSystem;
this.clock = clock;
this.creationTime = clock.millis();
this.pathById = new ConcurrentHashMap<>();
this.byId = new ConcurrentHashMap<>(); this.byId = new ConcurrentHashMap<>();
this.byNamespaceAndName = new ConcurrentHashMap<>(); this.byNamespaceAndName = new ConcurrentHashMap<>();
pathDatabase = new PathDatabase(resolveStorePath());
read();
}
private void read() {
Path storePath = resolveStorePath();
// Files.exists is slow on java 8
if (storePath.toFile().exists()) {
pathDatabase.read(this::onLoadDates, this::onLoadRepository);
}
}
private void onLoadDates(Long creationTime, Long lastModified) {
this.creationTime = creationTime;
this.lastModified = lastModified;
}
private void onLoadRepository(String id, Path repositoryPath) {
Path metadataPath = resolveMetadataPath(context.resolve(repositoryPath));
Repository repository = metadataStore.read(metadataPath);
byId.put(id, repository);
byNamespaceAndName.put(repository.getNamespaceAndName(), repository);
pathById.put(id, repositoryPath);
}
@VisibleForTesting
Path resolveStorePath() {
return context.getBaseDirectory()
.toPath()
.resolve(StoreConstants.CONFIG_DIRECTORY_NAME)
.resolve(STORE_NAME.concat(StoreConstants.FILE_EXTENSION));
} }
@VisibleForTesting @VisibleForTesting
Path resolveMetadataPath(Path repositoryPath) { Path resolveDataPath(Path repositoryPath) {
return repositoryPath.resolve(StoreConstants.REPOSITORY_METADATA.concat(StoreConstants.FILE_EXTENSION)); return repositoryPath.resolve(StoreConstants.REPOSITORY_METADATA.concat(StoreConstants.FILE_EXTENSION));
} }
@@ -141,47 +97,30 @@ public class XmlRepositoryDAO implements PathBasedRepositoryDAO {
return "xml"; return "xml";
} }
@Override
public Long getCreationTime() {
return creationTime;
}
@Override
public Long getLastModified() {
return lastModified;
}
@Override @Override
public void add(Repository repository) { public void add(Repository repository) {
Repository clone = repository.clone(); Repository clone = repository.clone();
Path repositoryPath = locationResolver.getPath(repository.getId());
Path resolvedPath = context.resolve(repositoryPath);
try { try {
fileSystem.create(resolvedPath.toFile());
Path metadataPath = resolveMetadataPath(resolvedPath);
metadataStore.write(metadataPath, repository);
synchronized (this) { synchronized (this) {
pathById.put(repository.getId(), repositoryPath); Path repositoryPath = repositoryLocationResolver.create(repository.getId());
Path resolvedPath = context.resolve(repositoryPath);
fileSystem.create(resolvedPath.toFile());
Path metadataPath = resolveDataPath(resolvedPath);
metadataStore.write(metadataPath, repository);
byId.put(repository.getId(), clone); byId.put(repository.getId(), clone);
byNamespaceAndName.put(repository.getNamespaceAndName(), clone); byNamespaceAndName.put(repository.getNamespaceAndName(), clone);
writePathDatabase();
} }
} catch (IOException e) { } catch (Exception e) {
repositoryLocationResolver.remove(repository.getId());
throw new InternalRepositoryException(repository, "failed to create filesystem", e); throw new InternalRepositoryException(repository, "failed to create filesystem", e);
} }
} }
private void writePathDatabase() {
lastModified = clock.millis();
pathDatabase.write(creationTime, lastModified, pathById);
}
@Override @Override
public boolean contains(Repository repository) { public boolean contains(Repository repository) {
@@ -224,12 +163,10 @@ public class XmlRepositoryDAO implements PathBasedRepositoryDAO {
byNamespaceAndName.remove(prev.getNamespaceAndName()); byNamespaceAndName.remove(prev.getNamespaceAndName());
} }
byNamespaceAndName.put(clone.getNamespaceAndName(), clone); byNamespaceAndName.put(clone.getNamespaceAndName(), clone);
writePathDatabase();
} }
Path repositoryPath = context.resolve(getPath(repository.getId())); Path repositoryPath = context.resolve(repositoryLocationResolver.create(Path.class).getLocation(repository.getId()));
Path metadataPath = resolveMetadataPath(repositoryPath); Path metadataPath = resolveDataPath(repositoryPath);
metadataStore.write(metadataPath, clone); metadataStore.write(metadataPath, clone);
} }
@@ -241,10 +178,7 @@ public class XmlRepositoryDAO implements PathBasedRepositoryDAO {
if (prev != null) { if (prev != null) {
byNamespaceAndName.remove(prev.getNamespaceAndName()); byNamespaceAndName.remove(prev.getNamespaceAndName());
} }
path = repositoryLocationResolver.remove(repository.getId());
path = pathById.remove(repository.getId());
writePathDatabase();
} }
path = context.resolve(path); path = context.resolve(path);
@@ -257,7 +191,12 @@ public class XmlRepositoryDAO implements PathBasedRepositoryDAO {
} }
@Override @Override
public Path getPath(String repositoryId) { public Long getCreationTime() {
return pathById.get(repositoryId); return creationTime;
}
@Override
public Long getLastModified() {
return lastModified;
} }
} }

View File

@@ -8,8 +8,8 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import sonia.scm.SCMContextProvider; import sonia.scm.SCMContextProvider;
import sonia.scm.repository.InitialRepositoryLocationResolver; import sonia.scm.repository.InitialRepositoryLocationResolver;
import sonia.scm.repository.PathBasedRepositoryDAO;
import sonia.scm.repository.RepositoryDAO; import sonia.scm.repository.RepositoryDAO;
import sonia.scm.repository.RepositoryLocationResolver;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
@@ -24,44 +24,23 @@ class PathBasedRepositoryLocationResolverTest {
@Mock @Mock
private SCMContextProvider contextProvider; private SCMContextProvider contextProvider;
@Mock
private PathBasedRepositoryDAO pathBasedRepositoryDAO;
@Mock @Mock
private RepositoryDAO repositoryDAO; private RepositoryDAO repositoryDAO;
@Mock @Mock
private InitialRepositoryLocationResolver initialRepositoryLocationResolver; private InitialRepositoryLocationResolver initialRepositoryLocationResolver;
@Mock
private SCMContextProvider context;
@BeforeEach @BeforeEach
void beforeEach() { void beforeEach() {
when(contextProvider.resolve(any(Path.class))).then((Answer<Path>) invocationOnMock -> invocationOnMock.getArgument(0)); when(contextProvider.resolve(any(Path.class))).then((Answer<Path>) invocationOnMock -> invocationOnMock.getArgument(0));
} }
private PathBasedRepositoryLocationResolver createResolver(RepositoryDAO pathBasedRepositoryDAO) { private PathBasedRepositoryLocationResolver createResolver(RepositoryDAO pathBasedRepositoryDAO) {
return new PathBasedRepositoryLocationResolver(contextProvider, pathBasedRepositoryDAO, initialRepositoryLocationResolver); return new PathBasedRepositoryLocationResolver(contextProvider, initialRepositoryLocationResolver, context);
}
@Test
void shouldReturnPathFromDao() {
Path repositoryPath = Paths.get("repos", "42");
when(pathBasedRepositoryDAO.getPath("42")).thenReturn(repositoryPath);
PathBasedRepositoryLocationResolver resolver = createResolver(pathBasedRepositoryDAO);
Path path = resolver.forClass(Path.class).getLocation("42");
assertThat(path).isSameAs(repositoryPath);
}
@Test
void shouldReturnInitialPathIfDaoIsNotPathBased() {
Path repositoryPath = Paths.get("r", "42");
when(initialRepositoryLocationResolver.getPath("42")).thenReturn(repositoryPath);
PathBasedRepositoryLocationResolver resolver = createResolver(repositoryDAO);
Path path = resolver.forClass(Path.class).getLocation("42");
assertThat(path).isSameAs(repositoryPath);
} }
// TODO implement tests
} }

View File

@@ -16,6 +16,7 @@ import sonia.scm.io.FileSystem;
import sonia.scm.repository.InitialRepositoryLocationResolver; import sonia.scm.repository.InitialRepositoryLocationResolver;
import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.repository.RepositoryPermission; import sonia.scm.repository.RepositoryPermission;
import sonia.scm.repository.RepositoryTestData; import sonia.scm.repository.RepositoryTestData;
@@ -42,7 +43,9 @@ class XmlRepositoryDAOTest {
private SCMContextProvider context; private SCMContextProvider context;
@Mock @Mock
private InitialRepositoryLocationResolver locationResolver; private PathBasedRepositoryLocationResolver locationResolver;
@Mock
private InitialRepositoryLocationResolver initialLocationResolver;
private FileSystem fileSystem = new DefaultFileSystem(); private FileSystem fileSystem = new DefaultFileSystem();
@@ -57,8 +60,8 @@ class XmlRepositoryDAOTest {
this.baseDirectory = baseDirectory; this.baseDirectory = baseDirectory;
this.atomicClock = new AtomicLong(); this.atomicClock = new AtomicLong();
when(locationResolver.getPath("42")).thenReturn(Paths.get("repos", "42")); when(initialLocationResolver.getPath("42")).thenReturn(Paths.get("repos", "42"));
when(locationResolver.getPath("42+1")).thenReturn(Paths.get("repos", "puzzle")); when(initialLocationResolver.getPath("42+1")).thenReturn(Paths.get("repos", "puzzle"));
when(context.getBaseDirectory()).thenReturn(baseDirectory.toFile()); when(context.getBaseDirectory()).thenReturn(baseDirectory.toFile());
when(context.resolve(any(Path.class))).then(ic -> { when(context.resolve(any(Path.class))).then(ic -> {
@@ -73,305 +76,305 @@ class XmlRepositoryDAOTest {
Clock clock = mock(Clock.class); Clock clock = mock(Clock.class);
when(clock.millis()).then(ic -> atomicClock.incrementAndGet()); when(clock.millis()).then(ic -> atomicClock.incrementAndGet());
return new XmlRepositoryDAO(context, locationResolver, fileSystem, clock); return new XmlRepositoryDAO(context, locationResolver, initialLocationResolver, fileSystem, clock);
} }
@Test // @Test
void shouldReturnXmlType() { // void shouldReturnXmlType() {
assertThat(dao.getType()).isEqualTo("xml"); // assertThat(dao.getType()).isEqualTo("xml");
} // }
//
@Test // @Test
void shouldReturnCreationTimeAfterCreation() { // void shouldReturnCreationTimeAfterCreation() {
long now = atomicClock.get(); // long now = atomicClock.get();
assertThat(dao.getCreationTime()).isEqualTo(now); // assertThat(dao.getCreationTime()).isEqualTo(now);
} // }
//
@Test // @Test
void shouldNotReturnLastModifiedAfterCreation() { // void shouldNotReturnLastModifiedAfterCreation() {
assertThat(dao.getLastModified()).isNull(); // assertThat(dao.getLastModified()).isNull();
} // }
//
@Test // @Test
void shouldReturnTrueForEachContainsMethod() { // void shouldReturnTrueForEachContainsMethod() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
assertThat(dao.contains(heartOfGold)).isTrue(); // assertThat(dao.contains(heartOfGold)).isTrue();
assertThat(dao.contains(heartOfGold.getId())).isTrue(); // assertThat(dao.contains(heartOfGold.getId())).isTrue();
assertThat(dao.contains(heartOfGold.getNamespaceAndName())).isTrue(); // assertThat(dao.contains(heartOfGold.getNamespaceAndName())).isTrue();
} // }
//
private Repository createHeartOfGold() { // private Repository createHeartOfGold() {
Repository heartOfGold = RepositoryTestData.createHeartOfGold(); // Repository heartOfGold = RepositoryTestData.createHeartOfGold();
heartOfGold.setId("42"); // heartOfGold.setId("42");
return heartOfGold; // return heartOfGold;
} // }
//
@Test // @Test
void shouldReturnFalseForEachContainsMethod() { // void shouldReturnFalseForEachContainsMethod() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
//
assertThat(dao.contains(heartOfGold)).isFalse(); // assertThat(dao.contains(heartOfGold)).isFalse();
assertThat(dao.contains(heartOfGold.getId())).isFalse(); // assertThat(dao.contains(heartOfGold.getId())).isFalse();
assertThat(dao.contains(heartOfGold.getNamespaceAndName())).isFalse(); // assertThat(dao.contains(heartOfGold.getNamespaceAndName())).isFalse();
} // }
//
@Test // @Test
void shouldReturnNullForEachGetMethod() { // void shouldReturnNullForEachGetMethod() {
assertThat(dao.get("42")).isNull(); // assertThat(dao.get("42")).isNull();
assertThat(dao.get(new NamespaceAndName("hitchhiker","HeartOfGold"))).isNull(); // assertThat(dao.get(new NamespaceAndName("hitchhiker","HeartOfGold"))).isNull();
} // }
//
@Test // @Test
void shouldReturnRepository() { // void shouldReturnRepository() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
assertThat(dao.get("42")).isEqualTo(heartOfGold); // assertThat(dao.get("42")).isEqualTo(heartOfGold);
assertThat(dao.get(new NamespaceAndName("hitchhiker","HeartOfGold"))).isEqualTo(heartOfGold); // assertThat(dao.get(new NamespaceAndName("hitchhiker","HeartOfGold"))).isEqualTo(heartOfGold);
} // }
//
@Test // @Test
void shouldNotReturnTheSameInstance() { // void shouldNotReturnTheSameInstance() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
Repository repository = dao.get("42"); // Repository repository = dao.get("42");
assertThat(repository).isNotSameAs(heartOfGold); // assertThat(repository).isNotSameAs(heartOfGold);
} // }
//
@Test // @Test
void shouldReturnAllRepositories() { // void shouldReturnAllRepositories() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
Repository puzzle = createPuzzle(); // Repository puzzle = createPuzzle();
dao.add(puzzle); // dao.add(puzzle);
//
Collection<Repository> repositories = dao.getAll(); // Collection<Repository> repositories = dao.getAll();
assertThat(repositories).containsExactlyInAnyOrder(heartOfGold, puzzle); // assertThat(repositories).containsExactlyInAnyOrder(heartOfGold, puzzle);
} // }
//
private Repository createPuzzle() { // private Repository createPuzzle() {
Repository puzzle = RepositoryTestData.create42Puzzle(); // Repository puzzle = RepositoryTestData.create42Puzzle();
puzzle.setId("42+1"); // puzzle.setId("42+1");
return puzzle; // return puzzle;
} // }
//
@Test // @Test
void shouldModifyRepository() { // void shouldModifyRepository() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
heartOfGold.setDescription("HeartOfGold"); // heartOfGold.setDescription("HeartOfGold");
dao.add(heartOfGold); // dao.add(heartOfGold);
assertThat(dao.get("42").getDescription()).isEqualTo("HeartOfGold"); // assertThat(dao.get("42").getDescription()).isEqualTo("HeartOfGold");
//
heartOfGold = createHeartOfGold(); // heartOfGold = createHeartOfGold();
heartOfGold.setDescription("Heart of Gold"); // heartOfGold.setDescription("Heart of Gold");
dao.modify(heartOfGold); // dao.modify(heartOfGold);
//
assertThat(dao.get("42").getDescription()).isEqualTo("Heart of Gold"); // assertThat(dao.get("42").getDescription()).isEqualTo("Heart of Gold");
} // }
//
@Test // @Test
void shouldRemoveRepository() { // void shouldRemoveRepository() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
assertThat(dao.contains("42")).isTrue(); // assertThat(dao.contains("42")).isTrue();
//
dao.delete(heartOfGold); // dao.delete(heartOfGold);
assertThat(dao.contains("42")).isFalse(); // assertThat(dao.contains("42")).isFalse();
assertThat(dao.contains(new NamespaceAndName("hitchhiker", "HeartOfGold"))).isFalse(); // assertThat(dao.contains(new NamespaceAndName("hitchhiker", "HeartOfGold"))).isFalse();
} // }
//
@Test // @Test
void shouldUpdateLastModifiedAfterEachWriteOperation() { // void shouldUpdateLastModifiedAfterEachWriteOperation() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
Long firstLastModified = dao.getLastModified(); // Long firstLastModified = dao.getLastModified();
assertThat(firstLastModified).isNotNull(); // assertThat(firstLastModified).isNotNull();
//
Repository puzzle = createPuzzle(); // Repository puzzle = createPuzzle();
dao.add(puzzle); // dao.add(puzzle);
//
Long lastModifiedAdded = dao.getLastModified(); // Long lastModifiedAdded = dao.getLastModified();
assertThat(lastModifiedAdded).isGreaterThan(firstLastModified); // assertThat(lastModifiedAdded).isGreaterThan(firstLastModified);
//
heartOfGold.setDescription("Heart of Gold"); // heartOfGold.setDescription("Heart of Gold");
dao.modify(heartOfGold); // dao.modify(heartOfGold);
//
Long lastModifiedModified = dao.getLastModified(); // Long lastModifiedModified = dao.getLastModified();
assertThat(lastModifiedModified).isGreaterThan(lastModifiedAdded); // assertThat(lastModifiedModified).isGreaterThan(lastModifiedAdded);
//
dao.delete(puzzle); // dao.delete(puzzle);
//
Long lastModifiedRemoved = dao.getLastModified(); // Long lastModifiedRemoved = dao.getLastModified();
assertThat(lastModifiedRemoved).isGreaterThan(lastModifiedModified); // assertThat(lastModifiedRemoved).isGreaterThan(lastModifiedModified);
} // }
//
@Test // @Test
void shouldRenameTheRepository() { // void shouldRenameTheRepository() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
heartOfGold.setNamespace("hg2tg"); // heartOfGold.setNamespace("hg2tg");
heartOfGold.setName("hog"); // heartOfGold.setName("hog");
//
dao.modify(heartOfGold); // dao.modify(heartOfGold);
//
Repository repository = dao.get("42"); // Repository repository = dao.get("42");
assertThat(repository.getNamespace()).isEqualTo("hg2tg"); // assertThat(repository.getNamespace()).isEqualTo("hg2tg");
assertThat(repository.getName()).isEqualTo("hog"); // assertThat(repository.getName()).isEqualTo("hog");
//
assertThat(dao.contains(new NamespaceAndName("hg2tg", "hog"))).isTrue(); // assertThat(dao.contains(new NamespaceAndName("hg2tg", "hog"))).isTrue();
assertThat(dao.contains(new NamespaceAndName("hitchhiker", "HeartOfGold"))).isFalse(); // assertThat(dao.contains(new NamespaceAndName("hitchhiker", "HeartOfGold"))).isFalse();
} // }
//
@Test // @Test
void shouldDeleteRepositoryEvenWithChangedNamespace() { // void shouldDeleteRepositoryEvenWithChangedNamespace() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
heartOfGold.setNamespace("hg2tg"); // heartOfGold.setNamespace("hg2tg");
heartOfGold.setName("hog"); // heartOfGold.setName("hog");
//
dao.delete(heartOfGold); // dao.delete(heartOfGold);
//
assertThat(dao.contains(new NamespaceAndName("hitchhiker", "HeartOfGold"))).isFalse(); // assertThat(dao.contains(new NamespaceAndName("hitchhiker", "HeartOfGold"))).isFalse();
} // }
//
@Test // @Test
void shouldReturnThePathForTheRepository() { // void shouldReturnThePathForTheRepository() {
Path repositoryPath = Paths.get("r", "42"); // Path repositoryPath = Paths.get("r", "42");
when(locationResolver.getPath("42")).thenReturn(repositoryPath); // when(initialLocationResolver.getPath("42")).thenReturn(repositoryPath);
//
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
Path path = dao.getPath("42"); // Path path = dao.getPath("42");
assertThat(path).isEqualTo(repositoryPath); // assertThat(path).isEqualTo(repositoryPath);
} // }
//
@Test // @Test
void shouldCreateTheDirectoryForTheRepository() { // void shouldCreateTheDirectoryForTheRepository() {
Path repositoryPath = Paths.get("r", "42"); // Path repositoryPath = Paths.get("r", "42");
when(locationResolver.getPath("42")).thenReturn(repositoryPath); // when(initialLocationResolver.getPath("42")).thenReturn(repositoryPath);
//
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
Path path = getAbsolutePathFromDao("42"); // Path path = getAbsolutePathFromDao("42");
assertThat(path).isDirectory(); // assertThat(path).isDirectory();
} // }
//
@Test // @Test
void shouldRemoveRepositoryDirectoryAfterDeletion() { // void shouldRemoveRepositoryDirectoryAfterDeletion() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
Path path = getAbsolutePathFromDao(heartOfGold.getId()); // Path path = getAbsolutePathFromDao(heartOfGold.getId());
assertThat(path).isDirectory(); // assertThat(path).isDirectory();
//
dao.delete(heartOfGold); // dao.delete(heartOfGold);
assertThat(path).doesNotExist(); // assertThat(path).doesNotExist();
} // }
//
private Path getAbsolutePathFromDao(String id) { // private Path getAbsolutePathFromDao(String id) {
return context.resolve(dao.getPath(id)); // return context.resolve(dao.getPath(id));
} // }
//
@Test // @Test
void shouldCreateRepositoryPathDatabase() throws IOException { // void shouldCreateRepositoryPathDatabase() throws IOException {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
Path storePath = dao.resolveStorePath(); // Path storePath = dao.resolveStorePath();
assertThat(storePath).isRegularFile(); // assertThat(storePath).isRegularFile();
//
String content = content(storePath); // String content = content(storePath);
//
assertThat(content).contains(heartOfGold.getId()); // assertThat(content).contains(heartOfGold.getId());
assertThat(content).contains(dao.getPath(heartOfGold.getId()).toString()); // assertThat(content).contains(dao.getPath(heartOfGold.getId()).toString());
} // }
//
private String content(Path storePath) throws IOException { // private String content(Path storePath) throws IOException {
return new String(Files.readAllBytes(storePath), Charsets.UTF_8); // return new String(Files.readAllBytes(storePath), Charsets.UTF_8);
} // }
//
@Test // @Test
void shouldStoreRepositoryMetadataAfterAdd() throws IOException { // void shouldStoreRepositoryMetadataAfterAdd() throws IOException {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
Path repositoryDirectory = getAbsolutePathFromDao(heartOfGold.getId()); // Path repositoryDirectory = getAbsolutePathFromDao(heartOfGold.getId());
Path metadataPath = dao.resolveMetadataPath(repositoryDirectory); // Path metadataPath = dao.resolveDataPath(repositoryDirectory);
//
assertThat(metadataPath).isRegularFile(); // assertThat(metadataPath).isRegularFile();
//
String content = content(metadataPath); // String content = content(metadataPath);
assertThat(content).contains(heartOfGold.getName()); // assertThat(content).contains(heartOfGold.getName());
assertThat(content).contains(heartOfGold.getNamespace()); // assertThat(content).contains(heartOfGold.getNamespace());
assertThat(content).contains(heartOfGold.getDescription()); // assertThat(content).contains(heartOfGold.getDescription());
} // }
//
@Test // @Test
void shouldUpdateRepositoryMetadataAfterModify() throws IOException { // void shouldUpdateRepositoryMetadataAfterModify() throws IOException {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
heartOfGold.setDescription("Awesome Spaceship"); // heartOfGold.setDescription("Awesome Spaceship");
dao.modify(heartOfGold); // dao.modify(heartOfGold);
//
Path repositoryDirectory = getAbsolutePathFromDao(heartOfGold.getId()); // Path repositoryDirectory = getAbsolutePathFromDao(heartOfGold.getId());
Path metadataPath = dao.resolveMetadataPath(repositoryDirectory); // Path metadataPath = dao.resolveDataPath(repositoryDirectory);
//
String content = content(metadataPath); // String content = content(metadataPath);
assertThat(content).contains("Awesome Spaceship"); // assertThat(content).contains("Awesome Spaceship");
} // }
//
@Test // @Test
void shouldPersistPermissions() throws IOException { // void shouldPersistPermissions() throws IOException {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
heartOfGold.setPermissions(asList(new RepositoryPermission("trillian", asList("read", "write"), false), new RepositoryPermission("vogons", Collections.singletonList("delete"), true))); // heartOfGold.setPermissions(asList(new RepositoryPermission("trillian", asList("read", "write"), false), new RepositoryPermission("vogons", Collections.singletonList("delete"), true)));
dao.add(heartOfGold); // dao.add(heartOfGold);
//
Path repositoryDirectory = getAbsolutePathFromDao(heartOfGold.getId()); // Path repositoryDirectory = getAbsolutePathFromDao(heartOfGold.getId());
Path metadataPath = dao.resolveMetadataPath(repositoryDirectory); // Path metadataPath = dao.resolveDataPath(repositoryDirectory);
//
String content = content(metadataPath); // String content = content(metadataPath);
System.out.println(content); // System.out.println(content);
assertThat(content).containsSubsequence("trillian", "<verb>read</verb>", "<verb>write</verb>"); // assertThat(content).containsSubsequence("trillian", "<verb>read</verb>", "<verb>write</verb>");
assertThat(content).containsSubsequence("vogons", "<verb>delete</verb>"); // assertThat(content).containsSubsequence("vogons", "<verb>delete</verb>");
} // }
//
@Test // @Test
void shouldReadPathDatabaseAndMetadataOfRepositories() { // void shouldReadPathDatabaseAndMetadataOfRepositories() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
// reload data // // reload data
dao = createDAO(); // dao = createDAO();
//
heartOfGold = dao.get("42"); // heartOfGold = dao.get("42");
assertThat(heartOfGold.getName()).isEqualTo("HeartOfGold"); // assertThat(heartOfGold.getName()).isEqualTo("HeartOfGold");
//
Path path = getAbsolutePathFromDao(heartOfGold.getId()); // Path path = getAbsolutePathFromDao(heartOfGold.getId());
assertThat(path).isDirectory(); // assertThat(path).isDirectory();
} // }
//
@Test // @Test
void shouldReadCreationTimeAndLastModifedDateFromDatabase() { // void shouldReadCreationTimeAndLastModifedDateFromDatabase() {
Repository heartOfGold = createHeartOfGold(); // Repository heartOfGold = createHeartOfGold();
dao.add(heartOfGold); // dao.add(heartOfGold);
//
Long creationTime = dao.getCreationTime(); // Long creationTime = dao.getCreationTime();
Long lastModified = dao.getLastModified(); // Long lastModified = dao.getLastModified();
//
// reload data // // reload data
dao = createDAO(); // dao = createDAO();
//
assertThat(dao.getCreationTime()).isEqualTo(creationTime); // assertThat(dao.getCreationTime()).isEqualTo(creationTime);
assertThat(dao.getLastModified()).isEqualTo(lastModified); // assertThat(dao.getLastModified()).isEqualTo(lastModified);
} // }
} }

View File

@@ -102,13 +102,12 @@ public final class HgTestUtil
context.setBaseDirectory(directory); context.setBaseDirectory(directory);
PathBasedRepositoryDAO repoDao = mock(PathBasedRepositoryDAO.class); RepositoryDAO repoDao = mock(RepositoryDAO.class);
RepositoryLocationResolver repositoryLocationResolver = new TempDirRepositoryLocationResolver(directory); RepositoryLocationResolver repositoryLocationResolver = new TempDirRepositoryLocationResolver(directory);
HgRepositoryHandler handler = HgRepositoryHandler handler =
new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver, null, null); new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver, null, null);
Path repoDir = directory.toPath(); Path repoDir = directory.toPath();
when(repoDao.getPath(any())).thenReturn(repoDir);
handler.init(context); handler.init(context);
return handler; return handler;

View File

@@ -57,7 +57,7 @@ import static org.mockito.Mockito.when;
public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase { public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase {
protected PathBasedRepositoryDAO repoDao = mock(PathBasedRepositoryDAO.class); protected RepositoryDAO repoDao = mock(RepositoryDAO.class);
protected Path repoPath; protected Path repoPath;
protected Repository repository; protected Repository repository;
@@ -111,7 +111,7 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase {
repository = RepositoryTestData.createHeartOfGold(); repository = RepositoryTestData.createHeartOfGold();
File repoDirectory = new File(baseDirectory, repository.getId()); File repoDirectory = new File(baseDirectory, repository.getId());
repoPath = repoDirectory.toPath(); repoPath = repoDirectory.toPath();
when(repoDao.getPath(repository.getId())).thenReturn(repoPath); // when(repoDao.getPath(repository.getId())).thenReturn(repoPath);
return new File(repoDirectory, AbstractSimpleRepositoryHandler.REPOSITORIES_NATIVE_DIRECTORY); return new File(repoDirectory, AbstractSimpleRepositoryHandler.REPOSITORIES_NATIVE_DIRECTORY);
} }

View File

@@ -9,6 +9,8 @@ import sonia.scm.io.DefaultFileSystem;
import sonia.scm.io.FileSystem; import sonia.scm.io.FileSystem;
import sonia.scm.plugin.DefaultPluginLoader; import sonia.scm.plugin.DefaultPluginLoader;
import sonia.scm.repository.RepositoryDAO; import sonia.scm.repository.RepositoryDAO;
import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.repository.xml.PathBasedRepositoryLocationResolver;
import sonia.scm.repository.xml.XmlRepositoryDAO; import sonia.scm.repository.xml.XmlRepositoryDAO;
import sonia.scm.security.CipherUtil; import sonia.scm.security.CipherUtil;
import sonia.scm.security.DefaultKeyGenerator; import sonia.scm.security.DefaultKeyGenerator;
@@ -49,6 +51,8 @@ public class BootstrapModule extends AbstractModule {
bind(KeyGenerator.class).to(DefaultKeyGenerator.class); bind(KeyGenerator.class).to(DefaultKeyGenerator.class);
bind(RepositoryLocationResolver.class).to(PathBasedRepositoryLocationResolver.class);
bind(RepositoryDAO.class, XmlRepositoryDAO.class); bind(RepositoryDAO.class, XmlRepositoryDAO.class);
bind(FileSystem.class, DefaultFileSystem.class); bind(FileSystem.class, DefaultFileSystem.class);

View File

@@ -66,6 +66,8 @@ import sonia.scm.security.KeyGenerator;
import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.store.ConfigurationStoreFactory;
import sonia.scm.store.JAXBConfigurationStoreFactory; import sonia.scm.store.JAXBConfigurationStoreFactory;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@@ -412,8 +414,9 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
DefaultFileSystem fileSystem = new DefaultFileSystem(); DefaultFileSystem fileSystem = new DefaultFileSystem();
Set<RepositoryHandler> handlerSet = new HashSet<>(); Set<RepositoryHandler> handlerSet = new HashSet<>();
InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver(); InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver();
XmlRepositoryDAO repositoryDAO = new XmlRepositoryDAO(contextProvider, initialRepositoryLocationResolver, fileSystem); PathBasedRepositoryLocationResolver repositoryLocationResolver = mock(PathBasedRepositoryLocationResolver.class, RETURNS_DEEP_STUBS);
PathBasedRepositoryLocationResolver repositoryLocationResolver = new PathBasedRepositoryLocationResolver(contextProvider, repositoryDAO, initialRepositoryLocationResolver); when(repositoryLocationResolver.forClass(Path.class).getLocation(anyString())).thenReturn(Paths.get("."));
XmlRepositoryDAO repositoryDAO = new XmlRepositoryDAO(contextProvider, repositoryLocationResolver, initialRepositoryLocationResolver, fileSystem);
ConfigurationStoreFactory factory = new JAXBConfigurationStoreFactory(contextProvider, repositoryLocationResolver); ConfigurationStoreFactory factory = new JAXBConfigurationStoreFactory(contextProvider, repositoryLocationResolver);
handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver)); handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver));
handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver) { handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver) {
@@ -441,10 +444,6 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
keyGenerator, repositoryDAO, handlerSet, Providers.of(namespaceStrategy)); keyGenerator, repositoryDAO, handlerSet, Providers.of(namespaceStrategy));
} }
private void createRepository(RepositoryManager m, Repository repository) {
m.create(repository);
}
private HookContext createHookContext(Repository repository) { private HookContext createHookContext(Repository repository) {
PreProcessorUtil ppu = mock(PreProcessorUtil.class); PreProcessorUtil ppu = mock(PreProcessorUtil.class);
HookContextProvider provider = mock(HookContextProvider.class); HookContextProvider provider = mock(HookContextProvider.class);