Use manually entered namespace and name

This commit is contained in:
René Pfeuffer
2019-06-11 13:10:31 +02:00
parent fdb8143b76
commit 802fb3e0cf
9 changed files with 427 additions and 195 deletions

View File

@@ -3,12 +3,14 @@ package sonia.scm.update;
import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache; import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory; import com.github.mustachejava.MustacheFactory;
import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.boot.RestartEvent; import sonia.scm.boot.RestartEvent;
import sonia.scm.event.ScmEventBus; import sonia.scm.event.ScmEventBus;
import sonia.scm.update.repository.MigrationStrategy; import sonia.scm.update.repository.MigrationStrategy;
import sonia.scm.update.repository.MigrationStrategyDao; import sonia.scm.update.repository.MigrationStrategyDao;
import sonia.scm.update.repository.V1Repository;
import sonia.scm.update.repository.XmlRepositoryV1UpdateStep; import sonia.scm.update.repository.XmlRepositoryV1UpdateStep;
import sonia.scm.util.ValidationUtil; import sonia.scm.util.ValidationUtil;
@@ -19,6 +21,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -62,9 +65,7 @@ class MigrationWizardServlet extends HttpServlet {
.stream() .stream()
.anyMatch(entry -> entry.isNamespaceInvalid() || entry.isNameInvalid())); .anyMatch(entry -> entry.isNamespaceInvalid() || entry.isNameInvalid()));
MustacheFactory mf = new DefaultMustacheFactory(); respondWithTemplate(resp, model, "templates/repository-migration.mustache");
Mustache template = mf.compile("templates/repository-migration.mustache");
respondWithTemplate(resp, model, template);
} }
@Override @Override
@@ -76,6 +77,7 @@ class MigrationWizardServlet extends HttpServlet {
String id = repositoryLineEntry.getId(); String id = repositoryLineEntry.getId();
String namespace = req.getParameter("namespace-" + id); String namespace = req.getParameter("namespace-" + id);
String name = req.getParameter("name-" + id); String name = req.getParameter("name-" + id);
String strategy = req.getParameter("strategy-" + id);
repositoryLineEntry.setNamespace(namespace); repositoryLineEntry.setNamespace(namespace);
repositoryLineEntry.setName(name); repositoryLineEntry.setName(name);
@@ -105,19 +107,15 @@ class MigrationWizardServlet extends HttpServlet {
} }
); );
MustacheFactory mf = new DefaultMustacheFactory();
Mustache template = mf.compile("templates/repository-migration-restart.mustache");
Map<String, Object> model = Collections.singletonMap("contextPath", req.getContextPath()); Map<String, Object> model = Collections.singletonMap("contextPath", req.getContextPath());
respondWithTemplate(resp, model, template); respondWithTemplate(resp, model, "templates/repository-migration-restart.mustache");
resp.setStatus(200);
ScmEventBus.getInstance().post(new RestartEvent(MigrationWizardServlet.class, "wrote migration data")); ScmEventBus.getInstance().post(new RestartEvent(MigrationWizardServlet.class, "wrote migration data"));
} }
private List<RepositoryLineEntry> getRepositoryLineEntries() { private List<RepositoryLineEntry> getRepositoryLineEntries() {
List<XmlRepositoryV1UpdateStep.V1Repository> repositoriesWithoutMigrationStrategies = List<V1Repository> repositoriesWithoutMigrationStrategies =
repositoryV1UpdateStep.getRepositoriesWithoutMigrationStrategies(); repositoryV1UpdateStep.getRepositoriesWithoutMigrationStrategies();
return repositoriesWithoutMigrationStrategies.stream() return repositoriesWithoutMigrationStrategies.stream()
.map(RepositoryLineEntry::new) .map(RepositoryLineEntry::new)
@@ -129,7 +127,11 @@ class MigrationWizardServlet extends HttpServlet {
return MigrationStrategy.values(); return MigrationStrategy.values();
} }
private void respondWithTemplate(HttpServletResponse resp, Map<String, Object> model, Mustache template) { @VisibleForTesting
void respondWithTemplate(HttpServletResponse resp, Map<String, Object> model, String templateName) {
MustacheFactory mf = new DefaultMustacheFactory();
Mustache template = mf.compile(templateName);
PrintWriter writer; PrintWriter writer;
try { try {
writer = resp.getWriter(); writer = resp.getWriter();
@@ -152,12 +154,30 @@ class MigrationWizardServlet extends HttpServlet {
private boolean namespaceValid = true; private boolean namespaceValid = true;
private boolean nameValid = true; private boolean nameValid = true;
public RepositoryLineEntry(XmlRepositoryV1UpdateStep.V1Repository repository) { public RepositoryLineEntry(V1Repository repository) {
this.id = repository.getId(); this.id = repository.getId();
this.type = repository.getType(); this.type = repository.getType();
this.path = repository.getPath(); this.path = repository.getType() + "/" + repository.getName();
this.namespace = repository.getNewNamespace(); this.namespace = computeNewNamespace(repository);
this.name = repository.getNewName(); this.name = computeNewName(repository);
}
private static String computeNewNamespace(V1Repository v1Repository) {
String[] nameParts = getNameParts(v1Repository.getName());
return nameParts.length > 1 ? nameParts[0] : v1Repository.getType();
}
private static String computeNewName(V1Repository v1Repository) {
String[] nameParts = getNameParts(v1Repository.getName());
return nameParts.length == 1 ? nameParts[0] : concatPathElements(nameParts);
}
private static String[] getNameParts(String v1Name) {
return v1Name.split("/");
}
private static String concatPathElements(String[] nameParts) {
return Arrays.stream(nameParts).skip(1).collect(Collectors.joining("_"));
} }
public String getId() { public String getId() {

View File

@@ -19,7 +19,7 @@ public class MigrationStrategyDao {
this.plan = store.getOptional().orElse(new RepositoryMigrationPlan()); this.plan = store.getOptional().orElse(new RepositoryMigrationPlan());
} }
public Optional<MigrationStrategy> get(String id) { public Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> get(String id) {
return plan.get(id); return plan.get(id);
} }

View File

@@ -13,53 +13,50 @@ import static java.util.Arrays.asList;
@XmlRootElement(name = "repository-migration") @XmlRootElement(name = "repository-migration")
class RepositoryMigrationPlan { class RepositoryMigrationPlan {
private List<RepositoryEntry> entries; private List<RepositoryMigrationEntry> entries;
RepositoryMigrationPlan() { RepositoryMigrationPlan() {
this(new RepositoryEntry[0]); this(new RepositoryMigrationEntry[0]);
} }
RepositoryMigrationPlan(RepositoryEntry... entries) { RepositoryMigrationPlan(RepositoryMigrationEntry... entries) {
this.entries = new ArrayList<>(asList(entries)); this.entries = new ArrayList<>(asList(entries));
} }
Optional<MigrationStrategy> get(String repositoryId) { Optional<RepositoryMigrationEntry> get(String repositoryId) {
return findEntry(repositoryId)
.map(RepositoryEntry::getDataMigrationStrategy);
}
public void set(String repositoryId, MigrationStrategy strategy, String newNamespace, String newName) {
Optional<RepositoryEntry> entry = findEntry(repositoryId);
if (entry.isPresent()) {
entry.get().setStrategy(strategy);
entry.get().setNewNamespace(newNamespace);
entry.get().setNewName(newName);
} else {
entries.add(new RepositoryEntry(repositoryId, strategy));
}
}
private Optional<RepositoryEntry> findEntry(String repositoryId) {
return entries.stream() return entries.stream()
.filter(repositoryEntry -> repositoryId.equals(repositoryEntry.repositoryId)) .filter(repositoryEntry -> repositoryId.equals(repositoryEntry.repositoryId))
.findFirst(); .findFirst();
} }
public void set(String repositoryId, MigrationStrategy strategy, String newNamespace, String newName) {
Optional<RepositoryMigrationEntry> entry = get(repositoryId);
if (entry.isPresent()) {
entry.get().setStrategy(strategy);
entry.get().setNewNamespace(newNamespace);
entry.get().setNewName(newName);
} else {
entries.add(new RepositoryMigrationEntry(repositoryId, strategy, newNamespace, newName));
}
}
@XmlRootElement(name = "entries") @XmlRootElement(name = "entries")
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)
static class RepositoryEntry { static class RepositoryMigrationEntry {
private String repositoryId; private String repositoryId;
private MigrationStrategy dataMigrationStrategy; private MigrationStrategy dataMigrationStrategy;
private String newNamespace; private String newNamespace;
private String newName; private String newName;
RepositoryEntry() { RepositoryMigrationEntry() {
} }
RepositoryEntry(String repositoryId, MigrationStrategy dataMigrationStrategy) { RepositoryMigrationEntry(String repositoryId, MigrationStrategy dataMigrationStrategy, String newNamespace, String newName) {
this.repositoryId = repositoryId; this.repositoryId = repositoryId;
this.dataMigrationStrategy = dataMigrationStrategy; this.dataMigrationStrategy = dataMigrationStrategy;
this.newNamespace = newNamespace;
this.newName = newName;
} }
public MigrationStrategy getDataMigrationStrategy() { public MigrationStrategy getDataMigrationStrategy() {

View File

@@ -0,0 +1,25 @@
package sonia.scm.update.repository;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "permissions")
class V1Permission {
private boolean groupPermission;
private String name;
private String type;
public boolean isGroupPermission() {
return groupPermission;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
}

View File

@@ -0,0 +1,92 @@
package sonia.scm.update.repository;
import sonia.scm.update.properties.V1Properties;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.List;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "repositories")
public class V1Repository {
private String contact;
private long creationDate;
private Long lastModified;
private String description;
private String id;
private String name;
private boolean isPublic;
private boolean archived;
private String type;
private List<V1Permission> permissions;
private V1Properties properties;
public V1Repository() {
}
public V1Repository(String id, String type, String name) {
this.id = id;
this.type = type;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public String getContact() {
return contact;
}
public long getCreationDate() {
return creationDate;
}
public Long getLastModified() {
return lastModified;
}
public String getDescription() {
return description;
}
public boolean isPublic() {
return isPublic;
}
public boolean isArchived() {
return archived;
}
public List<V1Permission> getPermissions() {
return permissions;
}
public V1Properties getProperties() {
return properties;
}
@Override
public String toString() {
return "V1Repository{" +
", contact='" + contact + '\'' +
", creationDate=" + creationDate +
", lastModified=" + lastModified +
", description='" + description + '\'' +
", id='" + id + '\'' +
", name='" + name + '\'' +
", isPublic=" + isPublic +
", archived=" + archived +
", type='" + type + '\'' +
'}';
}
}

View File

@@ -28,7 +28,6 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -104,7 +103,7 @@ public class XmlRepositoryV1UpdateStep implements UpdateStep {
JAXBContext jaxbContext = JAXBContext.newInstance(V1RepositoryDatabase.class); JAXBContext jaxbContext = JAXBContext.newInstance(V1RepositoryDatabase.class);
readV1Database(jaxbContext).ifPresent( readV1Database(jaxbContext).ifPresent(
v1Database -> { v1Database -> {
v1Database.repositoryList.repositories.forEach(this::readMigrationStrategy); v1Database.repositoryList.repositories.forEach(this::readMigrationEntry);
v1Database.repositoryList.repositories.forEach(this::update); v1Database.repositoryList.repositories.forEach(this::update);
backupOldRepositoriesFile(); backupOldRepositoriesFile();
} }
@@ -141,52 +140,56 @@ public class XmlRepositoryV1UpdateStep implements UpdateStep {
} }
private void update(V1Repository v1Repository) { private void update(V1Repository v1Repository) {
Optional<Path> destination = handleDataDirectory(v1Repository); RepositoryMigrationPlan.RepositoryMigrationEntry repositoryMigrationEntry = readMigrationEntry(v1Repository);
Optional<Path> destination = handleDataDirectory(v1Repository, repositoryMigrationEntry.getDataMigrationStrategy());
LOG.info("using strategy {} to migrate repository {} with id {} using new namespace {} and name {}",
repositoryMigrationEntry.getDataMigrationStrategy().getClass(),
v1Repository.getName(),
v1Repository.getId(),
repositoryMigrationEntry.getNewNamespace(),
repositoryMigrationEntry.getNewName());
destination.ifPresent( destination.ifPresent(
newPath -> { newPath -> {
Repository repository = new Repository( Repository repository = new Repository(
v1Repository.id, v1Repository.getId(),
v1Repository.type, v1Repository.getType(),
v1Repository.getNewNamespace(), repositoryMigrationEntry.getNewNamespace(),
v1Repository.getNewName(), repositoryMigrationEntry.getNewName(),
v1Repository.contact, v1Repository.getContact(),
v1Repository.description, v1Repository.getDescription(),
createPermissions(v1Repository)); createPermissions(v1Repository));
LOG.info("creating new repository {} with id {} from old repository {} in directory {}", repository.getNamespaceAndName(), repository.getId(), v1Repository.name, newPath); LOG.info("creating new repository {} with id {} from old repository {} in directory {}", repository.getNamespaceAndName(), repository.getId(), v1Repository.getName(), newPath);
repositoryDao.add(repository, newPath); repositoryDao.add(repository, newPath);
propertyStore.put(v1Repository.id, v1Repository.properties); propertyStore.put(v1Repository.getId(), v1Repository.getProperties());
} }
); );
} }
private Optional<Path> handleDataDirectory(V1Repository v1Repository) { private Optional<Path> handleDataDirectory(V1Repository v1Repository, MigrationStrategy dataMigrationStrategy) {
MigrationStrategy dataMigrationStrategy = readMigrationStrategy(v1Repository); return dataMigrationStrategy
LOG.info("using strategy {} to migrate repository {} with id {}", dataMigrationStrategy.getClass(), v1Repository.name, v1Repository.id); .from(injector)
return dataMigrationStrategy.from(injector).migrate(v1Repository.id, v1Repository.name, v1Repository.type); .migrate(v1Repository.getId(), v1Repository.getName(), v1Repository.getType());
} }
private MigrationStrategy readMigrationStrategy(V1Repository v1Repository) { private RepositoryMigrationPlan.RepositoryMigrationEntry readMigrationEntry(V1Repository v1Repository) {
return findMigrationStrategy(v1Repository) return findMigrationStrategy(v1Repository)
.orElseThrow(() -> new IllegalStateException("no strategy found for repository with id " + v1Repository.id + " and name " + v1Repository.name)); .orElseThrow(() -> new IllegalStateException("no strategy found for repository with id " + v1Repository.getId() + " and name " + v1Repository.getName()));
} }
private Optional<MigrationStrategy> findMigrationStrategy(V1Repository v1Repository) { private Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> findMigrationStrategy(V1Repository v1Repository) {
return migrationStrategyDao.get(v1Repository.id); return migrationStrategyDao.get(v1Repository.getId());
} }
private RepositoryPermission[] createPermissions(V1Repository v1Repository) { private RepositoryPermission[] createPermissions(V1Repository v1Repository) {
if (v1Repository.permissions == null) { return v1Repository.getPermissions()
return new RepositoryPermission[0];
}
return v1Repository.permissions
.stream() .stream()
.map(this::createPermission) .map(this::createPermission)
.toArray(RepositoryPermission[]::new); .toArray(RepositoryPermission[]::new);
} }
private RepositoryPermission createPermission(V1Permission v1Permission) { private RepositoryPermission createPermission(V1Permission v1Permission) {
LOG.info("creating permission {} for {}", v1Permission.type, v1Permission.name); LOG.info("creating permission {} for {}", v1Permission.getType(), v1Permission.getName());
return new RepositoryPermission(v1Permission.name, v1Permission.type, v1Permission.groupPermission); return new RepositoryPermission(v1Permission.getName(), v1Permission.getType(), v1Permission.isGroupPermission());
} }
private Optional<V1RepositoryDatabase> readV1Database(JAXBContext jaxbContext) throws JAXBException { private Optional<V1RepositoryDatabase> readV1Database(JAXBContext jaxbContext) throws JAXBException {
@@ -205,79 +208,6 @@ public class XmlRepositoryV1UpdateStep implements UpdateStep {
).toFile(); ).toFile();
} }
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "permissions")
private static class V1Permission {
private boolean groupPermission;
private String name;
private String type;
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "repositories")
public static class V1Repository {
private String contact;
private long creationDate;
private Long lastModified;
private String description;
private String id;
private String name;
private boolean isPublic;
private boolean archived;
private String type;
private List<V1Permission> permissions;
private V1Properties properties;
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public String getPath() {
return type + "/" + name;
}
public String getNewNamespace() {
String[] nameParts = getNameParts(name);
return nameParts.length > 1 ? nameParts[0] : type;
}
public String getNewName() {
String[] nameParts = getNameParts(name);
return nameParts.length == 1 ? nameParts[0] : concatPathElements(nameParts);
}
private String[] getNameParts(String v1Name) {
return v1Name.split("/");
}
private String concatPathElements(String[] nameParts) {
return Arrays.stream(nameParts).skip(1).collect(Collectors.joining("_"));
}
@Override
public String toString() {
return "V1Repository{" +
", contact='" + contact + '\'' +
", creationDate=" + creationDate +
", lastModified=" + lastModified +
", description='" + description + '\'' +
", id='" + id + '\'' +
", name='" + name + '\'' +
", isPublic=" + isPublic +
", archived=" + archived +
", type='" + type + '\'' +
'}';
}
}
private static class RepositoryList { private static class RepositoryList {
@XmlElement(name = "repository") @XmlElement(name = "repository")
private List<V1Repository> repositories; private List<V1Repository> repositories;

View File

@@ -0,0 +1,188 @@
package sonia.scm.update;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.update.repository.MigrationStrategy;
import sonia.scm.update.repository.MigrationStrategyDao;
import sonia.scm.update.repository.V1Repository;
import sonia.scm.update.repository.XmlRepositoryV1UpdateStep;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class MigrationWizardServletTest {
@Mock
XmlRepositoryV1UpdateStep updateStep;
@Mock
MigrationStrategyDao migrationStrategyDao;
@Mock
HttpServletRequest request;
@Mock
HttpServletResponse response;
String renderedTemplateName;
Map<String, Object> renderedModel;
MigrationWizardServlet servlet;
@BeforeEach
void initServlet() {
servlet = new MigrationWizardServlet(updateStep, migrationStrategyDao) {
@Override
void respondWithTemplate(HttpServletResponse resp, Map<String, Object> model, String templateName) {
renderedTemplateName = templateName;
renderedModel = model;
}
};
}
@Test
void shouldUseRepositoryTypeAsNamespaceForNamesWithSingleElement() {
when(updateStep.getRepositoriesWithoutMigrationStrategies()).thenReturn(
Collections.singletonList(new V1Repository("id", "git", "simple"))
);
servlet.doGet(request, response);
assertThat(renderedModel.get("repositories"))
.asList()
.extracting("namespace")
.contains("git");
assertThat(renderedModel.get("repositories"))
.asList()
.extracting("name")
.contains("simple");
}
@Test
void shouldUseDirectoriesForNamespaceAndNameForNamesWithTwoElements() {
when(updateStep.getRepositoriesWithoutMigrationStrategies()).thenReturn(
Collections.singletonList(new V1Repository("id", "git", "two/dirs"))
);
servlet.doGet(request, response);
assertThat(renderedModel.get("repositories"))
.asList()
.extracting("namespace")
.contains("two");
assertThat(renderedModel.get("repositories"))
.asList()
.extracting("name")
.contains("dirs");
}
@Test
void shouldUseDirectoriesForNamespaceAndConcatenatedNameForNamesWithMoreThanTwoElements() {
when(updateStep.getRepositoriesWithoutMigrationStrategies()).thenReturn(
Collections.singletonList(new V1Repository("id", "git", "more/than/two/dirs"))
);
servlet.doGet(request, response);
assertThat(renderedModel.get("repositories"))
.asList()
.extracting("namespace")
.contains("more");
assertThat(renderedModel.get("repositories"))
.asList()
.extracting("name")
.contains("than_two_dirs");
}
@Test
void shouldUseTypeAndNameAsPath() {
when(updateStep.getRepositoriesWithoutMigrationStrategies()).thenReturn(
Collections.singletonList(new V1Repository("id", "git", "name"))
);
servlet.doGet(request, response);
assertThat(renderedModel.get("repositories"))
.asList()
.extracting("path")
.contains("git/name");
}
@Test
void shouldKeepId() {
when(updateStep.getRepositoriesWithoutMigrationStrategies()).thenReturn(
Collections.singletonList(new V1Repository("id", "git", "name"))
);
servlet.doGet(request, response);
assertThat(renderedModel.get("repositories"))
.asList()
.extracting("id")
.contains("id");
}
@Test
void shouldNotBeInvalidAtFirstRequest() {
when(updateStep.getRepositoriesWithoutMigrationStrategies()).thenReturn(
Collections.singletonList(new V1Repository("id", "git", "name"))
);
servlet.doGet(request, response);
assertThat(renderedModel.get("validationErrorsFound")).isEqualTo(false);
assertThat(renderedModel.get("repositories"))
.asList()
.extracting("namespaceInvalid")
.contains(false);
assertThat(renderedModel.get("repositories"))
.asList()
.extracting("nameInvalid")
.contains(false);
}
@Test
void shouldValidateNamespaceAndNameOnPost() {
when(updateStep.getRepositoriesWithoutMigrationStrategies()).thenReturn(
Collections.singletonList(new V1Repository("id", "git", "name"))
);
doReturn("invalid namespace").when(request).getParameter("namespace-id");
doReturn("invalid name").when(request).getParameter("name-id");
doReturn("COPY").when(request).getParameter("strategy-id");
servlet.doPost(request, response);
assertThat(renderedModel.get("validationErrorsFound")).isEqualTo(true);
assertThat(renderedModel.get("repositories"))
.asList()
.extracting("namespaceInvalid")
.contains(true);
assertThat(renderedModel.get("repositories"))
.asList()
.extracting("nameInvalid")
.contains(true);
}
@Test
void shouldStoreValidMigration() {
when(updateStep.getRepositoriesWithoutMigrationStrategies()).thenReturn(
Collections.singletonList(new V1Repository("id", "git", "name"))
);
doReturn("namespace").when(request).getParameter("namespace-id");
doReturn("name").when(request).getParameter("name-id");
doReturn("COPY").when(request).getParameter("strategy-id");
servlet.doPost(request, response);
verify(migrationStrategyDao).set("id", MigrationStrategy.COPY, "namespace", "name");
}
}

View File

@@ -11,8 +11,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.SCMContextProvider; import sonia.scm.SCMContextProvider;
import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.store.ConfigurationStoreFactory;
import sonia.scm.store.JAXBConfigurationStoreFactory; import sonia.scm.store.JAXBConfigurationStoreFactory;
import sonia.scm.update.repository.MigrationStrategy;
import sonia.scm.update.repository.MigrationStrategyDao;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
import java.nio.file.Path; import java.nio.file.Path;
@@ -37,23 +35,31 @@ class MigrationStrategyDaoTest {
} }
@Test @Test
void shouldReturnEmptyOptionalWhenStoreIsEmpty() throws JAXBException { void shouldReturnEmptyOptionalWhenStoreIsEmpty() {
MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory); MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory);
Optional<MigrationStrategy> strategy = dao.get("any"); Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> entry = dao.get("any");
Assertions.assertThat(strategy).isEmpty(); Assertions.assertThat(entry).isEmpty();
} }
@Test @Test
void shouldReturnNewValue() throws JAXBException { void shouldReturnNewValue() {
MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory); MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory);
dao.set("id", INLINE); dao.set("id", INLINE, "space", "name");
Optional<MigrationStrategy> strategy = dao.get("id"); Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> entry = dao.get("id");
Assertions.assertThat(strategy).contains(INLINE); Assertions.assertThat(entry)
.map(RepositoryMigrationPlan.RepositoryMigrationEntry::getDataMigrationStrategy)
.contains(INLINE);
Assertions.assertThat(entry)
.map(RepositoryMigrationPlan.RepositoryMigrationEntry::getNewNamespace)
.contains("space");
Assertions.assertThat(entry)
.map(RepositoryMigrationPlan.RepositoryMigrationEntry::getNewName)
.contains("name");
} }
@Nested @Nested
@@ -62,16 +68,24 @@ class MigrationStrategyDaoTest {
void initExistingDatabase() throws JAXBException { void initExistingDatabase() throws JAXBException {
MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory); MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory);
dao.set("id", INLINE); dao.set("id", INLINE, "space", "name");
} }
@Test @Test
void shouldFindExistingValue() throws JAXBException { void shouldFindExistingValue() throws JAXBException {
MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory); MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory);
Optional<MigrationStrategy> strategy = dao.get("id"); Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> entry = dao.get("id");
Assertions.assertThat(strategy).contains(INLINE); Assertions.assertThat(entry)
.map(RepositoryMigrationPlan.RepositoryMigrationEntry::getDataMigrationStrategy)
.contains(INLINE);
Assertions.assertThat(entry)
.map(RepositoryMigrationPlan.RepositoryMigrationEntry::getNewNamespace)
.contains("space");
Assertions.assertThat(entry)
.map(RepositoryMigrationPlan.RepositoryMigrationEntry::getNewName)
.contains("name");
} }
} }
} }

View File

@@ -11,6 +11,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor; import org.mockito.Captor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryPermission; import sonia.scm.repository.RepositoryPermission;
import sonia.scm.repository.xml.XmlRepositoryDAO; import sonia.scm.repository.xml.XmlRepositoryDAO;
@@ -25,7 +26,6 @@ import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional; import java.util.Optional;
import static java.util.Optional.empty; import static java.util.Optional.empty;
@@ -38,9 +38,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static sonia.scm.update.repository.MigrationStrategy.COPY;
import static sonia.scm.update.repository.MigrationStrategy.DELETE;
import static sonia.scm.update.repository.MigrationStrategy.INLINE;
import static sonia.scm.update.repository.MigrationStrategy.MOVE; import static sonia.scm.update.repository.MigrationStrategy.MOVE;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@@ -92,9 +89,14 @@ class XmlRepositoryV1UpdateStepTest {
@BeforeEach @BeforeEach
void createMigrationPlan() { void createMigrationPlan() {
lenient().when(migrationStrategyDao.get("3b91caa5-59c3-448f-920b-769aaa56b761")).thenReturn(of(MOVE)); Answer<Object> planAnswer = invocation -> {
lenient().when(migrationStrategyDao.get("c1597b4f-a9f0-49f7-ad1f-37d3aae1c55f")).thenReturn(of(COPY)); String id = invocation.getArgument(0).toString();
lenient().when(migrationStrategyDao.get("454972da-faf9-4437-b682-dc4a4e0aa8eb")).thenReturn(of(INLINE)); return of(new RepositoryMigrationPlan.RepositoryMigrationEntry(id, MOVE, "namespace-" + id, "name-" + id));
};
lenient().when(migrationStrategyDao.get("3b91caa5-59c3-448f-920b-769aaa56b761")).thenAnswer(planAnswer);
lenient().when(migrationStrategyDao.get("c1597b4f-a9f0-49f7-ad1f-37d3aae1c55f")).thenAnswer(planAnswer);
lenient().when(migrationStrategyDao.get("454972da-faf9-4437-b682-dc4a4e0aa8eb")).thenAnswer(planAnswer);
} }
@Test @Test
@@ -107,56 +109,20 @@ class XmlRepositoryV1UpdateStepTest {
void shouldMapAttributes() throws JAXBException { void shouldMapAttributes() throws JAXBException {
updateStep.doUpdate(); updateStep.doUpdate();
Optional<Repository> repository = findByNamespace("git"); Optional<Repository> repository = findByNamespace("namespace-3b91caa5-59c3-448f-920b-769aaa56b761");
assertThat(repository) assertThat(repository)
.get() .get()
.hasFieldOrPropertyWithValue("type", "git") .hasFieldOrPropertyWithValue("type", "git")
.hasFieldOrPropertyWithValue("contact", "arthur@dent.uk") .hasFieldOrPropertyWithValue("contact", "arthur@dent.uk")
.hasFieldOrPropertyWithValue("description", "A simple repository without directories."); .hasFieldOrPropertyWithValue("description", "A repository with two folders.");
}
@Test
void shouldUseRepositoryTypeAsNamespaceForNamesWithSingleElement() throws JAXBException {
updateStep.doUpdate();
Optional<Repository> repository = findByNamespace("git");
assertThat(repository)
.get()
.hasFieldOrPropertyWithValue("namespace", "git")
.hasFieldOrPropertyWithValue("name", "simple");
}
@Test
void shouldUseDirectoriesForNamespaceAndNameForNamesWithTwoElements() throws JAXBException {
updateStep.doUpdate();
Optional<Repository> repository = findByNamespace("one");
assertThat(repository)
.get()
.hasFieldOrPropertyWithValue("namespace", "one")
.hasFieldOrPropertyWithValue("name", "directory");
}
@Test
void shouldUseDirectoriesForNamespaceAndConcatenatedNameForNamesWithMoreThanTwoElements() throws JAXBException {
updateStep.doUpdate();
Optional<Repository> repository = findByNamespace("some");
assertThat(repository)
.get()
.hasFieldOrPropertyWithValue("namespace", "some")
.hasFieldOrPropertyWithValue("name", "more_directories_than_one");
} }
@Test @Test
void shouldMapPermissions() throws JAXBException { void shouldMapPermissions() throws JAXBException {
updateStep.doUpdate(); updateStep.doUpdate();
Optional<Repository> repository = findByNamespace("git"); Optional<Repository> repository = findByNamespace("namespace-454972da-faf9-4437-b682-dc4a4e0aa8eb");
assertThat(repository.get().getPermissions()) assertThat(repository.get().getPermissions())
.hasSize(3) .hasSize(3)
@@ -179,7 +145,7 @@ class XmlRepositoryV1UpdateStepTest {
@Test @Test
void shouldUseDirectoryFromStrategy(@TempDirectory.TempDir Path tempDir) throws JAXBException { void shouldUseDirectoryFromStrategy(@TempDirectory.TempDir Path tempDir) throws JAXBException {
Path targetDir = tempDir.resolve("someDir"); Path targetDir = tempDir.resolve("someDir");
MigrationStrategy.Instance strategyMock = injectorMock.getInstance(InlineMigrationStrategy.class); MigrationStrategy.Instance strategyMock = injectorMock.getInstance(MoveMigrationStrategy.class);
when(strategyMock.migrate("454972da-faf9-4437-b682-dc4a4e0aa8eb", "simple", "git")).thenReturn(of(targetDir)); when(strategyMock.migrate("454972da-faf9-4437-b682-dc4a4e0aa8eb", "simple", "git")).thenReturn(of(targetDir));
updateStep.doUpdate(); updateStep.doUpdate();