diff --git a/Jenkinsfile b/Jenkinsfile index 6a9fa7e044..c759de5733 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,7 +37,7 @@ node('docker') { } stage('Integration Test') { - mvn 'verify -Pit -pl :scm-webapp,:scm-it -Dmaven.test.failure.ignore=true' + mvn 'verify -Pit -pl :scm-webapp,:scm-it -Dmaven.test.failure.ignore=true -DClassLoaderLeakPreventor.threadWaitMs=10' } stage('SonarQube') { diff --git a/pom.xml b/pom.xml index 7a8e16b869..98ddfde58a 100644 --- a/pom.xml +++ b/pom.xml @@ -220,7 +220,13 @@ org.jboss.resteasy - resteasy-jaxrs + resteasy-core + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-core-spi ${resteasy.version} @@ -831,7 +837,7 @@ 3.0.1 2.1.1 - 3.6.2.Final + 4.4.1.Final 1.19.4 2.11.1 2.10.0 @@ -839,7 +845,7 @@ 2.3.0 - 1.6.0 + 1.6.1 9.4.22.v20191022 diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 8437b256b2..4e8e315253 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -98,7 +98,7 @@ org.jboss.resteasy - resteasy-jaxrs + resteasy-core test diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index 1611633449..e6b2929bd2 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -72,7 +72,13 @@ org.jboss.resteasy - resteasy-jaxrs + resteasy-core + test + + + + org.jboss.resteasy + resteasy-core-spi test diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java index 2048d13dea..aa362b8ec6 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java @@ -70,6 +70,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import static java.util.Optional.empty; import static sonia.scm.ContextEntry.ContextBuilder.entity; import static sonia.scm.NotFoundException.notFound; @@ -209,7 +210,7 @@ public class GitBrowseCommand extends AbstractGitCommand logger.trace("fetch last commit for {} at {}", path, revId.getName()); RevCommit commit = getLatestCommit(repo, revId, path); - Optional lfsPointer = GitUtil.getLfsPointer(repo, path, commit, treeWalk); + Optional lfsPointer = commit == null? empty(): GitUtil.getLfsPointer(repo, path, commit, treeWalk); if (lfsPointer.isPresent()) { BlobStore lfsBlobStore = lfsBlobStoreFactory.getLfsBlobStore(repository); diff --git a/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.tsx b/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.tsx index 3ef02306fe..a23fc5040a 100644 --- a/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.tsx +++ b/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.tsx @@ -1,7 +1,7 @@ import React, { FormEvent } from "react"; import { WithTranslation, withTranslation } from "react-i18next"; import { Branch, Repository, Link } from "@scm-manager/ui-types"; -import { apiClient, BranchSelector, ErrorPage, Loading, Subtitle, SubmitButton } from "@scm-manager/ui-components"; +import { apiClient, BranchSelector, ErrorPage, Loading, Subtitle, Level, SubmitButton } from "@scm-manager/ui-components"; type Props = WithTranslation & { repository: Repository; @@ -143,10 +143,14 @@ class RepositoryConfig extends React.Component { } const submitButton = disabled ? null : ( - + } /> ); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java index c25b5c5b60..a561914b42 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java @@ -2,14 +2,11 @@ package sonia.scm.api.v2.resources; import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; -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.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.ArgumentCaptor; @@ -27,6 +24,7 @@ import sonia.scm.repository.RepositoryManager; import sonia.scm.store.ConfigurationStore; import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.web.GitVndMediaType; +import sonia.scm.web.RestDispatcher; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; @@ -52,10 +50,7 @@ public class GitConfigResourceTest { @Rule public ShiroRule shiro = new ShiroRule(); - @Rule - public ExpectedException thrown = ExpectedException.none(); - - private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); + private RestDispatcher dispatcher = new RestDispatcher(); private final URI baseUri = URI.create("/"); @@ -89,7 +84,7 @@ public class GitConfigResourceTest { when(repositoryHandler.getConfig()).thenReturn(gitConfig); GitRepositoryConfigResource gitRepositoryConfigResource = new GitRepositoryConfigResource(repositoryConfigMapper, repositoryManager, new GitRepositoryConfigStoreProvider(configurationStoreFactory)); GitConfigResource gitConfigResource = new GitConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler, of(gitRepositoryConfigResource)); - dispatcher.getRegistry().addSingletonResource(gitConfigResource); + dispatcher.addSingletonResource(gitConfigResource); when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri); } @@ -137,10 +132,11 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "writeOnly") - public void shouldNotGetConfigWhenNotAuthorized() throws URISyntaxException { - thrown.expectMessage("Subject does not have permission [configuration:read:git]"); + public void shouldNotGetConfigWhenNotAuthorized() throws URISyntaxException, UnsupportedEncodingException { + MockHttpResponse response = get(); - get(); + assertEquals("Subject does not have permission [configuration:read:git]", response.getContentAsString()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } @Test @@ -152,10 +148,11 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldNotUpdateConfigWhenNotAuthorized() throws URISyntaxException { - thrown.expectMessage("Subject does not have permission [configuration:write:git]"); + public void shouldNotUpdateConfigWhenNotAuthorized() throws URISyntaxException, UnsupportedEncodingException { + MockHttpResponse response = put(); - put(); + assertEquals("Subject does not have permission [configuration:write:git]", response.getContentAsString()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } @Test diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java index 1f88bfe665..5a3b809973 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java @@ -2,14 +2,11 @@ package sonia.scm.api.v2.resources; import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; -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.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; @@ -18,12 +15,15 @@ import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; +import sonia.scm.web.RestDispatcher; import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; import java.net.URISyntaxException; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -37,10 +37,7 @@ public class HgConfigAutoConfigurationResourceTest { @Rule public ShiroRule shiro = new ShiroRule(); - @Rule - public ExpectedException thrown = ExpectedException.none(); - - private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); + private RestDispatcher dispatcher = new RestDispatcher(); @InjectMocks private HgConfigDtoToHgConfigMapperImpl dtoToConfigMapper; @@ -57,7 +54,7 @@ public class HgConfigAutoConfigurationResourceTest { new HgConfigAutoConfigurationResource(dtoToConfigMapper, repositoryHandler); when(resourceProvider.get()).thenReturn(resource); - dispatcher.getRegistry().addSingletonResource( + dispatcher.addSingletonResource( new HgConfigResource(null, null, null, null, resourceProvider, null)); } @@ -76,9 +73,10 @@ public class HgConfigAutoConfigurationResourceTest { @Test @SubjectAware(username = "readOnly") public void shouldNotSetDefaultConfigAndInstallHgWhenNotAuthorized() throws Exception { - thrown.expectMessage("Subject does not have permission [configuration:write:hg]"); + MockHttpResponse response = put(null); - put(null); + assertEquals("Subject does not have permission [configuration:write:hg]", response.getContentAsString()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } @Test @@ -95,9 +93,10 @@ public class HgConfigAutoConfigurationResourceTest { @Test @SubjectAware(username = "readOnly") public void shouldNotUpdateConfigAndInstallHgWhenNotAuthorized() throws Exception { - thrown.expectMessage("Subject does not have permission [configuration:write:hg]"); + MockHttpResponse response = put("{\"disabled\":true}"); - put("{\"disabled\":true}"); + assertEquals("Subject does not have permission [configuration:write:hg]", response.getContentAsString()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } private MockHttpResponse put(String content) throws URISyntaxException { diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java index bcd9543d28..e77ed917ed 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java @@ -2,19 +2,17 @@ package sonia.scm.api.v2.resources; import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; -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.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import sonia.scm.web.RestDispatcher; import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; @@ -35,10 +33,7 @@ public class HgConfigInstallationsResourceTest { @Rule public ShiroRule shiro = new ShiroRule(); - @Rule - public ExpectedException thrown = ExpectedException.none(); - - private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); + private RestDispatcher dispatcher = new RestDispatcher(); private final URI baseUri = URI.create("/"); @@ -57,7 +52,7 @@ public class HgConfigInstallationsResourceTest { HgConfigInstallationsResource resource = new HgConfigInstallationsResource(mapper); when(resourceProvider.get()).thenReturn(resource); - dispatcher.getRegistry().addSingletonResource( + dispatcher.addSingletonResource( new HgConfigResource(null, null, null, null, null, resourceProvider)); @@ -82,9 +77,10 @@ public class HgConfigInstallationsResourceTest { @Test @SubjectAware(username = "writeOnly") public void shouldNotGetHgInstallationsWhenNotAuthorized() throws Exception { - thrown.expectMessage("Subject does not have permission [configuration:read:hg]"); + MockHttpResponse response = get("hg"); - get("hg"); + assertEquals("Subject does not have permission [configuration:read:hg]", response.getContentAsString()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } @Test @@ -104,9 +100,10 @@ public class HgConfigInstallationsResourceTest { @Test @SubjectAware(username = "writeOnly") public void shouldNotGetPythonInstallationsWhenNotAuthorized() throws Exception { - thrown.expectMessage("Subject does not have permission [configuration:read:hg]"); + MockHttpResponse response = get("python"); - get("python"); + assertEquals("Subject does not have permission [configuration:read:hg]", response.getContentAsString()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } private MockHttpResponse get(String path) throws URISyntaxException { diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java index 473ddfe4b4..9cd0e4b17e 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java @@ -5,14 +5,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; -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.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; @@ -23,6 +20,7 @@ import sonia.scm.installer.HgPackageReader; import sonia.scm.net.ahc.AdvancedHttpClient; import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; +import sonia.scm.web.RestDispatcher; import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; @@ -49,10 +47,7 @@ public class HgConfigPackageResourceTest { @Rule public ShiroRule shiro = new ShiroRule(); - @Rule - public ExpectedException thrown = ExpectedException.none(); - - private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); + private RestDispatcher dispatcher = new RestDispatcher(); private final URI baseUri = java.net.URI.create("/"); @@ -113,9 +108,10 @@ public class HgConfigPackageResourceTest { @Test @SubjectAware(username = "writeOnly") public void shouldNotGetPackagesWhenNotAuthorized() throws Exception { - thrown.expectMessage("Subject does not have permission [configuration:read:hg]"); + MockHttpResponse response = get(); - get(); + assertEquals("Subject does not have permission [configuration:read:hg]", response.getContentAsString()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } @Test @@ -158,9 +154,10 @@ public class HgConfigPackageResourceTest { @Test @SubjectAware(username = "readOnly") public void shouldNotInstallPackageWhenNotAuthorized() throws Exception { - thrown.expectMessage("Subject does not have permission [configuration:write:hg]"); + MockHttpResponse response = put("don-t-care"); - put("don-t-care"); + assertEquals("Subject does not have permission [configuration:write:hg]", response.getContentAsString()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } private List createPackages() { @@ -191,7 +188,7 @@ public class HgConfigPackageResourceTest { new HgConfigPackageResource(hgPackageReader, advancedHttpClient, repositoryHandler, mapper); when(hgConfigPackageResourceProvider.get()).thenReturn(hgConfigPackageResource); - dispatcher.getRegistry().addSingletonResource( + dispatcher.addSingletonResource( new HgConfigResource(null, null, null, hgConfigPackageResourceProvider, null, null)); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java index e0253ad86a..eae03f729d 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java @@ -4,14 +4,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; -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.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; @@ -20,6 +17,7 @@ import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; +import sonia.scm.web.RestDispatcher; import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; @@ -43,10 +41,7 @@ public class HgConfigResourceTest { @Rule public ShiroRule shiro = new ShiroRule(); - @Rule - public ExpectedException thrown = ExpectedException.none(); - - private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); + private RestDispatcher dispatcher = new RestDispatcher(); private final URI baseUri = URI.create("/"); @@ -78,7 +73,7 @@ public class HgConfigResourceTest { HgConfigResource gitConfigResource = new HgConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler, packagesResource, autoconfigResource, installationsResource); - dispatcher.getRegistry().addSingletonResource(gitConfigResource); + dispatcher.addSingletonResource(gitConfigResource); when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri); } @@ -120,10 +115,11 @@ public class HgConfigResourceTest { @Test @SubjectAware(username = "writeOnly") - public void shouldNotGetConfigWhenNotAuthorized() throws URISyntaxException { - thrown.expectMessage("Subject does not have permission [configuration:read:hg]"); + public void shouldNotGetConfigWhenNotAuthorized() throws URISyntaxException, UnsupportedEncodingException { + MockHttpResponse response = get(); - get(); + assertEquals("Subject does not have permission [configuration:read:hg]", response.getContentAsString()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } @Test @@ -135,10 +131,11 @@ public class HgConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldNotUpdateConfigWhenNotAuthorized() throws URISyntaxException { - thrown.expectMessage("Subject does not have permission [configuration:write:hg]"); + public void shouldNotUpdateConfigWhenNotAuthorized() throws URISyntaxException, UnsupportedEncodingException { + MockHttpResponse response = put(); - put(); + assertEquals("Subject does not have permission [configuration:write:hg]", response.getContentAsString()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } private MockHttpResponse get() throws URISyntaxException { diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java index f7ccf039b2..ee2918160c 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java @@ -4,14 +4,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; -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.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; @@ -19,6 +16,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.SvnConfig; import sonia.scm.repository.SvnRepositoryHandler; +import sonia.scm.web.RestDispatcher; import sonia.scm.web.SvnVndMediaType; import javax.servlet.http.HttpServletResponse; @@ -42,10 +40,7 @@ public class SvnConfigResourceTest { @Rule public ShiroRule shiro = new ShiroRule(); - @Rule - public ExpectedException thrown = ExpectedException.none(); - - private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); + private RestDispatcher dispatcher = new RestDispatcher(); private final URI baseUri = URI.create("/"); @@ -66,7 +61,7 @@ public class SvnConfigResourceTest { SvnConfig gitConfig = createConfiguration(); when(repositoryHandler.getConfig()).thenReturn(gitConfig); SvnConfigResource gitConfigResource = new SvnConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler); - dispatcher.getRegistry().addSingletonResource(gitConfigResource); + dispatcher.addSingletonResource(gitConfigResource); when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri); } @@ -108,10 +103,11 @@ public class SvnConfigResourceTest { @Test @SubjectAware(username = "writeOnly") - public void shouldNotGetConfigWhenNotAuthorized() throws URISyntaxException { - thrown.expectMessage("Subject does not have permission [configuration:read:svn]"); + public void shouldNotGetConfigWhenNotAuthorized() throws URISyntaxException, UnsupportedEncodingException { + MockHttpResponse response = get(); - get(); + assertEquals("Subject does not have permission [configuration:read:svn]", response.getContentAsString()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } @Test @@ -123,10 +119,11 @@ public class SvnConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldNotUpdateConfigWhenNotAuthorized() throws URISyntaxException { - thrown.expectMessage("Subject does not have permission [configuration:write:svn]"); + public void shouldNotUpdateConfigWhenNotAuthorized() throws URISyntaxException, UnsupportedEncodingException { + MockHttpResponse response = put(); - put(); + assertEquals("Subject does not have permission [configuration:write:svn]", response.getContentAsString()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } private MockHttpResponse get() throws URISyntaxException { diff --git a/scm-server/pom.xml b/scm-server/pom.xml index 74f6466821..4abaa49dc1 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -78,6 +78,7 @@ java.awt.headless=true logback.configurationFile=logging.xml + ClassLoaderLeakPreventor.threadWaitMs=100 diff --git a/scm-test/pom.xml b/scm-test/pom.xml index bddbcf2c85..d36b8c55f8 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -42,6 +42,20 @@ ${mockito.version} + + org.jboss.resteasy + resteasy-core-spi + + + org.jboss.resteasy + resteasy-core + + + org.jboss.resteasy + resteasy-jackson2-provider + ${resteasy.version} + + org.slf4j slf4j-simple diff --git a/scm-test/src/main/java/sonia/scm/web/RestDispatcher.java b/scm-test/src/main/java/sonia/scm/web/RestDispatcher.java new file mode 100644 index 0000000000..29d57f204f --- /dev/null +++ b/scm-test/src/main/java/sonia/scm/web/RestDispatcher.java @@ -0,0 +1,111 @@ +package sonia.scm.web; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.shiro.authz.AuthorizationException; +import org.apache.shiro.authz.UnauthorizedException; +import org.jboss.resteasy.mock.MockDispatcherFactory; +import org.jboss.resteasy.spi.Dispatcher; +import org.jboss.resteasy.spi.HttpRequest; +import org.jboss.resteasy.spi.HttpResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.AlreadyExistsException; +import sonia.scm.BadRequestException; +import sonia.scm.ConcurrentModificationException; +import sonia.scm.NotFoundException; +import sonia.scm.ScmConstraintViolationException; + +import javax.ws.rs.Produces; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ContextResolver; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import java.util.HashMap; +import java.util.Map; + +public class RestDispatcher { + + private static final Logger LOG = LoggerFactory.getLogger(RestDispatcher.class); + + private final Dispatcher dispatcher; + private final EnhanceableExceptionMapper exceptionMapper; + + public RestDispatcher() { + dispatcher = MockDispatcherFactory.createDispatcher(); + exceptionMapper = new EnhanceableExceptionMapper(); + dispatcher.getProviderFactory().register(exceptionMapper); + dispatcher.getProviderFactory().registerProviderInstance(new JacksonProducer()); + } + + public void addSingletonResource(Object resource) { + dispatcher.getRegistry().addSingletonResource(resource); + } + + public void invoke(HttpRequest in, HttpResponse response) { + dispatcher.invoke(in, response); + } + + public void registerException(Class exceptionClass, Status status) { + exceptionMapper.registerException(exceptionClass, status); + } + + public void putDefaultContextObject(Class clazz, T object) { + dispatcher.getDefaultContextObjects().put(clazz, object); + } + + private static class EnhanceableExceptionMapper implements ExceptionMapper { + + private final Map, Integer> statusCodes = new HashMap<>(); + + public EnhanceableExceptionMapper() { + registerException(NotFoundException.class, Status.NOT_FOUND); + registerException(AlreadyExistsException.class, Status.CONFLICT); + registerException(ConcurrentModificationException.class, Status.CONFLICT); + registerException(UnauthorizedException.class, Status.FORBIDDEN); + registerException(AuthorizationException.class, Status.FORBIDDEN); + registerException(BadRequestException.class, Status.BAD_REQUEST); + registerException(ScmConstraintViolationException.class, Status.BAD_REQUEST); + } + + private void registerException(Class exceptionClass, Status status) { + statusCodes.put(exceptionClass, status.getStatusCode()); + } + + @Override + public Response toResponse(Exception e) { + return Response.status(getStatus(e)).entity(e.getMessage()).build(); + } + + private Integer getStatus(Exception ex) { + return statusCodes + .entrySet() + .stream() + .filter(e -> e.getKey().isAssignableFrom(ex.getClass())) + .map(Map.Entry::getValue) + .findAny() + .orElse(handleUnknownException(ex)); + } + + private Integer handleUnknownException(Exception ex) { + LOG.info("got unknown exception in rest api test", ex); + return 500; + } + } + + @Provider + @Produces("application/*+json") + public static class JacksonProducer implements ContextResolver { + public JacksonProducer() { + this.json + = new ObjectMapper().findAndRegisterModules(); + } + + @Override + public ObjectMapper getContext(Class objectType) { + return json; + } + + private final ObjectMapper json; + } +} diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index ac759241d2..ac6a6638fc 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -456,6 +456,83 @@ exports[`Storyshots Forms|Radio Disabled 1`] = ` `; +exports[`Storyshots Forms|Textarea OnCancel 1`] = ` +
+
+ +
+