Merge with 2.0.0-m3

This commit is contained in:
René Pfeuffer
2019-06-11 13:55:43 +02:00
110 changed files with 7002 additions and 4053 deletions

View File

@@ -89,8 +89,7 @@ class DefaultRepositoryRoleManagerTest {
@BeforeEach
void authorizeUser() {
when(subject.isPermitted("repositoryRole:read")).thenReturn(true);
when(subject.isPermitted("repositoryRole:modify")).thenReturn(true);
when(subject.isPermitted("repositoryRole:write")).thenReturn(true);
}
@Test
@@ -184,8 +183,15 @@ class DefaultRepositoryRoleManagerTest {
}
@Test
void shouldThrowException_forGet() {
assertThrows(UnauthorizedException.class, () -> manager.get("any"));
void shouldReturnNull_forNotExistingRole() {
RepositoryRole role = manager.get("noSuchRole");
assertThat(role).isNull();
}
@Test
void shouldReturnRole_forExistingRole() {
RepositoryRole role = manager.get(CUSTOM_ROLE_NAME);
assertThat(role).isNotNull();
}
@Test
@@ -201,18 +207,25 @@ class DefaultRepositoryRoleManagerTest {
}
@Test
void shouldReturnEmptyList() {
assertThat(manager.getAll()).isEmpty();
void shouldReturnAllRoles() {
List<RepositoryRole> allRoles = manager.getAll();
assertThat(allRoles).containsExactly(CUSTOM_ROLE, SYSTEM_ROLE);
}
@Test
void shouldReturnEmptyFilteredList() {
assertThat(manager.getAll(x -> true, null)).isEmpty();
void shouldReturnFilteredList() {
Collection<RepositoryRole> allRoles = manager.getAll(role -> CUSTOM_ROLE_NAME.equals(role.getName()), null);
assertThat(allRoles).containsExactly(CUSTOM_ROLE);
}
@Test
void shouldReturnEmptyPaginatedList() {
assertThat(manager.getAll(1, 1)).isEmpty();
void shouldReturnPaginatedRoles() {
Collection<RepositoryRole> allRoles =
manager.getAll(
Comparator.comparing(RepositoryRole::getType),
1, 1
);
assertThat(allRoles).containsExactly(CUSTOM_ROLE);
}
}
}

View File

@@ -0,0 +1,76 @@
package sonia.scm.update;
import com.google.common.io.Resources;
import org.mockito.Mockito;
import sonia.scm.SCMContextProvider;
import sonia.scm.security.AssignedPermission;
import sonia.scm.security.DefaultKeyGenerator;
import sonia.scm.store.ConfigurationEntryStore;
import sonia.scm.store.ConfigurationEntryStoreFactory;
import sonia.scm.store.JAXBConfigurationEntryStoreFactory;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
public class UpdateStepTestUtil {
private final SCMContextProvider contextProvider;
private final Path tempDir;
private final ConfigurationEntryStoreFactory storeFactory;
public UpdateStepTestUtil(Path tempDir) {
this.tempDir = tempDir;
contextProvider = Mockito.mock(SCMContextProvider.class);
storeFactory = new JAXBConfigurationEntryStoreFactory(contextProvider, null, new DefaultKeyGenerator());
lenient().when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
lenient().when(contextProvider.resolve(any())).thenAnswer(invocation -> tempDir.resolve(invocation.getArgument(0).toString()));
}
public SCMContextProvider getContextProvider() {
return contextProvider;
}
public ConfigurationEntryStoreFactory getStoreFactory() {
return storeFactory;
}
public void copyConfigFile(String fileName) throws IOException {
Path configDir = tempDir.resolve("config");
Files.createDirectories(configDir);
copyTestDatabaseFile(configDir, fileName);
}
public void copyConfigFile(String fileName, String targetFileName) throws IOException {
Path configDir = tempDir.resolve("config");
Files.createDirectories(configDir);
copyTestDatabaseFile(configDir, fileName, targetFileName);
}
public ConfigurationEntryStore<AssignedPermission> getStoreForConfigFile(String name) {
return storeFactory
.withType(AssignedPermission.class)
.withName(name)
.build();
}
public Path getFile(String name) {
return tempDir.resolve("config").resolve(name);
}
private void copyTestDatabaseFile(Path configDir, String fileName) throws IOException {
Path targetFileName = Paths.get(fileName).getFileName();
copyTestDatabaseFile(configDir, fileName, targetFileName.toString());
}
private void copyTestDatabaseFile(Path configDir, String fileName, String targetFileName) throws IOException {
URL url = Resources.getResource(fileName);
Files.copy(url.openStream(), configDir.resolve(targetFileName));
}
}

View File

@@ -0,0 +1,136 @@
package sonia.scm.update.group;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junitpioneer.jupiter.TempDirectory;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.group.Group;
import sonia.scm.group.xml.XmlGroupDAO;
import sonia.scm.update.UpdateStepTestUtil;
import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.linesOf;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
@ExtendWith(TempDirectory.class)
class XmlGroupV1UpdateStepTest {
@Mock
XmlGroupDAO groupDAO;
@Captor
ArgumentCaptor<Group> groupCaptor;
XmlGroupV1UpdateStep updateStep;
private UpdateStepTestUtil testUtil;
@BeforeEach
void mockScmHome(@TempDirectory.TempDir Path tempDir) {
testUtil = new UpdateStepTestUtil(tempDir);
updateStep = new XmlGroupV1UpdateStep(testUtil.getContextProvider(), groupDAO, testUtil.getStoreFactory());
}
@Nested
class WithExistingDatabase {
@BeforeEach
void captureStoredRepositories() {
doNothing().when(groupDAO).add(groupCaptor.capture());
}
@BeforeEach
void createGroupV1XML() throws IOException {
testUtil.copyConfigFile("sonia/scm/update/group/groups.xml");
}
@Test
void shouldCreateNewGroupFromGroupsV1Xml() throws JAXBException {
updateStep.doUpdate();
verify(groupDAO, times(2)).add(any());
}
@Test
void shouldMapAttributesFromGroupsV1Xml() throws JAXBException {
updateStep.doUpdate();
Optional<Group> group = groupCaptor.getAllValues().stream().filter(u -> u.getName().equals("normals")).findFirst();
assertThat(group)
.get()
.hasFieldOrPropertyWithValue("name", "normals")
.hasFieldOrPropertyWithValue("description", "Normal people")
.hasFieldOrPropertyWithValue("type", "xml")
.hasFieldOrPropertyWithValue("members", asList("trillian", "dent"))
.hasFieldOrPropertyWithValue("lastModified", 1559550955883L)
.hasFieldOrPropertyWithValue("creationDate", 1559548942457L);
}
@Test
void shouldExtractProperties() throws JAXBException {
updateStep.doUpdate();
Path propertiesFile = testUtil.getFile("group-properties-v1.xml");
assertThat(propertiesFile)
.exists();
assertThat(linesOf(propertiesFile.toFile()))
.extracting(String::trim)
.containsSequence(
"<key>normals</key>",
"<value>",
"<item>",
"<key>mostly</key>",
"<value>humans</value>",
"</item>",
"</value>");
}
}
@Nested
class WithExistingDatabaseWithEmptyList {
@BeforeEach
void createGroupV1XML() throws IOException {
testUtil.copyConfigFile("sonia/scm/update/group/groups_empty_groups.xml", "groups.xml");
}
@Test
void shouldCreateNewGroupFromGroupsV1Xml() throws JAXBException {
updateStep.doUpdate();
verify(groupDAO, times(0)).add(any());
}
}
@Nested
class WithExistingDatabaseWithoutList {
@BeforeEach
void createGroupV1XML() throws IOException {
testUtil.copyConfigFile("sonia/scm/update/group/groups_no_groups.xml", "groups.xml");
}
@Test
void shouldCreateNewGroupFromGroupsV1Xml() throws JAXBException {
updateStep.doUpdate();
verify(groupDAO, times(0)).add(any());
}
}
@Test
void shouldNotFailForMissingConfigDir() throws JAXBException {
updateStep.doUpdate();
}
}

View File

@@ -0,0 +1,89 @@
package sonia.scm.update.repository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junitpioneer.jupiter.TempDirectory;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.SCMContextProvider;
import sonia.scm.repository.RepositoryLocationResolver;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(TempDirectory.class)
@ExtendWith(MockitoExtension.class)
class CopyMigrationStrategyTest {
@Mock
SCMContextProvider contextProvider;
@Mock
RepositoryLocationResolver locationResolver;
@BeforeEach
void mockContextProvider(@TempDirectory.TempDir Path tempDir) {
when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
}
@BeforeEach
void createV1Home(@TempDirectory.TempDir Path tempDir) throws IOException {
V1RepositoryFileSystem.createV1Home(tempDir);
}
@BeforeEach
void mockLocationResolver(@TempDirectory.TempDir Path tempDir) {
RepositoryLocationResolver.RepositoryLocationResolverInstance instanceMock = mock(RepositoryLocationResolver.RepositoryLocationResolverInstance.class);
when(locationResolver.forClass(Path.class)).thenReturn(instanceMock);
when(instanceMock.getLocation(anyString())).thenAnswer(invocation -> tempDir.resolve((String) invocation.getArgument(0)));
}
@Test
void shouldUseStandardDirectory(@TempDirectory.TempDir Path tempDir) {
Path target = new CopyMigrationStrategy(contextProvider, locationResolver).migrate("b4f-a9f0-49f7-ad1f-37d3aae1c55f", "some/more/directories/than/one", "git");
assertThat(target).isEqualTo(tempDir.resolve("b4f-a9f0-49f7-ad1f-37d3aae1c55f"));
}
@Test
void shouldCopyDataDirectory(@TempDirectory.TempDir Path tempDir) {
Path target = new CopyMigrationStrategy(contextProvider, locationResolver).migrate("b4f-a9f0-49f7-ad1f-37d3aae1c55f", "some/more/directories/than/one", "git");
assertThat(target.resolve("data")).exists();
Path originalDataDir = tempDir
.resolve("repositories")
.resolve("git")
.resolve("some")
.resolve("more")
.resolve("directories")
.resolve("than")
.resolve("one");
assertDirectoriesEqual(target.resolve("data"), originalDataDir);
}
private void assertDirectoriesEqual(Path targetDataDir, Path originalDataDir) {
Stream<Path> list = null;
try {
list = Files.list(originalDataDir);
} catch (IOException e) {
fail("could not read original directory", e);
}
list.forEach(
original -> {
Path expectedTarget = targetDataDir.resolve(original.getFileName());
assertThat(expectedTarget).exists();
if (Files.isDirectory(original)) {
assertDirectoriesEqual(expectedTarget, original);
} else {
assertThat(expectedTarget).hasSameContentAs(original);
}
}
);
}
}

View File

@@ -0,0 +1,49 @@
package sonia.scm.update.repository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junitpioneer.jupiter.TempDirectory;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.SCMContextProvider;
import java.io.IOException;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(TempDirectory.class)
@ExtendWith(MockitoExtension.class)
class InlineMigrationStrategyTest {
@Mock
SCMContextProvider contextProvider;
@BeforeEach
void mockContextProvider(@TempDirectory.TempDir Path tempDir) {
when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
}
@BeforeEach
void createV1Home(@TempDirectory.TempDir Path tempDir) throws IOException {
V1RepositoryFileSystem.createV1Home(tempDir);
}
@Test
void shouldUseExistingDirectory(@TempDirectory.TempDir Path tempDir) {
Path target = new InlineMigrationStrategy(contextProvider).migrate("b4f-a9f0-49f7-ad1f-37d3aae1c55f", "some/more/directories/than/one", "git");
assertThat(target).isEqualTo(resolveOldDirectory(tempDir));
}
@Test
void shouldMoveDataDirectory(@TempDirectory.TempDir Path tempDir) {
new InlineMigrationStrategy(contextProvider).migrate("b4f-a9f0-49f7-ad1f-37d3aae1c55f", "some/more/directories/than/one", "git");
assertThat(resolveOldDirectory(tempDir).resolve("data")).exists();
}
private Path resolveOldDirectory(Path tempDir) {
return tempDir.resolve("repositories").resolve("git").resolve("some").resolve("more").resolve("directories").resolve("than").resolve("one");
}
}

View File

@@ -1,4 +1,4 @@
package sonia.scm.repository.update;
package sonia.scm.update.repository;
import com.google.common.io.Resources;
import org.assertj.core.api.Assertions;
@@ -42,7 +42,7 @@ class MigrateVerbsToPermissionRolesTest {
@BeforeEach
void init(@TempDirectory.TempDir Path tempDir) throws IOException {
URL metadataUrl = Resources.getResource("sonia/scm/repository/update/metadataWithoutRoles.xml");
URL metadataUrl = Resources.getResource("sonia/scm/update/repository/metadataWithoutRoles.xml");
Files.copy(metadataUrl.openStream(), tempDir.resolve("metadata.xml"));
doAnswer(invocation -> {
((BiConsumer<String, Path>) invocation.getArgument(0)).accept(EXISTING_REPOSITORY_ID, tempDir);

View File

@@ -0,0 +1,77 @@
package sonia.scm.update.repository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junitpioneer.jupiter.TempDirectory;
import org.mockito.Mock;
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;
import java.util.Optional;
import static org.mockito.Mockito.when;
import static sonia.scm.update.repository.MigrationStrategy.INLINE;
@ExtendWith(MockitoExtension.class)
@ExtendWith(TempDirectory.class)
class MigrationStrategyDaoTest {
@Mock
SCMContextProvider contextProvider;
private ConfigurationStoreFactory storeFactory;
@BeforeEach
void initStore(@TempDirectory.TempDir Path tempDir) {
when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
storeFactory = new JAXBConfigurationStoreFactory(contextProvider, null);
}
@Test
void shouldReturnEmptyOptionalWhenStoreIsEmpty() throws JAXBException {
MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory);
Optional<MigrationStrategy> strategy = dao.get("any");
Assertions.assertThat(strategy).isEmpty();
}
@Test
void shouldReturnNewValue() throws JAXBException {
MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory);
dao.set("id", INLINE);
Optional<MigrationStrategy> strategy = dao.get("id");
Assertions.assertThat(strategy).contains(INLINE);
}
@Nested
class WithExistingDatabase {
@BeforeEach
void initExistingDatabase() throws JAXBException {
MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory);
dao.set("id", INLINE);
}
@Test
void shouldFindExistingValue() throws JAXBException {
MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory);
Optional<MigrationStrategy> strategy = dao.get("id");
Assertions.assertThat(strategy).contains(INLINE);
}
}
}

View File

@@ -0,0 +1,25 @@
package sonia.scm.update.repository;
import com.google.inject.Injector;
import sonia.scm.update.repository.MigrationStrategy.Instance;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class MigrationStrategyMock {
static Injector init() {
Map<Class, Instance> mocks = new HashMap<>();
Injector mock = mock(Injector.class);
when(
mock.getInstance(any(Class.class)))
.thenAnswer(
invocationOnMock -> mocks.computeIfAbsent(invocationOnMock.getArgument(0), key -> mock((Class<Instance>) key))
);
return mock;
}
}

View File

@@ -0,0 +1,62 @@
package sonia.scm.update.repository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junitpioneer.jupiter.TempDirectory;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.SCMContextProvider;
import sonia.scm.repository.RepositoryLocationResolver;
import java.io.IOException;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(TempDirectory.class)
@ExtendWith(MockitoExtension.class)
class MoveMigrationStrategyTest {
@Mock
SCMContextProvider contextProvider;
@Mock
RepositoryLocationResolver locationResolver;
@BeforeEach
void mockContextProvider(@TempDirectory.TempDir Path tempDir) {
when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
}
@BeforeEach
void createV1Home(@TempDirectory.TempDir Path tempDir) throws IOException {
V1RepositoryFileSystem.createV1Home(tempDir);
}
@BeforeEach
void mockLocationResolver(@TempDirectory.TempDir Path tempDir) {
RepositoryLocationResolver.RepositoryLocationResolverInstance instanceMock = mock(RepositoryLocationResolver.RepositoryLocationResolverInstance.class);
when(locationResolver.forClass(Path.class)).thenReturn(instanceMock);
when(instanceMock.getLocation(anyString())).thenAnswer(invocation -> tempDir.resolve((String) invocation.getArgument(0)));
}
@Test
void shouldUseStandardDirectory(@TempDirectory.TempDir Path tempDir) {
Path target = new MoveMigrationStrategy(contextProvider, locationResolver).migrate("b4f-a9f0-49f7-ad1f-37d3aae1c55f", "some/more/directories/than/one", "git");
assertThat(target).isEqualTo(tempDir.resolve("b4f-a9f0-49f7-ad1f-37d3aae1c55f"));
}
@Test
void shouldMoveDataDirectory(@TempDirectory.TempDir Path tempDir) {
Path target = new MoveMigrationStrategy(contextProvider, locationResolver).migrate("b4f-a9f0-49f7-ad1f-37d3aae1c55f", "some/more/directories/than/one", "git");
assertThat(target.resolve("data")).exists();
Path originalDataDir = tempDir
.resolve("repositories")
.resolve("git")
.resolve("some");
assertThat(originalDataDir).doesNotExist();
}
}

View File

@@ -0,0 +1,67 @@
package sonia.scm.update.repository;
import sonia.scm.repository.spi.ZippedRepositoryTestBase;
import java.io.IOException;
import java.nio.file.Path;
class V1RepositoryFileSystem {
/**
* Creates the following v1 repositories in the temp dir:
* <pre>
* <repository>
* <properties/>
* <contact>arthur@dent.uk</contact>
* <creationDate>1558423492071</creationDate>
* <description>A repository with two folders.</description>
* <id>3b91caa5-59c3-448f-920b-769aaa56b761</id>
* <name>one/directory</name>
* <public>false</public>
* <archived>false</archived>
* <type>git</type>
* </repository>
* <repository>
* <properties/>
* <contact>arthur@dent.uk</contact>
* <creationDate>1558423543716</creationDate>
* <description>A repository in deeply nested folders.</description>
* <id>c1597b4f-a9f0-49f7-ad1f-37d3aae1c55f</id>
* <name>some/more/directories/than/one</name>
* <public>false</public>
* <archived>false</archived>
* <type>git</type>
* </repository>
* <repository>
* <properties/>
* <contact>arthur@dent.uk</contact>
* <creationDate>1558423440258</creationDate>
* <description>A simple repository without directories.</description>
* <id>454972da-faf9-4437-b682-dc4a4e0aa8eb</id>
* <lastModified>1558425918578</lastModified>
* <name>simple</name>
* <permissions>
* <groupPermission>true</groupPermission>
* <name>mice</name>
* <type>WRITE</type>
* </permissions>
* <permissions>
* <groupPermission>false</groupPermission>
* <name>dent</name>
* <type>OWNER</type>
* </permissions>
* <permissions>
* <groupPermission>false</groupPermission>
* <name>trillian</name>
* <type>READ</type>
* </permissions>
* <public>false</public>
* <archived>false</archived>
* <type>git</type>
* <url>http://localhost:8081/scm/git/simple</url>
* </repository>
* </pre>
*/
static void createV1Home(Path tempDir) throws IOException {
ZippedRepositoryTestBase.extract(tempDir.toFile(), "sonia/scm/update/repository/scm-home.v1.zip");
}
}

View File

@@ -0,0 +1,47 @@
package sonia.scm.update.repository;
import com.google.common.io.Resources;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junitpioneer.jupiter.TempDirectory;
import sonia.scm.SCMContextProvider;
import sonia.scm.repository.xml.PathBasedRepositoryLocationResolver;
import sonia.scm.repository.xml.XmlRepositoryDAO;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(TempDirectory.class)
class XmlRepositoryFileNameUpdateStepTest {
SCMContextProvider contextProvider = mock(SCMContextProvider.class);
XmlRepositoryDAO repositoryDAO = mock(XmlRepositoryDAO.class);
@BeforeEach
void mockScmHome(@TempDirectory.TempDir Path tempDir) {
when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
}
@Test
void shouldCopyRepositoriesFileToRepositoryPathsFile(@TempDirectory.TempDir Path tempDir) throws IOException {
XmlRepositoryFileNameUpdateStep updateStep = new XmlRepositoryFileNameUpdateStep(contextProvider, repositoryDAO);
URL url = Resources.getResource("sonia/scm/update/repository/formerV2RepositoryFile.xml");
Path configDir = tempDir.resolve("config");
Files.createDirectories(configDir);
Files.copy(url.openStream(), configDir.resolve("repositories.xml"));
updateStep.doUpdate();
assertThat(configDir.resolve(PathBasedRepositoryLocationResolver.STORE_NAME + ".xml")).exists();
assertThat(configDir.resolve("repositories.xml")).doesNotExist();
verify(repositoryDAO).refresh();
}
}

View File

@@ -0,0 +1,234 @@
package sonia.scm.update.repository;
import com.google.common.io.Resources;
import com.google.inject.Injector;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junitpioneer.jupiter.TempDirectory;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryPermission;
import sonia.scm.repository.xml.XmlRepositoryDAO;
import sonia.scm.store.ConfigurationEntryStore;
import sonia.scm.store.ConfigurationEntryStoreFactory;
import sonia.scm.store.InMemoryConfigurationEntryStore;
import sonia.scm.store.InMemoryConfigurationEntryStoreFactory;
import sonia.scm.update.UpdateStepTestUtil;
import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
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.INLINE;
import static sonia.scm.update.repository.MigrationStrategy.MOVE;
@ExtendWith(MockitoExtension.class)
@ExtendWith(TempDirectory.class)
class XmlRepositoryV1UpdateStepTest {
Injector injectorMock = MigrationStrategyMock.init();
@Mock
XmlRepositoryDAO repositoryDAO;
@Mock
MigrationStrategyDao migrationStrategyDao;
ConfigurationEntryStoreFactory configurationEntryStoreFactory = new InMemoryConfigurationEntryStoreFactory(new InMemoryConfigurationEntryStore());
@Captor
ArgumentCaptor<Repository> storeCaptor;
@Captor
ArgumentCaptor<Path> locationCaptor;
UpdateStepTestUtil testUtil;
XmlRepositoryV1UpdateStep updateStep;
@BeforeEach
void createUpdateStepFromMocks(@TempDirectory.TempDir Path tempDir) {
testUtil = new UpdateStepTestUtil(tempDir);
updateStep = new XmlRepositoryV1UpdateStep(
testUtil.getContextProvider(),
repositoryDAO,
migrationStrategyDao,
injectorMock,
configurationEntryStoreFactory
);
}
@Nested
class WithExistingDatabase {
@BeforeEach
void createV1Home(@TempDirectory.TempDir Path tempDir) throws IOException {
V1RepositoryFileSystem.createV1Home(tempDir);
}
@BeforeEach
void captureStoredRepositories() {
lenient().doNothing().when(repositoryDAO).add(storeCaptor.capture(), locationCaptor.capture());
}
@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));
}
@Test
void shouldCreateNewRepositories() throws JAXBException {
updateStep.doUpdate();
verify(repositoryDAO, times(3)).add(any(), any());
}
@Test
void shouldMapAttributes() throws JAXBException {
updateStep.doUpdate();
Optional<Repository> repository = findByNamespace("git");
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> 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
void shouldMapPermissions() throws JAXBException {
updateStep.doUpdate();
Optional<Repository> repository = findByNamespace("git");
assertThat(repository.get().getPermissions())
.hasSize(3)
.contains(
new RepositoryPermission("mice", "WRITE", true),
new RepositoryPermission("dent", "OWNER", false),
new RepositoryPermission("trillian", "READ", false)
);
}
@Test
void shouldExtractPropertiesFromRepositories() throws JAXBException {
updateStep.doUpdate();
ConfigurationEntryStore<Object> store = configurationEntryStoreFactory.withType(null).withName("").build();
assertThat(store.getAll())
.hasSize(3);
}
@Test
void shouldUseDirectoryFromStrategy(@TempDirectory.TempDir Path tempDir) throws JAXBException {
Path targetDir = tempDir.resolve("someDir");
MigrationStrategy.Instance strategyMock = injectorMock.getInstance(InlineMigrationStrategy.class);
when(strategyMock.migrate("454972da-faf9-4437-b682-dc4a4e0aa8eb", "simple", "git")).thenReturn(targetDir);
updateStep.doUpdate();
assertThat(locationCaptor.getAllValues()).contains(targetDir);
}
@Test
void shouldFailForMissingMigrationStrategy() {
lenient().when(migrationStrategyDao.get("c1597b4f-a9f0-49f7-ad1f-37d3aae1c55f")).thenReturn(empty());
assertThrows(IllegalStateException.class, () -> updateStep.doUpdate());
}
@Test
void shouldBackupOldRepositoryDatabaseFile(@TempDirectory.TempDir Path tempDir) throws JAXBException {
updateStep.doUpdate();
assertThat(tempDir.resolve("config").resolve("repositories.xml")).doesNotExist();
assertThat(tempDir.resolve("config").resolve("repositories.xml.v1.backup")).exists();
}
}
@Test
void shouldNotFailIfNoOldDatabaseExists() throws JAXBException {
updateStep.doUpdate();
}
@Test
void shouldNotFailIfFormerV2DatabaseExists(@TempDirectory.TempDir Path tempDir) throws JAXBException, IOException {
createFormerV2RepositoriesFile(tempDir);
updateStep.doUpdate();
}
@Test
void shouldNotBackupFormerV2DatabaseFile(@TempDirectory.TempDir Path tempDir) throws JAXBException, IOException {
createFormerV2RepositoriesFile(tempDir);
updateStep.doUpdate();
assertThat(tempDir.resolve("config").resolve("repositories.xml")).exists();
assertThat(tempDir.resolve("config").resolve("repositories.xml.v1.backup")).doesNotExist();
}
private void createFormerV2RepositoriesFile(@TempDirectory.TempDir Path tempDir) throws IOException {
URL url = Resources.getResource("sonia/scm/update/repository/formerV2RepositoryFile.xml");
Path configDir = tempDir.resolve("config");
Files.createDirectories(configDir);
Files.copy(url.openStream(), configDir.resolve("repositories.xml"));
}
private Optional<Repository> findByNamespace(String namespace) {
return storeCaptor.getAllValues().stream().filter(r -> r.getNamespace().equals(namespace)).findFirst();
}
}

View File

@@ -0,0 +1,94 @@
package sonia.scm.update.security;
import com.google.common.io.Resources;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junitpioneer.jupiter.TempDirectory;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.SCMContextProvider;
import sonia.scm.security.AssignedPermission;
import sonia.scm.store.ConfigurationEntryStore;
import sonia.scm.store.ConfigurationEntryStoreFactory;
import sonia.scm.store.InMemoryConfigurationEntryStore;
import sonia.scm.store.InMemoryConfigurationEntryStoreFactory;
import sonia.scm.update.security.XmlSecurityV1UpdateStep;
import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@ExtendWith(TempDirectory.class)
class XmlSecurityV1UpdateStepTest {
@Mock
SCMContextProvider contextProvider;
XmlSecurityV1UpdateStep updateStep;
ConfigurationEntryStore<AssignedPermission> assignedPermissionStore;
@BeforeEach
void mockScmHome(@TempDirectory.TempDir Path tempDir) {
when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
assignedPermissionStore = new InMemoryConfigurationEntryStore<>();
ConfigurationEntryStoreFactory inMemoryConfigurationEntryStoreFactory = new InMemoryConfigurationEntryStoreFactory(assignedPermissionStore);
updateStep = new XmlSecurityV1UpdateStep(contextProvider, inMemoryConfigurationEntryStoreFactory);
}
@Nested
class WithExistingDatabase {
@BeforeEach
void createConfigV1XML(@TempDirectory.TempDir Path tempDir) throws IOException {
Path configDir = tempDir.resolve("config");
Files.createDirectories(configDir);
copyTestDatabaseFile(configDir, "config.xml");
}
@Test
void shouldCreatePermissionForUsersConfiguredAsAdmin() throws JAXBException {
updateStep.doUpdate();
List<String> assignedPermission =
assignedPermissionStore.getAll().values()
.stream()
.filter(a -> a.getPermission().getValue().equals("*"))
.filter(a -> !a.isGroupPermission())
.map(AssignedPermission::getName)
.collect(toList());
assertThat(assignedPermission).contains("arthur", "dent", "ldap-admin");
}
@Test
void shouldCreatePermissionForGroupsConfiguredAsAdmin() throws JAXBException {
updateStep.doUpdate();
List<String> assignedPermission =
assignedPermissionStore.getAll().values()
.stream()
.filter(a -> a.getPermission().getValue().equals("*"))
.filter(AssignedPermission::isGroupPermission)
.map(AssignedPermission::getName)
.collect(toList());
assertThat(assignedPermission).contains("admins", "vogons");
}
}
private void copyTestDatabaseFile(Path configDir, String fileName) throws IOException {
URL url = Resources.getResource("sonia/scm/update/security/" + fileName);
Files.copy(url.openStream(), configDir.resolve(fileName));
}
@Test
void shouldNotFailForMissingConfigDir() throws JAXBException {
updateStep.doUpdate();
}
}

View File

@@ -0,0 +1,125 @@
package sonia.scm.update.user;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junitpioneer.jupiter.TempDirectory;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.security.AssignedPermission;
import sonia.scm.update.UpdateStepTestUtil;
import sonia.scm.user.User;
import sonia.scm.user.xml.XmlUserDAO;
import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.linesOf;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
@ExtendWith(TempDirectory.class)
class XmlUserV1UpdateStepTest {
@Mock
XmlUserDAO userDAO;
@Captor
ArgumentCaptor<User> userCaptor;
XmlUserV1UpdateStep updateStep;
private UpdateStepTestUtil testUtil;
@BeforeEach
void mockScmHome(@TempDirectory.TempDir Path tempDir) {
testUtil = new UpdateStepTestUtil(tempDir);
updateStep = new XmlUserV1UpdateStep(testUtil.getContextProvider(), userDAO, testUtil.getStoreFactory());
}
@Nested
class WithExistingDatabase {
@BeforeEach
void captureStoredRepositories() {
doNothing().when(userDAO).add(userCaptor.capture());
}
@BeforeEach
void createUserV1XML() throws IOException {
testUtil.copyConfigFile("sonia/scm/update/user/users.xml");
}
@Test
void shouldCreateNewPermissionsForV1AdminUser() throws JAXBException {
updateStep.doUpdate();
Optional<AssignedPermission> assignedPermission =
testUtil.getStoreForConfigFile("security")
.getAll()
.values()
.stream()
.filter(a -> a.getName().equals("scmadmin"))
.findFirst();
assertThat(assignedPermission.get().getPermission().getValue()).contains("*");
assertThat(assignedPermission.get().isGroupPermission()).isFalse();
}
@Test
void shouldCreateNewUserFromUsersV1Xml() throws JAXBException {
updateStep.doUpdate();
verify(userDAO, times(5)).add(any());
}
@Test
void shouldMapAttributesFromUsersV1Xml() throws JAXBException {
updateStep.doUpdate();
Optional<User> user = userCaptor.getAllValues().stream().filter(u -> u.getName().equals("scmadmin")).findFirst();
assertThat(user)
.get()
.hasFieldOrPropertyWithValue("name", "scmadmin")
.hasFieldOrPropertyWithValue("mail", "scm-admin@scm-manager.com")
.hasFieldOrPropertyWithValue("displayName", "SCM Administrator")
.hasFieldOrPropertyWithValue("active", false)
.hasFieldOrPropertyWithValue("password", "ff8f5c593a01f9fcd3ed48b09a4b013e8d8f3be7")
.hasFieldOrPropertyWithValue("type", "xml")
.hasFieldOrPropertyWithValue("lastModified", 1558597367492L)
.hasFieldOrPropertyWithValue("creationDate", 1558597074732L);
}
@Test
void shouldExtractProperties() throws JAXBException {
updateStep.doUpdate();
Path propertiesFile = testUtil.getFile("user-properties-v1.xml");
assertThat(propertiesFile)
.exists();
assertThat(linesOf(propertiesFile.toFile()))
.extracting(String::trim)
.containsSequence(
"<key>dent</key>",
"<value>",
"<item>",
"<key>born.on</key>",
"<value>earth</value>",
"</item>",
"<item>",
"<key>last.seen</key>",
"<value>end of the universe</value>",
"</item>",
"</value>");
}
}
@Test
void shouldNotFailForMissingConfigDir() throws JAXBException {
updateStep.doUpdate();
}
}

View File

@@ -1,66 +0,0 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.upgrade;
//~--- non-JDK imports --------------------------------------------------------
import org.junit.Test;
import static org.junit.Assert.*;
import static sonia.scm.upgrade.ClientDateFormatConverter.extjsToMoments;
/**
*
* @author Sebastian Sdorra
*/
public class ClientDateFormatConverterTest
{
/**
* Method description
*
*/
@Test
public void testExtjsToMoments()
{
assertEquals("YYYY-MM-DD", extjsToMoments("Y-m-d"));
assertEquals("HH:mm:ss", extjsToMoments("H:i:s"));
assertEquals("YYYY-MM-DD HH:mm:ss", extjsToMoments("Y-m-d H:i:s"));
assertEquals("YYYY/MM/DD", extjsToMoments("Y/m/d"));
assertEquals("MMMM D, YYYY, h:mm a", extjsToMoments("F j, Y, g:i a"));
// with time ago pattern
assertEquals("YYYY-MM-DD {0}", extjsToMoments("Y-m-d {0}"));
}
}