This commit is contained in:
Mohamed Karray
2018-11-15 11:28:33 +01:00
381 changed files with 12331 additions and 11177 deletions

View File

@@ -17,6 +17,7 @@ import java.io.StringWriter;
import java.io.Writer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,6 +46,23 @@ public class TemplatingPushStateDispatcherTest {
@Test
public void testDispatch() throws IOException {
TemplatingPushStateDispatcher.IndexHtmlModel model = dispatch();
assertEquals("/scm", model.getContextPath());
assertNull(model.getLiveReloadURL());
}
@Test
public void testDispatchWithLiveReloadURL() throws IOException {
System.setProperty("livereload.url", "/livereload.js");
try {
TemplatingPushStateDispatcher.IndexHtmlModel model = dispatch();
assertEquals("/livereload.js", model.getLiveReloadURL());
} finally {
System.clearProperty("livereload.url");
}
}
private TemplatingPushStateDispatcher.IndexHtmlModel dispatch() throws IOException {
when(request.getContextPath()).thenReturn("/scm");
when(templateEngine.getTemplate(TemplatingPushStateDispatcher.TEMPLATE)).thenReturn(template);
@@ -59,8 +77,7 @@ public class TemplatingPushStateDispatcherTest {
verify(template).execute(any(Writer.class), captor.capture());
TemplatingPushStateDispatcher.IndexHtmlModel model = (TemplatingPushStateDispatcher.IndexHtmlModel) captor.getValue();
assertEquals("/scm", model.getContextPath());
return (TemplatingPushStateDispatcher.IndexHtmlModel) captor.getValue();
}
}

View File

@@ -9,7 +9,6 @@ import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.util.ThreadState;
import org.assertj.core.util.Lists;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.After;
@@ -19,7 +18,6 @@ import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.ChangesetPagingResult;
import sonia.scm.repository.NamespaceAndName;
@@ -38,7 +36,6 @@ import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -50,7 +47,8 @@ public class ChangesetRootResourceTest extends RepositoryTestBase {
public static final String CHANGESET_PATH = "space/repo/changesets/";
public static final String CHANGESET_URL = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + CHANGESET_PATH;
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
private Dispatcher dispatcher;
private final URI baseUri = URI.create("/");
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
@@ -77,16 +75,14 @@ public class ChangesetRootResourceTest extends RepositoryTestBase {
@Before
public void prepareEnvironment() throws Exception {
public void prepareEnvironment() {
changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks);
changesetRootResource = new ChangesetRootResource(serviceFactory, changesetCollectionToDtoMapper, changesetToChangesetDtoMapper);
super.changesetRootResource = Providers.of(changesetRootResource);
dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource());
dispatcher = DispatcherMock.createDispatcher(getRepositoryRootResource());
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService);
when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);
when(repositoryService.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo"));
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
when(repositoryService.getLogCommand()).thenReturn(logCommandBuilder);
subjectThreadState.bind();
ThreadContext.bind(subject);
@@ -169,7 +165,7 @@ public class ChangesetRootResourceTest extends RepositoryTestBase {
when(logCommandBuilder.setStartChangeset(anyString())).thenReturn(logCommandBuilder);
when(logCommandBuilder.getChangesets()).thenReturn(changesetPagingResult);
MockHttpRequest request = MockHttpRequest
.get(CHANGESET_URL + "id")
.get(CHANGESET_URL + id)
.accept(VndMediaType.CHANGESET);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);

View File

@@ -8,9 +8,8 @@ import org.mockito.Answers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.NotFoundException;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.PathNotFoundException;
import sonia.scm.repository.RepositoryNotFoundException;
import sonia.scm.repository.api.CatCommandBuilder;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
@@ -58,8 +57,8 @@ public class ContentResourceTest {
when(catCommand.setRevision(REV)).thenReturn(catCommand);
// defaults for unknown things
doThrow(new RepositoryNotFoundException("x")).when(repositoryServiceFactory).create(not(eq(existingNamespaceAndName)));
doThrow(new PathNotFoundException("x")).when(catCommand).getStream(any());
doThrow(new NotFoundException("Test", "r")).when(repositoryServiceFactory).create(not(eq(existingNamespaceAndName)));
doThrow(new NotFoundException("Test", "X")).when(catCommand).getStream(any());
}
@Test
@@ -93,7 +92,7 @@ public class ContentResourceTest {
Response response = contentResource.get(NAMESPACE, REPO_NAME, REV, "SomeGoCode.go");
assertEquals(200, response.getStatus());
assertEquals("GO", response.getHeaderString("Language"));
assertEquals("GO", response.getHeaderString("X-Programming-Language"));
assertEquals("text/x-go", response.getHeaderString("Content-Type"));
}
@@ -104,7 +103,7 @@ public class ContentResourceTest {
Response response = contentResource.get(NAMESPACE, REPO_NAME, REV, "Dockerfile");
assertEquals(200, response.getStatus());
assertEquals("DOCKERFILE", response.getHeaderString("Language"));
assertEquals("DOCKERFILE", response.getHeaderString("X-Programming-Language"));
assertEquals("text/plain", response.getHeaderString("Content-Type"));
}
@@ -175,7 +174,7 @@ public class ContentResourceTest {
}
@Override
public void close() throws IOException {
public void close() {
closed = true;
}

View File

@@ -17,20 +17,23 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.NotFoundException;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.api.v2.NotFoundExceptionMapper;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryNotFoundException;
import sonia.scm.repository.RevisionNotFoundException;
import sonia.scm.repository.api.DiffCommandBuilder;
import sonia.scm.repository.api.DiffFormat;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.web.VndMediaType;
import java.net.URISyntaxException;
import java.util.Arrays;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
@@ -62,14 +65,15 @@ public class DiffResourceTest extends RepositoryTestBase {
@Before
public void prepareEnvironment() throws Exception {
public void prepareEnvironment() {
diffRootResource = new DiffRootResource(serviceFactory);
super.diffRootResource = Providers.of(diffRootResource);
dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource());
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
when(service.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo"));
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
ExceptionWithContextToErrorDtoMapperImpl mapper = new ExceptionWithContextToErrorDtoMapperImpl();
dispatcher.getProviderFactory().register(new NotFoundExceptionMapper(mapper));
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(CRLFInjectionExceptionMapper.class);
when(service.getDiffCommand()).thenReturn(diffCommandBuilder);
@@ -86,19 +90,17 @@ public class DiffResourceTest extends RepositoryTestBase {
@Test
public void shouldGetDiffs() throws Exception {
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.retriveContent(any())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.retrieveContent(any())).thenReturn(diffCommandBuilder);
MockHttpRequest request = MockHttpRequest
.get(DIFF_URL + "revision")
.accept(VndMediaType.DIFF);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(200, response.getStatus());
log.info("Response :{}", response.getContentAsString());
assertThat(response.getStatus())
.isEqualTo(200);
assertThat(response.getContentAsString())
.isNotNull();
String expectedHeader = "Content-Disposition";
String expectedValue = "attachment; filename=\"repo-revision.diff\"; filename*=utf-8''repo-revision.diff";
assertThat(response.getOutputHeaders().containsKey(expectedHeader)).isTrue();
@@ -107,8 +109,8 @@ public class DiffResourceTest extends RepositoryTestBase {
}
@Test
public void shouldGet404OnMissingRepository() throws URISyntaxException, RepositoryNotFoundException {
when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(RepositoryNotFoundException.class);
public void shouldGet404OnMissingRepository() throws URISyntaxException {
when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(new NotFoundException("Text", "x"));
MockHttpRequest request = MockHttpRequest
.get(DIFF_URL + "revision")
.accept(VndMediaType.DIFF);
@@ -120,20 +122,24 @@ public class DiffResourceTest extends RepositoryTestBase {
@Test
public void shouldGet404OnMissingRevision() throws Exception {
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.retriveContent(any())).thenThrow(RevisionNotFoundException.class);
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.retrieveContent(any())).thenThrow(new NotFoundException("Text", "x"));
MockHttpRequest request = MockHttpRequest
.get(DIFF_URL + "revision")
.accept(VndMediaType.DIFF);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(404, response.getStatus());
}
@Test
public void shouldGet400OnCrlfInjection() throws Exception {
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.retriveContent(any())).thenThrow(RevisionNotFoundException.class);
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.retrieveContent(any())).thenThrow(new NotFoundException("Text", "x"));
MockHttpRequest request = MockHttpRequest
.get(DIFF_URL + "ny%0D%0ASet-cookie:%20Tamper=3079675143472450634")
@@ -143,6 +149,47 @@ public class DiffResourceTest extends RepositoryTestBase {
assertEquals(400, response.getStatus());
}
@Test
public void shouldGet400OnUnknownFormat() throws Exception {
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.retrieveContent(any())).thenThrow(new NotFoundException("Test", "test"));
MockHttpRequest request = MockHttpRequest
.get(DIFF_URL + "revision?format=Unknown")
.accept(VndMediaType.DIFF);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(400, response.getStatus());
}
@Test
public void shouldAcceptDiffFormats() throws Exception {
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.retrieveContent(any())).thenReturn(diffCommandBuilder);
Arrays.stream(DiffFormat.values()).map(DiffFormat::name).forEach(
this::assertRequestOk
);
}
private void assertRequestOk(String format) {
MockHttpRequest request = null;
try {
request = MockHttpRequest
.get(DIFF_URL + "revision?format=" + format)
.accept(VndMediaType.DIFF);
} catch (URISyntaxException e) {
e.printStackTrace();
fail("got exception: " + e);
}
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertThat(response.getStatus())
.withFailMessage("diff format from DiffFormat enum must be accepted: " + format)
.isEqualTo(200);
}
}

View File

@@ -5,20 +5,22 @@ import org.jboss.resteasy.mock.MockDispatcherFactory;
import sonia.scm.api.rest.AlreadyExistsExceptionMapper;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.api.rest.ConcurrentModificationExceptionMapper;
import sonia.scm.api.rest.IllegalArgumentExceptionMapper;
import sonia.scm.api.v2.NotFoundExceptionMapper;
import sonia.scm.api.v2.NotSupportedFeatureExceptionMapper;
public class DispatcherMock {
public static Dispatcher createDispatcher(Object resource) {
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addSingletonResource(resource);
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(AlreadyExistsExceptionMapper.class);
ExceptionWithContextToErrorDtoMapperImpl mapper = new ExceptionWithContextToErrorDtoMapperImpl();
dispatcher.getProviderFactory().register(new NotFoundExceptionMapper(mapper));
dispatcher.getProviderFactory().register(new AlreadyExistsExceptionMapper(mapper));
dispatcher.getProviderFactory().register(new ConcurrentModificationExceptionMapper(mapper));
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(ConcurrentModificationExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(ChangePasswordNotAllowedExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(InvalidPasswordExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(IllegalArgumentExceptionMapper.class);
dispatcher.getProviderFactory().register(new InternalRepositoryExceptionMapper(mapper));
dispatcher.getProviderFactory().register(new ChangePasswordNotAllowedExceptionMapper(mapper));
dispatcher.getProviderFactory().register(new InvalidPasswordExceptionMapper(mapper));
dispatcher.getProviderFactory().register(new NotSupportedFeatureExceptionMapper(mapper));
return dispatcher;
}
}

View File

@@ -8,7 +8,6 @@ import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.util.ThreadState;
import org.assertj.core.util.Lists;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.After;
@@ -18,15 +17,14 @@ import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.ContextEntry;
import sonia.scm.NotFoundException;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.ChangesetPagingResult;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Person;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryNotFoundException;
import sonia.scm.repository.RevisionNotFoundException;
import sonia.scm.repository.api.LogCommandBuilder;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
@@ -52,7 +50,6 @@ public class FileHistoryResourceTest extends RepositoryTestBase {
public static final String FILE_HISTORY_PATH = "space/repo/history/";
public static final String FILE_HISTORY_URL = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + FILE_HISTORY_PATH;
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
private final URI baseUri = URI.create("/");
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
@@ -73,23 +70,21 @@ public class FileHistoryResourceTest extends RepositoryTestBase {
private FileHistoryRootResource fileHistoryRootResource;
private Dispatcher dispatcher;
private final Subject subject = mock(Subject.class);
private final ThreadState subjectThreadState = new SubjectThreadState(subject);
@Before
public void prepareEnvironment() throws Exception {
public void prepareEnvironment() {
fileHistoryCollectionToDtoMapper = new FileHistoryCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks);
fileHistoryRootResource = new FileHistoryRootResource(serviceFactory, fileHistoryCollectionToDtoMapper);
super.fileHistoryRootResource = Providers.of(fileHistoryRootResource);
dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource());
dispatcher = DispatcherMock.createDispatcher(getRepositoryRootResource());
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
when(service.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo"));
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class);
ExceptionWithContextToErrorDtoMapperImpl mapper = new ExceptionWithContextToErrorDtoMapperImpl();
when(service.getLogCommand()).thenReturn(logCommandBuilder);
subjectThreadState.bind();
ThreadContext.bind(subject);
@@ -133,8 +128,8 @@ public class FileHistoryResourceTest extends RepositoryTestBase {
@Test
public void shouldGet404OnMissingRepository() throws URISyntaxException, RepositoryNotFoundException {
when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(RepositoryNotFoundException.class);
public void shouldGet404OnMissingRepository() throws URISyntaxException {
when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(new NotFoundException("Text", "x"));
MockHttpRequest request = MockHttpRequest
.get(FILE_HISTORY_URL + "revision/a.txt")
.accept(VndMediaType.CHANGESET_COLLECTION);
@@ -152,7 +147,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase {
when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setStartChangeset(eq(id))).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPath(eq(path))).thenReturn(logCommandBuilder);
when(logCommandBuilder.getChangesets()).thenThrow(RevisionNotFoundException.class);
when(logCommandBuilder.getChangesets()).thenThrow(new NotFoundException("Text", "x"));
MockHttpRequest request = MockHttpRequest
.get(FILE_HISTORY_URL + id + "/" + path)
@@ -171,7 +166,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase {
when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setStartChangeset(eq(id))).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPath(eq(path))).thenReturn(logCommandBuilder);
when(logCommandBuilder.getChangesets()).thenThrow(InternalRepositoryException.class);
when(logCommandBuilder.getChangesets()).thenThrow(new InternalRepositoryException(ContextEntry.ContextBuilder.noContext(), "", new RuntimeException()));
MockHttpRequest request = MockHttpRequest
.get(FILE_HISTORY_URL + id + "/" + path)
@@ -182,7 +177,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase {
}
@Test
public void shouldGet500OnNullChangesets() throws Exception {
public void shouldGet404OnNullChangesets() throws Exception {
String id = "revision_123";
String path = "root_dir/sub_dir/file-to-inspect.txt";
@@ -197,6 +192,6 @@ public class FileHistoryResourceTest extends RepositoryTestBase {
.accept(VndMediaType.CHANGESET_COLLECTION);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(500, response.getStatus());
assertEquals(404, response.getStatus());
}
}

View File

@@ -62,7 +62,7 @@ public class GroupRootResourceTest {
private ArgumentCaptor<Group> groupCaptor = ArgumentCaptor.forClass(Group.class);
@Before
public void prepareEnvironment() throws Exception {
public void prepareEnvironment() {
initMocks(this);
when(groupManager.create(groupCaptor.capture())).thenAnswer(invocation -> invocation.getArguments()[0]);
doNothing().when(groupManager).modify(groupCaptor.capture());

View File

@@ -13,6 +13,7 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import sonia.scm.ContextEntry;
import sonia.scm.user.InvalidPasswordException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
@@ -168,7 +169,8 @@ public class MeResourceTest {
.content(content.getBytes());
MockHttpResponse response = new MockHttpResponse();
doThrow(InvalidPasswordException.class).when(userManager).changePasswordForLoggedInUser(any(), any());
doThrow(new InvalidPasswordException(ContextEntry.ContextBuilder.entity("passwortChange", "-")))
.when(userManager).changePasswordForLoggedInUser(any(), any());
dispatcher.invoke(request, response);

View File

@@ -8,7 +8,6 @@ import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.util.ThreadState;
import org.assertj.core.util.Lists;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.After;
@@ -18,7 +17,6 @@ import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.Modifications;
import sonia.scm.repository.NamespaceAndName;
@@ -37,6 +35,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static sonia.scm.ContextEntry.ContextBuilder.noContext;
@Slf4j
@RunWith(MockitoJUnitRunner.Silent.class)
@@ -45,7 +44,8 @@ public class ModificationsResourceTest extends RepositoryTestBase {
public static final String MODIFICATIONS_PATH = "space/repo/modifications/";
public static final String MODIFICATIONS_URL = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + MODIFICATIONS_PATH;
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
private Dispatcher dispatcher;
private final URI baseUri = URI.create("/");
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
@@ -70,16 +70,13 @@ public class ModificationsResourceTest extends RepositoryTestBase {
@Before
public void prepareEnvironment() throws Exception {
public void prepareEnvironment() {
modificationsRootResource = new ModificationsRootResource(serviceFactory, modificationsToDtoMapper);
super.modificationsRootResource = Providers.of(modificationsRootResource);
dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource());
dispatcher = DispatcherMock.createDispatcher(getRepositoryRootResource());
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService);
when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);
when(repositoryService.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo"));
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class);
when(repositoryService.getModificationsCommand()).thenReturn(modificationsCommandBuilder);
subjectThreadState.bind();
ThreadContext.bind(subject);
@@ -107,7 +104,7 @@ public class ModificationsResourceTest extends RepositoryTestBase {
@Test
public void shouldGet500OnModificationsCommandError() throws Exception {
when(modificationsCommandBuilder.revision(any())).thenReturn(modificationsCommandBuilder);
when(modificationsCommandBuilder.getModifications()).thenThrow(InternalRepositoryException.class);
when(modificationsCommandBuilder.getModifications()).thenThrow(new InternalRepositoryException(noContext(), "", new RuntimeException()));
MockHttpRequest request = MockHttpRequest
.get(MODIFICATIONS_URL + "revision")

View File

@@ -164,10 +164,7 @@ public class PermissionRootResourceTest extends RepositoryTestBase {
@TestFactory
@DisplayName("test endpoints on missing permissions and user is Admin")
Stream<DynamicTest> missedPermissionTestFactory() {
Repository mockRepository = mock(Repository.class);
when(mockRepository.getId()).thenReturn(REPOSITORY_NAME);
when(mockRepository.getNamespace()).thenReturn(REPOSITORY_NAMESPACE);
when(mockRepository.getName()).thenReturn(REPOSITORY_NAME);
Repository mockRepository = new Repository(REPOSITORY_NAME, "git", REPOSITORY_NAMESPACE, REPOSITORY_NAME);
when(repositoryManager.get(any(NamespaceAndName.class))).thenReturn(mockRepository);
return createDynamicTestsToAssertResponses(
requestGETPermission.expectedResponseStatus(404),
@@ -180,10 +177,6 @@ public class PermissionRootResourceTest extends RepositoryTestBase {
@TestFactory
@DisplayName("test endpoints on missing permissions and user is not Admin")
Stream<DynamicTest> missedPermissionUserForbiddenTestFactory() {
Repository mockRepository = mock(Repository.class);
when(mockRepository.getId()).thenReturn(REPOSITORY_NAME);
when(mockRepository.getNamespace()).thenReturn(REPOSITORY_NAMESPACE);
when(mockRepository.getName()).thenReturn(REPOSITORY_NAME);
doThrow(AuthorizationException.class).when(repositoryManager).get(any(NamespaceAndName.class));
return createDynamicTestsToAssertResponses(
requestGETPermission.expectedResponseStatus(403),
@@ -409,17 +402,17 @@ public class PermissionRootResourceTest extends RepositoryTestBase {
}
private Repository createUserWithRepository(String userPermission) {
Repository mockRepository = mock(Repository.class);
when(mockRepository.getId()).thenReturn(REPOSITORY_NAME);
when(mockRepository.getNamespace()).thenReturn(REPOSITORY_NAMESPACE);
when(mockRepository.getName()).thenReturn(REPOSITORY_NAME);
Repository mockRepository = new Repository();
mockRepository.setId(REPOSITORY_NAME);
mockRepository.setNamespace(REPOSITORY_NAMESPACE);
mockRepository.setName(REPOSITORY_NAME);
when(repositoryManager.get(any(NamespaceAndName.class))).thenReturn(mockRepository);
when(subject.isPermitted(userPermission != null ? eq(userPermission) : any(String.class))).thenReturn(true);
return mockRepository;
}
private void createUserWithRepositoryAndPermissions(ArrayList<Permission> permissions, String userPermission) {
when(createUserWithRepository(userPermission).getPermissions()).thenReturn(permissions);
createUserWithRepository(userPermission).setPermissions(permissions);
}
private Stream<DynamicTest> createDynamicTestsToAssertResponses(ExpectedRequest... expectedRequests) {
@@ -427,10 +420,9 @@ public class PermissionRootResourceTest extends RepositoryTestBase {
.map(entry -> dynamicTest("the endpoint " + entry.description + " should return the status code " + entry.expectedResponseStatus, () -> assertExpectedRequest(entry)));
}
private MockHttpResponse assertExpectedRequest(ExpectedRequest entry) throws URISyntaxException {
private void assertExpectedRequest(ExpectedRequest entry) throws URISyntaxException {
MockHttpResponse response = new MockHttpResponse();
HttpRequest request = null;
request = MockHttpRequest
HttpRequest request = MockHttpRequest
.create(entry.method, "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + entry.path)
.content(entry.content)
.contentType(VndMediaType.PERMISSION);
@@ -442,7 +434,6 @@ public class PermissionRootResourceTest extends RepositoryTestBase {
if (entry.responseValidator != null) {
entry.responseValidator.accept(response);
}
return response;
}
@ToString
@@ -476,12 +467,12 @@ public class PermissionRootResourceTest extends RepositoryTestBase {
return this;
}
public ExpectedRequest expectedResponseStatus(int expectedResponseStatus) {
ExpectedRequest expectedResponseStatus(int expectedResponseStatus) {
this.expectedResponseStatus = expectedResponseStatus;
return this;
}
public ExpectedRequest responseValidator(Consumer<MockHttpResponse> responseValidator) {
ExpectedRequest responseValidator(Consumer<MockHttpResponse> responseValidator) {
this.responseValidator = responseValidator;
return this;
}

View File

@@ -4,6 +4,9 @@ import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.io.Resources;
import com.google.inject.util.Providers;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.assertj.core.api.Assertions;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
@@ -22,6 +25,7 @@ import sonia.scm.repository.RepositoryIsNotArchivedException;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.user.User;
import sonia.scm.web.VndMediaType;
import javax.servlet.http.HttpServletResponse;
@@ -37,6 +41,7 @@ import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static javax.servlet.http.HttpServletResponse.SC_PRECONDITION_FAILED;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -59,6 +64,8 @@ import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
)
public class RepositoryRootResourceTest extends RepositoryTestBase {
private static final String REALM = "AdminRealm";
private Dispatcher dispatcher;
@Rule
@@ -96,6 +103,13 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
when(scmPathInfoStore.get()).thenReturn(uriInfo);
when(uriInfo.getApiRestUri()).thenReturn(URI.create("/x/y"));
SimplePrincipalCollection trillian = new SimplePrincipalCollection("trillian", REALM);
trillian.add(new User("trillian"), REALM);
shiro.setSubject(
new Subject.Builder()
.principals(trillian)
.authenticated(true)
.buildSubject());
}
@Test
@@ -257,6 +271,34 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
verify(repositoryManager).create(any(Repository.class));
}
@Test
public void shouldSetCurrentUserAsOwner() throws Exception {
ArgumentCaptor<Repository> createCaptor = ArgumentCaptor.forClass(Repository.class);
when(repositoryManager.create(createCaptor.capture())).thenAnswer(invocation -> {
Repository repository = (Repository) invocation.getArguments()[0];
repository.setNamespace("otherspace");
return repository;
});
URL url = Resources.getResource("sonia/scm/api/v2/repository-test-update.json");
byte[] repositoryJson = Resources.toByteArray(url);
MockHttpRequest request = MockHttpRequest
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2)
.contentType(VndMediaType.REPOSITORY)
.content(repositoryJson);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
Assertions.assertThat(createCaptor.getValue().getPermissions())
.hasSize(1)
.allSatisfy(p -> {
assertThat(p.getName()).isEqualTo("trillian");
assertThat(p.getType()).isEqualTo(PermissionType.OWNER);
});
}
@Test
public void shouldNotOverwriteExistingPermissionsOnUpdate() throws Exception {
Repository existingRepository = mockRepository("space", "repo");

View File

@@ -14,7 +14,6 @@ import sonia.scm.NotFoundException;
import sonia.scm.repository.BrowserResult;
import sonia.scm.repository.FileObject;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.RepositoryNotFoundException;
import sonia.scm.repository.api.BrowseCommandBuilder;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
@@ -60,7 +59,7 @@ public class SourceRootResourceTest extends RepositoryTestBase {
}
@Test
public void shouldReturnSources() throws URISyntaxException, IOException, NotFoundException {
public void shouldReturnSources() throws URISyntaxException, IOException {
BrowserResult result = createBrowserResult();
when(browseCommandBuilder.getBrowserResult()).thenReturn(result);
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/sources");
@@ -74,8 +73,8 @@ public class SourceRootResourceTest extends RepositoryTestBase {
}
@Test
public void shouldReturn404IfRepoNotFound() throws URISyntaxException, RepositoryNotFoundException {
when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(RepositoryNotFoundException.class);
public void shouldReturn404IfRepoNotFound() throws URISyntaxException {
when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(new NotFoundException("Test", "a"));
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "idont/exist/sources");
MockHttpResponse response = new MockHttpResponse();
@@ -84,7 +83,7 @@ public class SourceRootResourceTest extends RepositoryTestBase {
}
@Test
public void shouldGetResultForSingleFile() throws URISyntaxException, IOException, NotFoundException {
public void shouldGetResultForSingleFile() throws URISyntaxException, IOException {
FileObject fileObject = new FileObject();
fileObject.setName("File Object!");
fileObject.setPath("/");
@@ -100,8 +99,8 @@ public class SourceRootResourceTest extends RepositoryTestBase {
}
@Test
public void shouldGet404ForSingleFileIfRepoNotFound() throws URISyntaxException, RepositoryNotFoundException {
when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(RepositoryNotFoundException.class);
public void shouldGet404ForSingleFileIfRepoNotFound() throws URISyntaxException {
when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(new NotFoundException("Test", "a"));
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "idont/exist/sources/revision/fileabc");
MockHttpResponse response = new MockHttpResponse();

View File

@@ -18,6 +18,7 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.api.v2.NotFoundExceptionMapper;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Repository;
import sonia.scm.repository.Tag;

View File

@@ -14,6 +14,7 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import sonia.scm.ContextEntry;
import sonia.scm.NotFoundException;
import sonia.scm.PageResult;
import sonia.scm.user.ChangePasswordNotAllowedException;
@@ -102,7 +103,7 @@ public class UserRootResourceTest {
@Test
public void shouldGet400OnCreatingNewUserWithNotAllowedCharacters() throws URISyntaxException {
// the @ character at the begin of the name is not allowed
String userJson = "{ \"name\": \"@user\", \"type\": \"db\" }";
String userJson = "{ \"name\": \"@user\",\"active\": true,\"admin\": false,\"displayName\": \"someone\",\"mail\": \"x@example.com\",\"type\": \"db\" }";
MockHttpRequest request = MockHttpRequest
.post("/" + UserRootResource.USERS_PATH_V2)
.contentType(VndMediaType.USER)
@@ -114,7 +115,7 @@ public class UserRootResourceTest {
assertEquals(400, response.getStatus());
// the whitespace at the begin opf the name is not allowed
userJson = "{ \"name\": \" user\", \"type\": \"db\" }";
userJson = "{ \"name\": \" user\",\"active\": true,\"admin\": false,\"displayName\": \"someone\",\"mail\": \"x@example.com\",\"type\": \"db\" }";
request = MockHttpRequest
.post("/" + UserRootResource.USERS_PATH_V2)
.contentType(VndMediaType.USER)
@@ -167,7 +168,7 @@ public class UserRootResourceTest {
.content(content.getBytes());
MockHttpResponse response = new MockHttpResponse();
doThrow(ChangePasswordNotAllowedException.class).when(userManager).overwritePassword(any(), any());
doThrow(new ChangePasswordNotAllowedException(ContextEntry.ContextBuilder.entity("passwordChange", "-"), "xml")).when(userManager).overwritePassword(any(), any());
dispatcher.invoke(request, response);
@@ -185,7 +186,7 @@ public class UserRootResourceTest {
.content(content.getBytes());
MockHttpResponse response = new MockHttpResponse();
doThrow(NotFoundException.class).when(userManager).overwritePassword(any(), any());
doThrow(new NotFoundException("Test", "x")).when(userManager).overwritePassword(any(), any());
dispatcher.invoke(request, response);

View File

@@ -0,0 +1,133 @@
package sonia.scm.boot;
import com.github.legman.Subscribe;
import com.google.common.base.Charsets;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.Stage;
import sonia.scm.event.ScmEventBus;
import sonia.scm.event.ScmTestEventBus;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class RestartServletTest {
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
private RestartServlet restartServlet;
private EventListener listener;
private void setUpObjectUnderTest(Stage stage) {
listener = new EventListener();
ScmEventBus eventBus = ScmTestEventBus.getInstance();
eventBus.register(listener);
restartServlet = new RestartServlet(eventBus, stage);
}
@Test
public void testRestart() throws IOException {
setUpObjectUnderTest(Stage.DEVELOPMENT);
setRequestInputReason("something changed");
restartServlet.doPost(request, response);
verify(response).setStatus(HttpServletResponse.SC_ACCEPTED);
RestartEvent restartEvent = listener.restartEvent;
assertThat(restartEvent).isNotNull();
assertThat(restartEvent.getCause()).isEqualTo(RestartServlet.class);
assertThat(restartEvent.getReason()).isEqualTo("something changed");
}
@Test
public void testRestartCalledTwice() throws IOException {
setUpObjectUnderTest(Stage.DEVELOPMENT);
setRequestInputReason("initial change");
restartServlet.doPost(request, response);
verify(response).setStatus(HttpServletResponse.SC_ACCEPTED);
setRequestInputReason("changed again");
restartServlet.doPost(request, response);
verify(response).setStatus(HttpServletResponse.SC_CONFLICT);
}
@Test
public void testRestartWithInvalidContent() throws IOException {
setUpObjectUnderTest(Stage.DEVELOPMENT);
setRequestInputContent("invalid json");
restartServlet.doPost(request, response);
verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
@Test
public void testRestartInProductionStage() throws IOException {
setUpObjectUnderTest(Stage.PRODUCTION);
setRequestInputReason("initial change");
restartServlet.doPost(request, response);
verify(response).setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
}
private void setRequestInputReason(String message) throws IOException {
String content = createReason(message);
setRequestInputContent(content);
}
private void setRequestInputContent(String content) throws IOException {
InputStream input = createReasonAsInputStream(content);
when(request.getInputStream()).thenReturn(createServletInputStream(input));
}
private ServletInputStream createServletInputStream(final InputStream inputStream) {
return new ServletInputStream() {
@Override
public int read() throws IOException {
return inputStream.read();
}
};
}
private InputStream createReasonAsInputStream(String content) {
return new ByteArrayInputStream(content.getBytes(Charsets.UTF_8));
}
private String createReason(String message) {
return String.format("{\"message\": \"%s\"}", message);
}
public static class EventListener {
private RestartEvent restartEvent;
@Subscribe(async = false)
public void store(RestartEvent restartEvent) {
this.restartEvent = restartEvent;
}
}
}

View File

@@ -0,0 +1,53 @@
package sonia.scm.boot;
import com.google.common.collect.ImmutableSet;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import javax.servlet.ServletContext;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Set;
import java.util.Vector;
import static org.junit.Assert.*;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class ServletContextCleanerTest {
@Mock
private ServletContext servletContext;
@Test
public void testCleanup() {
Set<String> names = ImmutableSet.of(
"org.jboss.resteasy.Dispatcher",
"resteasy.Deployment",
"sonia.scm.Context",
"org.eclipse.jetty.HttpServer",
"javax.servlet.Context",
"org.apache.shiro.SecurityManager"
);
when(servletContext.getAttributeNames()).thenReturn(toEnumeration(names));
ServletContextCleaner.cleanup(servletContext);
verify(servletContext).removeAttribute("org.jboss.resteasy.Dispatcher");
verify(servletContext).removeAttribute("resteasy.Deployment");
verify(servletContext).removeAttribute("sonia.scm.Context");
verify(servletContext, never()).removeAttribute("org.eclipse.jetty.HttpServer");
verify(servletContext, never()).removeAttribute("javax.servlet.Context");
verify(servletContext).removeAttribute("org.apache.shiro.SecurityManager");
}
private <T> Enumeration<T> toEnumeration(Collection<T> collection) {
return new Vector<>(collection).elements();
}
}

View File

@@ -1,85 +0,0 @@
/**
* Copyright (c) 2014, 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.filter;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import org.apache.shiro.SecurityUtils;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.config.ScmConfiguration;
/**
* Unit tests for {@link AdminSecurityFilter}.
*
* @author Sebastian Sdorra
*/
@RunWith(MockitoJUnitRunner.class)
@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini")
public class AdminSecurityFilterTest {
private AdminSecurityFilter securityFilter;
@Rule
public ShiroRule shiro = new ShiroRule();
/**
* Prepare object under test and mocks.
*/
@Before
public void setUp(){
this.securityFilter = new AdminSecurityFilter(new ScmConfiguration());
}
/**
* Tests {@link AdminSecurityFilter#hasPermission(org.apache.shiro.subject.Subject)} as administrator.
*/
@Test
@SubjectAware(username = "dent", password = "secret")
public void testHasPermissionAsAdministrator() {
assertTrue(securityFilter.hasPermission(SecurityUtils.getSubject()));
}
/**
* Tests {@link AdminSecurityFilter#hasPermission(org.apache.shiro.subject.Subject)} as user.
*/
@Test
@SubjectAware(username = "trillian", password = "secret")
public void testHasPermissionAsUser() {
assertFalse(securityFilter.hasPermission(SecurityUtils.getSubject()));
}
}

View File

@@ -98,9 +98,10 @@ public class MDCFilterTest extends AbstractTestBase {
assertNotNull(chain.ctx);
assertEquals("trillian", chain.ctx.get(MDCFilter.MDC_USERNAME));
assertEquals("api/v1/repositories", chain.ctx.get(MDCFilter.MDC_REQUEST_URI));
assertEquals("127.0.0.1", chain.ctx.get(MDCFilter.MDC_CLIEN_IP));
assertEquals("localhost", chain.ctx.get(MDCFilter.MDC_CLIEN_HOST));
assertEquals("127.0.0.1", chain.ctx.get(MDCFilter.MDC_CLIENT_IP));
assertEquals("localhost", chain.ctx.get(MDCFilter.MDC_CLIENT_HOST));
assertEquals("GET", chain.ctx.get(MDCFilter.MDC_REQUEST_METHOD));
assertNotNull(chain.ctx.get(MDCFilter.MDC_TRANSACTION_ID));
}
/**

View File

@@ -33,38 +33,38 @@ package sonia.scm.filter;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import static org.mockito.Mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.SCMContext;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.user.User;
import sonia.scm.user.UserTestData;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.verify;
/**
* Unit tests for {@link SecurityFilter}.
* Unit tests for {@link PropagatePrincipleFilter}.
*
* @author Sebastian Sdorra
*/
@RunWith(MockitoJUnitRunner.class)
@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini")
public class SecurityFilterTest {
public class PropagatePrincipleFilterTest {
@Mock
private HttpServletRequest request;
@@ -83,7 +83,7 @@ public class SecurityFilterTest {
private ScmConfiguration configuration;
private SecurityFilter securityFilter;
private PropagatePrincipleFilter propagatePrincipleFilter;
@Rule
public ShiroRule shiro = new ShiroRule();
@@ -94,38 +94,7 @@ public class SecurityFilterTest {
@Before
public void setUp(){
this.configuration = new ScmConfiguration();
this.securityFilter = new SecurityFilter(configuration);
when(request.getContextPath()).thenReturn("/scm");
}
/**
* Tests filter on authentication endpoint v1.
*
* @throws IOException
* @throws ServletException
*/
@Test
public void testDoOnAuthenticationUrlV1() throws IOException, ServletException {
checkIfAuthenticationUrlIsPassedThrough("/scm/api/auth/access_token");
}
/**
* Tests filter on authentication endpoint v2.
*
* @throws IOException
* @throws ServletException
*/
@Test
public void testDoOnAuthenticationUrlV2() throws IOException, ServletException {
checkIfAuthenticationUrlIsPassedThrough("/scm/api/v2/auth/access_token");
}
private void checkIfAuthenticationUrlIsPassedThrough(String uri) throws IOException, ServletException {
when(request.getRequestURI()).thenReturn(uri);
securityFilter.doFilter(request, response, chain);
verify(request, never()).setAttribute(Mockito.anyString(), Mockito.any());
verify(chain).doFilter(request, response);
this.propagatePrincipleFilter = new PropagatePrincipleFilter(configuration);
}
/**
@@ -136,8 +105,7 @@ public class SecurityFilterTest {
*/
@Test
public void testAnonymous() throws IOException, ServletException {
when(request.getRequestURI()).thenReturn("/scm/api");
securityFilter.doFilter(request, response, chain);
propagatePrincipleFilter.doFilter(request, response, chain);
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
@@ -149,14 +117,13 @@ public class SecurityFilterTest {
*/
@Test
public void testAnonymousWithAccessEnabled() throws IOException, ServletException {
when(request.getRequestURI()).thenReturn("/scm/api");
configuration.setAnonymousAccessEnabled(true);
// execute
securityFilter.doFilter(request, response, chain);
propagatePrincipleFilter.doFilter(request, response, chain);
// verify and capture
verify(request).setAttribute(SecurityFilter.ATTRIBUTE_REMOTE_USER, SCMContext.USER_ANONYMOUS);
verify(request).setAttribute(PropagatePrincipleFilter.ATTRIBUTE_REMOTE_USER, SCMContext.USER_ANONYMOUS);
verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture());
// assert
@@ -173,13 +140,12 @@ public class SecurityFilterTest {
@Test
public void testAuthenticated() throws IOException, ServletException {
authenticateUser(UserTestData.createTrillian());
when(request.getRequestURI()).thenReturn("/scm/api");
// execute
securityFilter.doFilter(request, response, chain);
propagatePrincipleFilter.doFilter(request, response, chain);
// verify and capture
verify(request).setAttribute(SecurityFilter.ATTRIBUTE_REMOTE_USER, "trillian");
verify(request).setAttribute(PropagatePrincipleFilter.ATTRIBUTE_REMOTE_USER, "trillian");
verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture());
// assert
@@ -187,42 +153,6 @@ public class SecurityFilterTest {
assertEquals("trillian", captured.getRemoteUser());
}
/**
* Tests filter without permissions.
*
* @throws IOException
* @throws ServletException
*/
@Test
public void testForbidden() throws IOException, ServletException {
authenticateUser(UserTestData.createTrillian());
when(request.getRequestURI()).thenReturn("/scm/api");
// execute
securityFilter = new AccessForbiddenSecurityFilter(configuration);
securityFilter.doFilter(request, response, chain);
// assert
verify(response).sendError(HttpServletResponse.SC_FORBIDDEN);
}
/**
* Tests filter unauthenticated and without permissions.
*
* @throws IOException
* @throws ServletException
*/
@Test
public void testUnauthorized() throws IOException, ServletException {
when(request.getRequestURI()).thenReturn("/scm/api");
// execute
securityFilter = new AccessForbiddenSecurityFilter(configuration);
securityFilter.doFilter(request, response, chain);
// assert
verify(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
private void authenticateUser(User user) {
SimplePrincipalCollection spc = new SimplePrincipalCollection();
@@ -236,18 +166,4 @@ public class SecurityFilterTest {
shiro.setSubject(subject);
}
private static class AccessForbiddenSecurityFilter extends SecurityFilter {
private AccessForbiddenSecurityFilter(ScmConfiguration configuration) {
super(configuration);
}
@Override
protected boolean hasPermission(Subject subject) {
return false;
}
}
}

View File

@@ -42,6 +42,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.Stage;
import javax.servlet.ServletContext;
import java.io.File;
@@ -96,14 +97,12 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase
* Method description
*
*
* @throws MalformedURLException
*/
@Test
public void testGetResourceFromCache() throws MalformedURLException
{
public void testGetResourceFromCache() {
DefaultUberWebResourceLoader resourceLoader =
new DefaultUberWebResourceLoader(servletContext,
new ArrayList<PluginWrapper>());
new ArrayList<PluginWrapper>(), Stage.PRODUCTION);
resourceLoader.getCache().put("/myresource", GITHUB);
@@ -112,6 +111,15 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase
assertSame(GITHUB, resource);
}
@Test
public void testGetResourceCacheIsDisableInStageDevelopment() throws MalformedURLException {
DefaultUberWebResourceLoader resourceLoader = new DefaultUberWebResourceLoader(servletContext, new ArrayList<>(), Stage.DEVELOPMENT);
when(servletContext.getResource("/scm")).thenAnswer(invocation -> new URL("https://scm-manager.org"));
URL url = resourceLoader.getResource("/scm");
URL secondUrl = resourceLoader.getResource("/scm");
assertNotSame(url, secondUrl);
}
/**
* Method description
*

View File

@@ -184,7 +184,7 @@ private long calculateAverage(List<Long> times) {
private Repository createTestRepository(int number) {
Repository repository = new Repository(keyGenerator.createKey(), REPOSITORY_TYPE, "namespace", "repo-" + number);
repository.getPermissions().add(new Permission("trillian", PermissionType.READ));
repository.addPermission(new Permission("trillian", PermissionType.READ));
return repository;
}

View File

@@ -111,7 +111,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
private String mockedNamespace = "default_namespace";
@Test
public void testCreate() throws AlreadyExistsException {
public void testCreate() {
Repository heartOfGold = createTestRepository();
Repository dbRepo = manager.get(heartOfGold.getId());
@@ -123,18 +123,18 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
username = "unpriv"
)
@Test(expected = UnauthorizedException.class)
public void testCreateWithoutPrivileges() throws AlreadyExistsException {
public void testCreateWithoutPrivileges() {
createTestRepository();
}
@Test(expected = AlreadyExistsException.class)
public void testCreateExisting() throws AlreadyExistsException {
public void testCreateExisting() {
createTestRepository();
createTestRepository();
}
@Test
public void testDelete() throws Exception {
public void testDelete() {
delete(manager, createTestRepository());
}
@@ -142,12 +142,12 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
username = "unpriv"
)
@Test(expected = UnauthorizedException.class)
public void testDeleteWithoutPrivileges() throws Exception {
public void testDeleteWithoutPrivileges() {
delete(manager, createTestRepository());
}
@Test(expected = RepositoryIsNotArchivedException.class)
public void testDeleteNonArchived() throws Exception {
public void testDeleteNonArchived() {
configuration.setEnableRepositoryArchive(true);
delete(manager, createTestRepository());
}
@@ -158,7 +158,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
}
@Test
public void testDeleteWithEnabledArchive() throws Exception {
public void testDeleteWithEnabledArchive() {
Repository repository = createTestRepository();
repository.setArchived(true);
@@ -168,7 +168,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
}
@Test
public void testGet() throws AlreadyExistsException {
public void testGet() {
Repository heartOfGold = createTestRepository();
String id = heartOfGold.getId();
String description = heartOfGold.getDescription();
@@ -186,7 +186,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
@SubjectAware(
username = "crato"
)
public void testGetWithoutRequiredPrivileges() throws AlreadyExistsException {
public void testGetWithoutRequiredPrivileges() {
Repository heartOfGold = RepositoryTestData.createHeartOfGold();
manager.create(heartOfGold);
@@ -195,7 +195,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
}
@Test
public void testGetAll() throws AlreadyExistsException {
public void testGetAll() {
Repository heartOfGold = createTestRepository();
Repository happyVerticalPeopleTransporter = createSecondTestRepository();
boolean foundHeart = false;
@@ -233,7 +233,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
@Test
@SuppressWarnings("unchecked")
@SubjectAware(username = "dent")
public void testGetAllWithPermissionsForTwoOrThreeRepos() throws AlreadyExistsException {
public void testGetAllWithPermissionsForTwoOrThreeRepos() {
// mock key generator
KeyGenerator keyGenerator = mock(KeyGenerator.class);
Stack<String> keys = new Stack<>();
@@ -274,7 +274,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
}
@Test
public void testEvents() throws Exception {
public void testEvents() {
RepositoryManager repoManager = createRepositoryManager(false);
repoManager.init(contextProvider);
TestListener listener = new TestListener();
@@ -305,7 +305,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
}
@Test
public void testModify() throws AlreadyExistsException {
public void testModify() {
Repository heartOfGold = createTestRepository();
heartOfGold.setDescription("prototype ship");
@@ -319,7 +319,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
@Test
@SubjectAware(username = "crato")
public void testModifyWithoutRequiredPermissions() throws AlreadyExistsException, NotFoundException {
public void testModifyWithoutRequiredPermissions() {
Repository heartOfGold = RepositoryTestData.createHeartOfGold();
manager.create(heartOfGold);
heartOfGold.setDescription("prototype ship");
@@ -334,7 +334,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
}
@Test
public void testRefresh() throws AlreadyExistsException {
public void testRefresh() {
Repository heartOfGold = createTestRepository();
String description = heartOfGold.getDescription();
@@ -345,7 +345,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
@Test
@SubjectAware(username = "crato")
public void testRefreshWithoutRequiredPermissions() throws AlreadyExistsException, NotFoundException {
public void testRefreshWithoutRequiredPermissions() {
Repository heartOfGold = RepositoryTestData.createHeartOfGold();
manager.create(heartOfGold);
heartOfGold.setDescription("prototype ship");
@@ -354,13 +354,13 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
manager.refresh(heartOfGold);
}
@Test(expected = RepositoryNotFoundException.class)
@Test(expected = NotFoundException.class)
public void testRefreshNotFound(){
manager.refresh(createRepositoryWithId());
}
@Test
public void testRepositoryHook() throws AlreadyExistsException {
public void testRepositoryHook() {
CountingReceiveHook hook = new CountingReceiveHook();
RepositoryManager repoManager = createRepositoryManager(false);
@@ -380,23 +380,23 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
}
@Test
public void testNamespaceSet() throws Exception {
public void testNamespaceSet() {
RepositoryManager repoManager = createRepositoryManager(false);
Repository repository = spy(createTestRepository());
repository.setName("Testrepo");
((DefaultRepositoryManager) repoManager).create(repository);
repoManager.create(repository);
assertEquals("default_namespace", repository.getNamespace());
}
@Test
public void shouldSetNamespace() throws AlreadyExistsException {
public void shouldSetNamespace() {
Repository repository = new Repository(null, "hg", null, "scm");
manager.create(repository);
assertNotNull(repository.getId());
assertNotNull(repository.getNamespace());
}
private void createUriTestRepositories(RepositoryManager m) throws AlreadyExistsException {
private void createUriTestRepositories(RepositoryManager m) {
mockedNamespace = "namespace";
createRepository(m, new Repository("1", "hg", "namespace", "scm"));
createRepository(m, new Repository("2", "hg", "namespace", "scm-test"));
@@ -452,7 +452,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
keyGenerator, repositoryDAO, handlerSet, namespaceStrategy);
}
private void createRepository(RepositoryManager m, Repository repository) throws AlreadyExistsException {
private void createRepository(RepositoryManager m, Repository repository) {
m.create(repository);
}
@@ -475,7 +475,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
assertEquals(repo.getLastModified(), other.getLastModified());
}
private Repository createRepository(Repository repository) throws AlreadyExistsException {
private Repository createRepository(Repository repository) {
manager.create(repository);
assertNotNull(repository.getId());
assertNotNull(manager.get(repository.getId()));
@@ -490,12 +490,12 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
return repository;
}
private Repository createSecondTestRepository() throws AlreadyExistsException {
private Repository createSecondTestRepository() {
return createRepository(
RepositoryTestData.createHappyVerticalPeopleTransporter());
}
private Repository createTestRepository() throws AlreadyExistsException {
private Repository createTestRepository() {
return createRepository(RepositoryTestData.createHeartOfGold());
}

View File

@@ -0,0 +1,63 @@
package sonia.scm.security;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import org.apache.shiro.authc.AuthenticationException;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ResourceInfo;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini")
public class SecurityRequestFilterTest {
@Rule
public ShiroRule shiroRule = new ShiroRule();
@Mock
private ResourceInfo resourceInfo;
@Mock
private ContainerRequestContext context;
@InjectMocks
private SecurityRequestFilter securityRequestFilter;
@Test
public void shouldAllowUnauthenticatedAccessForAnnotatedMethod() throws NoSuchMethodException {
when(resourceInfo.getResourceMethod()).thenReturn(SecurityTestClass.class.getMethod("anonymousAccessAllowed"));
securityRequestFilter.filter(context);
}
@Test(expected = AuthenticationException.class)
public void shouldRejectUnauthenticatedAccessForAnnotatedMethod() throws NoSuchMethodException {
when(resourceInfo.getResourceMethod()).thenReturn(SecurityTestClass.class.getMethod("loginRequired"));
securityRequestFilter.filter(context);
}
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldAllowAuthenticatedAccessForMethodWithoutAnnotation() throws NoSuchMethodException {
when(resourceInfo.getResourceMethod()).thenReturn(SecurityTestClass.class.getMethod("loginRequired"));
securityRequestFilter.filter(context);
}
private static class SecurityTestClass {
@AllowAnonymousAccess
public void anonymousAccessAllowed() {
}
public void loginRequired() {
}
}
}

View File

@@ -1,60 +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.selenium;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.selenium.page.Pages;
import sonia.scm.selenium.page.MainPage;
import sonia.scm.selenium.page.LoginPage;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Authentication related selenium integration tests.
*
* @author Sebastian Sdorra
*/
public class AuthenticationITCase extends SeleniumITCaseBase {
/**
* Authenticates an user and call logout function.
*/
@Test
public void testAuthentication() {
MainPage main = Pages.get(driver, LoginPage.class).login("scmadmin", "scmadmin");
assertEquals("scmadmin", main.getUserInfo());
main.logout();
}
}

View File

@@ -1,88 +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.selenium;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.selenium.page.Pages;
import sonia.scm.selenium.page.MainPage;
import sonia.scm.selenium.page.LoginPage;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import sonia.scm.repository.Repository;
/**
* Repository related selenium integration tests.
*
* @author Sebastian Sdorra
*/
public class RepositoriesITCase extends SeleniumITCaseBase {
private MainPage main;
/**
* Authenticates admin user, before each test.
*/
@Before
public void login() {
main = Pages.get(driver, LoginPage.class)
.login("scmadmin", "scmadmin");
}
/**
* Creates, select and removes a repository.
*/
@Test
public void createRepository() {
Repository repository = new Repository();
repository.setName("scm");
repository.setType("git");
repository.setContact("scmadmin@scm-manager.org");
repository.setDescription("SCM-Manager");
main.repositories()
.add(repository)
.select(repository.getName())
.remove();
}
/**
* Logs the user out, after each test.
*/
@After
public void logout() {
main.logout();
}
}

View File

@@ -1,71 +0,0 @@
/**
* Copyright (c) 2014, 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.selenium;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
/**
* Base class for selenium integration tests.
*
* @author Sebastian Sdorra
*/
public class SeleniumITCaseBase {
/**
* Selenium test driver.
*/
protected static WebDriver driver;
/**
* Setup selenium test driver.
*/
@BeforeClass
public static void setUpDriver() {
// DesiredCapabilities capa = DesiredCapabilities.chrome();
// capa.setBrowserName("firefox");
// capa.setPlatform(Platform.ANY);
// RemoteWebDriver driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), capa);
driver = new FirefoxDriver();
driver.get("http://localhost:8082/scm/index.html");
}
/**
* Closes the selenium test driver.
*/
@AfterClass
public static void tearDownDriver() {
driver.close();
}
}

View File

@@ -1,168 +0,0 @@
/**
* Copyright (c) 2014, 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.selenium.page;
import com.google.common.base.Throwables;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
/**
* Abstract selenium base page.
*
* @author Sebastian Sdorra
*
* @param <P> concrete page implementation
*/
public abstract class BasePage<P extends BasePage> {
/**
* Selenium test driver.
*/
protected final WebDriver driver;
/**
* Constructs a new base page.
*
* @param driver selenium test driver
*/
protected BasePage(WebDriver driver) {
this.driver = driver;
}
/**
* Performs a {@link Thread#sleep(long)} for the given timeout.
*
* @param time timeout
* @param unit time unit of timeout
*/
protected void sleep(long time, TimeUnit unit) {
try {
unit.sleep(time);
} catch (InterruptedException ex) {
throw Throwables.propagate(ex);
}
}
/**
* Wait for the element until it is clickable.
*
* @param by element selector
*
* @return web element
*/
protected WebElement waitToBeClickable(By by){
return waitToBeClickable(driver.findElement(by));
}
/**
* Waits for the element until it is clickable.
*
* @param element web element
*
* @return web element
*/
protected WebElement waitToBeClickable(WebElement element) {
WebDriverWait wait = new WebDriverWait(driver, 5);
return wait.until(ExpectedConditions.elementToBeClickable(element));
}
/**
* Waits until the element is present.
*
* @param by element locator
*
* @return web element
*/
protected WebElement waitFor(By by){
WebDriverWait wait = new WebDriverWait(driver, 1);
return wait.until(ExpectedConditions.presenceOfElementLocated(by));
}
/**
* Waits until the elements are present.
*
* @param by element selector
*
* @return list of web elements
*/
protected List<WebElement> waitForAll(By by){
WebDriverWait wait = new WebDriverWait(driver, 1);
return wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(by));
}
/**
* Creates a screenshot of the current browser content and stores it at the given path.
*
* @param target target file path
*
* @return {@code this}
*/
public P screenshot(String target) {
return screenshot(new File(target));
}
/**
* Creates a screenshot of the current browser content and stores it at the file.
*
* @param target target file
*
* @return {@code this}
*/
public P screenshot(File target) {
try {
File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Files.copy(scrFile, target);
} catch (IOException ex) {
throw Throwables.propagate(ex);
}
return self();
}
/**
* Returns {@code this}.
*
* @return {@code this}
*/
protected abstract P self();
}

View File

@@ -1,89 +0,0 @@
/**
* Copyright (c) 2014, 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.selenium.page;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
* Page object for the scm-manager login page.
*
* @author Sebastian Sdorra
*/
public class LoginPage extends BasePage<LoginPage> {
@FindBy(css = "input[name=username]")
private WebElement usernameInput;
@FindBy(css = "input[name=password]")
private WebElement passwordInput;
@FindBy(css = "#loginButton button")
private WebElement loginButton;
/**
* Constructs a new page. This constructor should only be called from {@link Pages}.
*
* @param driver selenium test driver
*/
LoginPage(WebDriver driver) {
super(driver);
}
@Override
protected LoginPage self() {
return this;
}
/**
* Authenticates the user and returns the {@link MainPage}.
*
* @param username username
* @param password password
*
* @return {@link MainPage} after successful authentication
*/
public MainPage login(String username, String password) {
usernameInput.clear();
usernameInput.sendKeys(username);
passwordInput.clear();
passwordInput.sendKeys(password);
sleep(250, TimeUnit.MILLISECONDS);
waitToBeClickable(loginButton).click();
return Pages.get(driver, MainPage.class);
}
}

View File

@@ -1,95 +0,0 @@
/**
* Copyright (c) 2014, 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.selenium.page;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
* Page object for scm-manager's main page.
*
* @author Sebastian Sdorra
*/
public class MainPage extends BasePage<MainPage> {
@FindBy(css = "#navLogout a")
private WebElement logoutLink;
@FindBy(linkText = "Repositories")
private WebElement repositoriesLink;
@FindBy(css = "#scm-userinfo-tip")
private WebElement userInfoTip;
/**
* Constructs a new page. This constructor should only be called from {@link Pages}.
*
* @param driver selenium test driver
*/
MainPage(WebDriver driver) {
super(driver);
}
@Override
protected MainPage self() {
return this;
}
/**
* Returns the name of the current authenticated user from the user info tip.
*
* @return name of the current authenticated user
*/
public String getUserInfo(){
return userInfoTip.getText();
}
/**
* Navigates to the repositories page and returns the page object for this page.
*
* @return page object for repositories page
*/
public RepositoriesPage repositories(){
repositoriesLink.click();
return Pages.get(driver, RepositoriesPage.class);
}
/**
* Logs the current user out.
*
* @return page object for the login
*/
public LoginPage logout(){
waitToBeClickable(logoutLink).click();
return Pages.get(driver, LoginPage.class);
}
}

View File

@@ -1,113 +0,0 @@
/**
* Copyright (c) 2014, 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.selenium.page;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
/**
* Helper class for selenium page objects.
*
* @author Sebastian Sdorra
*/
public final class Pages {
private Pages() {
}
/**
* Creates an instance of the given page object.
*
* @param <T> page object type
* @param driver selenium driver
* @param clazz page object type
* @param otherArguments other constructor arguments
*
* @return instance of page object
*/
public static <T extends BasePage> T get(WebDriver driver, Class<T> clazz, Object... otherArguments)
{
T page = null;
try {
List<Class<?>> argumentTypes = Lists.newArrayList();
argumentTypes.add(WebDriver.class);
for (Object argument : otherArguments) {
argumentTypes.add(argument.getClass());
}
List<Object> arguments = Lists.newArrayList();
arguments.add(driver);
arguments.addAll(Arrays.asList(otherArguments));
Constructor<T> constructor = clazz.getDeclaredConstructor(
argumentTypes.toArray(new Class<?>[argumentTypes.size()])
);
page = constructor.newInstance(arguments.toArray(new Object[arguments.size()]));
PageFactory.initElements(new DefaultElementLocatorFactory(new WaitingSearchContext(driver)), page);
} catch (Exception ex) {
throw Throwables.propagate(ex);
}
return page;
}
private static class WaitingSearchContext implements SearchContext {
private final WebDriver driver;
private final WebDriverWait wait;
private WaitingSearchContext(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, 1);
}
@Override
public List<WebElement> findElements(By by) {
return wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(by));
}
@Override
public WebElement findElement(By by) {
return wait.until(ExpectedConditions.presenceOfElementLocated(by));
}
}
}

View File

@@ -1,137 +0,0 @@
/**
* Copyright (c) 2014, 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.selenium.page;
import com.google.common.base.MoreObjects;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NotFoundException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.WebDriverWait;
import sonia.scm.repository.Repository;
import java.util.List;
/**
* Page object for scm-manager's repository creation page.
*
* @author Sebastian Sdorra
*/
public class RepositoriesAddPage extends BasePage<RepositoriesAddPage> {
@FindBy(css = "input[name=name]")
private WebElement nameInput;
@FindBy(css = "input[name=contact]")
private WebElement contactInput;
@FindBy(css = "#x-form-el-repositoryType img")
private WebElement typeInput;
@FindBy(css = "textarea[name=description]")
private WebElement descriptionInput;
@FindBy(css = "div.x-panel-btns button:nth-of-type(1)")
private WebElement okButton;
private final RepositoriesPage repositoriesPage;
/**
* Constructs a new page. This constructor should only be called from {@link Pages}.
*
* @param driver selenium test driver
* @param repositoriesPage repositories page object
*/
RepositoriesAddPage(WebDriver driver, RepositoriesPage repositoriesPage) {
super(driver);
this.repositoriesPage = repositoriesPage;
}
@Override
protected RepositoriesAddPage self() {
return this;
}
/**
* Creates a new {@link Repository}.
*
* @param repository repository for creation
*
* @return repositories overview page
*/
public RepositoriesPage add(Repository repository) {
nameInput.sendKeys(repository.getName());
selectType(repository.getType());
contactInput.sendKeys(repository.getContact());
descriptionInput.sendKeys(repository.getDescription());
waitToBeClickable(okButton).click();
return repositoriesPage;
}
private void selectType(String type) {
typeInput.click();
String displayName = findDisplayName(type);
WebDriverWait wait = new WebDriverWait(driver, 1);
List<WebElement> elements = waitForAll(By.className("x-combo-list-item"));
WebElement typeElement = null;
for (WebElement te : elements){
if (te.getText().trim().equalsIgnoreCase(displayName)){
typeElement = te;
break;
}
}
if (typeElement == null){
throw new NotFoundException("could not find type element with type " + displayName);
}
typeElement.click();
}
private String findDisplayName(String type) {
String displayName = null;
if (driver instanceof JavascriptExecutor) {
// TODO seams not to work
String script = "Sonia.repository.getTypeByName('" + type + "').displayName;";
displayName = (String) ((JavascriptExecutor)driver).executeScript(script);
}
return MoreObjects.firstNonNull(displayName, type);
}
}

View File

@@ -1,103 +0,0 @@
/**
* Copyright (c) 2014, 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.selenium.page;
import java.util.List;
import java.util.Locale;
import org.openqa.selenium.By;
import org.openqa.selenium.NotFoundException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import sonia.scm.repository.Repository;
/**
* Page object for scm-manager's repositories overview page.
*
* @author Sebastian Sdorra
*/
public class RepositoriesPage extends BasePage<RepositoriesPage> {
@FindBy(id = "repositoryAddButton")
private WebElement addButton;
/**
* Constructs a new page. This constructor should only be called from {@link Pages}.
*
* @param driver selenium test driver
*/
RepositoriesPage(WebDriver driver) {
super(driver);
}
@Override
protected RepositoriesPage self() {
return this;
}
/**
* Creates a new {@link Repository}.
*
* @param repository repository for creation
*
* @return {@link this}
*/
public RepositoriesPage add(Repository repository){
addButton.click();
RepositoriesAddPage addPage = Pages.get(driver, RepositoriesAddPage.class, this);
return addPage.add(repository);
}
/**
* Selects the repository with the given name and returns the detail page object for the selected repository.
*
* @param repositoryName name of the repository
*
* @return page object for selected repository
*/
public RepositoryPage select(String repositoryName){
WebElement repositoryNameColumn = null;
List<WebElement> elements = waitForAll(By.className("x-grid3-col-name"));
for (WebElement element : elements){
if (element.getText().trim().toLowerCase(Locale.ENGLISH).equals(repositoryName)){
repositoryNameColumn = element;
break;
}
}
if ( repositoryNameColumn == null ) {
throw new NotFoundException("could not find repository " + repositoryName);
}
return Pages.get(driver, RepositoryPage.class, this);
}
}

View File

@@ -1,77 +0,0 @@
/**
* Copyright (c) 2014, 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.selenium.page;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
* Page object for scm-manager's repository detail page.
*
* @author Sebastian Sdorra
*/
public class RepositoryPage extends BasePage<RepositoryPage> {
@FindBy(css = "#repoRmButton button")
private WebElement removeButton;
private final RepositoriesPage repositoriesPage;
/**
* Constructs a new page. This constructor should only be called from {@link Pages}.
*
* @param driver selenium test driver
* @param repositoriesPage repositories page object
*/
RepositoryPage(WebDriver driver, RepositoriesPage repositoriesPage) {
super(driver);
this.repositoriesPage = repositoriesPage;
}
@Override
protected RepositoryPage self() {
return this;
}
/**
* Removes the selected repository.
*
* @return repositories overview page object
*/
public RepositoriesPage remove(){
removeButton.click();
waitToBeClickable(By.cssSelector("div.x-window button:nth-of-type(1)")).click();
return repositoriesPage;
}
}

View File

@@ -40,7 +40,6 @@ import com.github.sdorra.shiro.SubjectAware;
import com.google.common.collect.Lists;
import org.assertj.core.api.Assertions;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -48,7 +47,6 @@ import org.mockito.ArgumentCaptor;
import sonia.scm.NotFoundException;
import sonia.scm.store.JAXBConfigurationStoreFactory;
import sonia.scm.user.xml.XmlUserDAO;
import sonia.scm.util.MockUtil;
import static org.mockito.Mockito.*;
@@ -57,7 +55,6 @@ import static org.mockito.Mockito.*;
import java.util.Collections;
import java.util.List;
import org.junit.Rule;
import sonia.scm.store.ConfigurationStoreFactory;
/**
*

View File

@@ -4,11 +4,11 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import sonia.scm.NotFoundException;
import sonia.scm.PushStateDispatcher;
import sonia.scm.repository.DefaultRepositoryProvider;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryNotFoundException;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.repository.spi.HttpScmProtocol;
@@ -58,12 +58,12 @@ public class HttpProtocolServletTest {
private HttpScmProtocol protocol;
@Before
public void init() throws RepositoryNotFoundException {
public void init() {
initMocks(this);
when(userAgentParser.parse(request)).thenReturn(userAgent);
when(userAgent.isBrowser()).thenReturn(false);
NamespaceAndName existingRepo = new NamespaceAndName("space", "repo");
when(serviceFactory.create(not(eq(existingRepo)))).thenThrow(RepositoryNotFoundException.class);
when(serviceFactory.create(not(eq(existingRepo)))).thenThrow(new NotFoundException("Test", "a"));
when(serviceFactory.create(existingRepo)).thenReturn(repositoryService);
when(requestProvider.get()).thenReturn(httpServletRequest);
}
@@ -97,7 +97,7 @@ public class HttpProtocolServletTest {
}
@Test
public void shouldDelegateToProvider() throws RepositoryNotFoundException, IOException, ServletException {
public void shouldDelegateToProvider() throws IOException, ServletException {
when(request.getPathInfo()).thenReturn("/space/name");
NamespaceAndName namespaceAndName = new NamespaceAndName("space", "name");
doReturn(repositoryService).when(serviceFactory).create(namespaceAndName);