diff --git a/scm-webapp/src/main/java/sonia/scm/update/MigrationWizardServlet.java b/scm-webapp/src/main/java/sonia/scm/update/MigrationWizardServlet.java index cd5f640c7b..f26b1afac5 100644 --- a/scm-webapp/src/main/java/sonia/scm/update/MigrationWizardServlet.java +++ b/scm-webapp/src/main/java/sonia/scm/update/MigrationWizardServlet.java @@ -3,12 +3,14 @@ package sonia.scm.update; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheFactory; +import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.boot.RestartEvent; import sonia.scm.event.ScmEventBus; 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 sonia.scm.util.ValidationUtil; @@ -19,6 +21,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -62,9 +65,7 @@ class MigrationWizardServlet extends HttpServlet { .stream() .anyMatch(entry -> entry.isNamespaceInvalid() || entry.isNameInvalid())); - MustacheFactory mf = new DefaultMustacheFactory(); - Mustache template = mf.compile("templates/repository-migration.mustache"); - respondWithTemplate(resp, model, template); + respondWithTemplate(resp, model, "templates/repository-migration.mustache"); } @Override @@ -76,6 +77,7 @@ class MigrationWizardServlet extends HttpServlet { String id = repositoryLineEntry.getId(); String namespace = req.getParameter("namespace-" + id); String name = req.getParameter("name-" + id); + String strategy = req.getParameter("strategy-" + id); repositoryLineEntry.setNamespace(namespace); 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 model = Collections.singletonMap("contextPath", req.getContextPath()); - respondWithTemplate(resp, model, template); - - resp.setStatus(200); + respondWithTemplate(resp, model, "templates/repository-migration-restart.mustache"); ScmEventBus.getInstance().post(new RestartEvent(MigrationWizardServlet.class, "wrote migration data")); } private List getRepositoryLineEntries() { - List repositoriesWithoutMigrationStrategies = + List repositoriesWithoutMigrationStrategies = repositoryV1UpdateStep.getRepositoriesWithoutMigrationStrategies(); return repositoriesWithoutMigrationStrategies.stream() .map(RepositoryLineEntry::new) @@ -129,7 +127,11 @@ class MigrationWizardServlet extends HttpServlet { return MigrationStrategy.values(); } - private void respondWithTemplate(HttpServletResponse resp, Map model, Mustache template) { + @VisibleForTesting + void respondWithTemplate(HttpServletResponse resp, Map model, String templateName) { + MustacheFactory mf = new DefaultMustacheFactory(); + Mustache template = mf.compile(templateName); + PrintWriter writer; try { writer = resp.getWriter(); @@ -152,12 +154,30 @@ class MigrationWizardServlet extends HttpServlet { private boolean namespaceValid = true; private boolean nameValid = true; - public RepositoryLineEntry(XmlRepositoryV1UpdateStep.V1Repository repository) { + public RepositoryLineEntry(V1Repository repository) { this.id = repository.getId(); this.type = repository.getType(); - this.path = repository.getPath(); - this.namespace = repository.getNewNamespace(); - this.name = repository.getNewName(); + this.path = repository.getType() + "/" + repository.getName(); + this.namespace = computeNewNamespace(repository); + 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() { diff --git a/scm-webapp/src/main/java/sonia/scm/update/repository/MigrationStrategyDao.java b/scm-webapp/src/main/java/sonia/scm/update/repository/MigrationStrategyDao.java index 07c049bc84..5670ce458a 100644 --- a/scm-webapp/src/main/java/sonia/scm/update/repository/MigrationStrategyDao.java +++ b/scm-webapp/src/main/java/sonia/scm/update/repository/MigrationStrategyDao.java @@ -19,7 +19,7 @@ public class MigrationStrategyDao { this.plan = store.getOptional().orElse(new RepositoryMigrationPlan()); } - public Optional get(String id) { + public Optional get(String id) { return plan.get(id); } diff --git a/scm-webapp/src/main/java/sonia/scm/update/repository/RepositoryMigrationPlan.java b/scm-webapp/src/main/java/sonia/scm/update/repository/RepositoryMigrationPlan.java index c839eba5b0..3c4bf36342 100644 --- a/scm-webapp/src/main/java/sonia/scm/update/repository/RepositoryMigrationPlan.java +++ b/scm-webapp/src/main/java/sonia/scm/update/repository/RepositoryMigrationPlan.java @@ -13,53 +13,50 @@ import static java.util.Arrays.asList; @XmlRootElement(name = "repository-migration") class RepositoryMigrationPlan { - private List entries; + private List entries; RepositoryMigrationPlan() { - this(new RepositoryEntry[0]); + this(new RepositoryMigrationEntry[0]); } - RepositoryMigrationPlan(RepositoryEntry... entries) { + RepositoryMigrationPlan(RepositoryMigrationEntry... entries) { this.entries = new ArrayList<>(asList(entries)); } - Optional get(String repositoryId) { - return findEntry(repositoryId) - .map(RepositoryEntry::getDataMigrationStrategy); - } - - public void set(String repositoryId, MigrationStrategy strategy, String newNamespace, String newName) { - Optional 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 findEntry(String repositoryId) { + Optional get(String repositoryId) { return entries.stream() .filter(repositoryEntry -> repositoryId.equals(repositoryEntry.repositoryId)) .findFirst(); } + public void set(String repositoryId, MigrationStrategy strategy, String newNamespace, String newName) { + Optional 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") @XmlAccessorType(XmlAccessType.FIELD) - static class RepositoryEntry { + static class RepositoryMigrationEntry { private String repositoryId; private MigrationStrategy dataMigrationStrategy; private String newNamespace; private String newName; - RepositoryEntry() { + RepositoryMigrationEntry() { } - RepositoryEntry(String repositoryId, MigrationStrategy dataMigrationStrategy) { + RepositoryMigrationEntry(String repositoryId, MigrationStrategy dataMigrationStrategy, String newNamespace, String newName) { this.repositoryId = repositoryId; this.dataMigrationStrategy = dataMigrationStrategy; + this.newNamespace = newNamespace; + this.newName = newName; } public MigrationStrategy getDataMigrationStrategy() { diff --git a/scm-webapp/src/main/java/sonia/scm/update/repository/V1Permission.java b/scm-webapp/src/main/java/sonia/scm/update/repository/V1Permission.java new file mode 100644 index 0000000000..97426f1a83 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/update/repository/V1Permission.java @@ -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; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/update/repository/V1Repository.java b/scm-webapp/src/main/java/sonia/scm/update/repository/V1Repository.java new file mode 100644 index 0000000000..256e2ad397 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/update/repository/V1Repository.java @@ -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 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 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 + '\'' + + '}'; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/update/repository/XmlRepositoryV1UpdateStep.java b/scm-webapp/src/main/java/sonia/scm/update/repository/XmlRepositoryV1UpdateStep.java index 54d0d7c52f..2f16dc74f6 100644 --- a/scm-webapp/src/main/java/sonia/scm/update/repository/XmlRepositoryV1UpdateStep.java +++ b/scm-webapp/src/main/java/sonia/scm/update/repository/XmlRepositoryV1UpdateStep.java @@ -28,7 +28,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -104,7 +103,7 @@ public class XmlRepositoryV1UpdateStep implements UpdateStep { JAXBContext jaxbContext = JAXBContext.newInstance(V1RepositoryDatabase.class); readV1Database(jaxbContext).ifPresent( v1Database -> { - v1Database.repositoryList.repositories.forEach(this::readMigrationStrategy); + v1Database.repositoryList.repositories.forEach(this::readMigrationEntry); v1Database.repositoryList.repositories.forEach(this::update); backupOldRepositoriesFile(); } @@ -141,52 +140,56 @@ public class XmlRepositoryV1UpdateStep implements UpdateStep { } private void update(V1Repository v1Repository) { - Optional destination = handleDataDirectory(v1Repository); + RepositoryMigrationPlan.RepositoryMigrationEntry repositoryMigrationEntry = readMigrationEntry(v1Repository); + Optional 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( newPath -> { Repository repository = new Repository( - v1Repository.id, - v1Repository.type, - v1Repository.getNewNamespace(), - v1Repository.getNewName(), - v1Repository.contact, - v1Repository.description, + v1Repository.getId(), + v1Repository.getType(), + repositoryMigrationEntry.getNewNamespace(), + repositoryMigrationEntry.getNewName(), + v1Repository.getContact(), + v1Repository.getDescription(), 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); - propertyStore.put(v1Repository.id, v1Repository.properties); + propertyStore.put(v1Repository.getId(), v1Repository.getProperties()); } ); } - private Optional handleDataDirectory(V1Repository v1Repository) { - MigrationStrategy dataMigrationStrategy = readMigrationStrategy(v1Repository); - LOG.info("using strategy {} to migrate repository {} with id {}", dataMigrationStrategy.getClass(), v1Repository.name, v1Repository.id); - return dataMigrationStrategy.from(injector).migrate(v1Repository.id, v1Repository.name, v1Repository.type); + private Optional handleDataDirectory(V1Repository v1Repository, MigrationStrategy dataMigrationStrategy) { + return dataMigrationStrategy + .from(injector) + .migrate(v1Repository.getId(), v1Repository.getName(), v1Repository.getType()); } - private MigrationStrategy readMigrationStrategy(V1Repository v1Repository) { + private RepositoryMigrationPlan.RepositoryMigrationEntry readMigrationEntry(V1Repository 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 findMigrationStrategy(V1Repository v1Repository) { - return migrationStrategyDao.get(v1Repository.id); + private Optional findMigrationStrategy(V1Repository v1Repository) { + return migrationStrategyDao.get(v1Repository.getId()); } private RepositoryPermission[] createPermissions(V1Repository v1Repository) { - if (v1Repository.permissions == null) { - return new RepositoryPermission[0]; - } - return v1Repository.permissions + return v1Repository.getPermissions() .stream() .map(this::createPermission) .toArray(RepositoryPermission[]::new); } private RepositoryPermission createPermission(V1Permission v1Permission) { - LOG.info("creating permission {} for {}", v1Permission.type, v1Permission.name); - return new RepositoryPermission(v1Permission.name, v1Permission.type, v1Permission.groupPermission); + LOG.info("creating permission {} for {}", v1Permission.getType(), v1Permission.getName()); + return new RepositoryPermission(v1Permission.getName(), v1Permission.getType(), v1Permission.isGroupPermission()); } private Optional readV1Database(JAXBContext jaxbContext) throws JAXBException { @@ -205,79 +208,6 @@ public class XmlRepositoryV1UpdateStep implements UpdateStep { ).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 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 { @XmlElement(name = "repository") private List repositories; diff --git a/scm-webapp/src/test/java/sonia/scm/update/MigrationWizardServletTest.java b/scm-webapp/src/test/java/sonia/scm/update/MigrationWizardServletTest.java new file mode 100644 index 0000000000..e40179eb7f --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/update/MigrationWizardServletTest.java @@ -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 renderedModel; + + MigrationWizardServlet servlet; + + @BeforeEach + void initServlet() { + servlet = new MigrationWizardServlet(updateStep, migrationStrategyDao) { + @Override + void respondWithTemplate(HttpServletResponse resp, Map 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"); + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/update/repository/MigrationStrategyDaoTest.java b/scm-webapp/src/test/java/sonia/scm/update/repository/MigrationStrategyDaoTest.java index e3fd4457b6..5829f5b98f 100644 --- a/scm-webapp/src/test/java/sonia/scm/update/repository/MigrationStrategyDaoTest.java +++ b/scm-webapp/src/test/java/sonia/scm/update/repository/MigrationStrategyDaoTest.java @@ -11,8 +11,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import sonia.scm.SCMContextProvider; import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.store.JAXBConfigurationStoreFactory; -import sonia.scm.update.repository.MigrationStrategy; -import sonia.scm.update.repository.MigrationStrategyDao; import javax.xml.bind.JAXBException; import java.nio.file.Path; @@ -37,23 +35,31 @@ class MigrationStrategyDaoTest { } @Test - void shouldReturnEmptyOptionalWhenStoreIsEmpty() throws JAXBException { + void shouldReturnEmptyOptionalWhenStoreIsEmpty() { MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory); - Optional strategy = dao.get("any"); + Optional entry = dao.get("any"); - Assertions.assertThat(strategy).isEmpty(); + Assertions.assertThat(entry).isEmpty(); } @Test - void shouldReturnNewValue() throws JAXBException { + void shouldReturnNewValue() { MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory); - dao.set("id", INLINE); + dao.set("id", INLINE, "space", "name"); - Optional strategy = dao.get("id"); + Optional 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 @@ -62,16 +68,24 @@ class MigrationStrategyDaoTest { void initExistingDatabase() throws JAXBException { MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory); - dao.set("id", INLINE); + dao.set("id", INLINE, "space", "name"); } @Test void shouldFindExistingValue() throws JAXBException { MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory); - Optional strategy = dao.get("id"); + Optional 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"); } } } diff --git a/scm-webapp/src/test/java/sonia/scm/update/repository/XmlRepositoryV1UpdateStepTest.java b/scm-webapp/src/test/java/sonia/scm/update/repository/XmlRepositoryV1UpdateStepTest.java index 76a74993b3..6c977a3cb6 100644 --- a/scm-webapp/src/test/java/sonia/scm/update/repository/XmlRepositoryV1UpdateStepTest.java +++ b/scm-webapp/src/test/java/sonia/scm/update/repository/XmlRepositoryV1UpdateStepTest.java @@ -11,6 +11,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.stubbing.Answer; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryPermission; import sonia.scm.repository.xml.XmlRepositoryDAO; @@ -25,7 +26,6 @@ import java.io.IOException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Optional; 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.verify; 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; @ExtendWith(MockitoExtension.class) @@ -92,9 +89,14 @@ class XmlRepositoryV1UpdateStepTest { @BeforeEach void createMigrationPlan() { - lenient().when(migrationStrategyDao.get("3b91caa5-59c3-448f-920b-769aaa56b761")).thenReturn(of(MOVE)); - lenient().when(migrationStrategyDao.get("c1597b4f-a9f0-49f7-ad1f-37d3aae1c55f")).thenReturn(of(COPY)); - lenient().when(migrationStrategyDao.get("454972da-faf9-4437-b682-dc4a4e0aa8eb")).thenReturn(of(INLINE)); + Answer planAnswer = invocation -> { + String id = invocation.getArgument(0).toString(); + 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 @@ -107,56 +109,20 @@ class XmlRepositoryV1UpdateStepTest { void shouldMapAttributes() throws JAXBException { updateStep.doUpdate(); - Optional repository = findByNamespace("git"); + Optional repository = findByNamespace("namespace-3b91caa5-59c3-448f-920b-769aaa56b761"); assertThat(repository) .get() .hasFieldOrPropertyWithValue("type", "git") .hasFieldOrPropertyWithValue("contact", "arthur@dent.uk") - .hasFieldOrPropertyWithValue("description", "A simple repository without directories."); - } - - @Test - void shouldUseRepositoryTypeAsNamespaceForNamesWithSingleElement() throws JAXBException { - updateStep.doUpdate(); - - Optional repository = findByNamespace("git"); - - assertThat(repository) - .get() - .hasFieldOrPropertyWithValue("namespace", "git") - .hasFieldOrPropertyWithValue("name", "simple"); - } - - @Test - void shouldUseDirectoriesForNamespaceAndNameForNamesWithTwoElements() throws JAXBException { - updateStep.doUpdate(); - - Optional repository = findByNamespace("one"); - - assertThat(repository) - .get() - .hasFieldOrPropertyWithValue("namespace", "one") - .hasFieldOrPropertyWithValue("name", "directory"); - } - - @Test - void shouldUseDirectoriesForNamespaceAndConcatenatedNameForNamesWithMoreThanTwoElements() throws JAXBException { - updateStep.doUpdate(); - - Optional repository = findByNamespace("some"); - - assertThat(repository) - .get() - .hasFieldOrPropertyWithValue("namespace", "some") - .hasFieldOrPropertyWithValue("name", "more_directories_than_one"); + .hasFieldOrPropertyWithValue("description", "A repository with two folders."); } @Test void shouldMapPermissions() throws JAXBException { updateStep.doUpdate(); - Optional repository = findByNamespace("git"); + Optional repository = findByNamespace("namespace-454972da-faf9-4437-b682-dc4a4e0aa8eb"); assertThat(repository.get().getPermissions()) .hasSize(3) @@ -179,7 +145,7 @@ class XmlRepositoryV1UpdateStepTest { @Test void shouldUseDirectoryFromStrategy(@TempDirectory.TempDir Path tempDir) throws JAXBException { 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)); updateStep.doUpdate();